summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2015-10-02 14:53:20 +0200
committerDaniel Wilhelm <daniel@wili.li>2015-10-02 14:53:20 +0200
commit94db751716dd2851f99b5c4c2981da1d1f4780f8 (patch)
treee4ffc9f5ae2b2873f267a6e5d3d2092c8aad49a7 /zen
parent6.10 (diff)
downloadFreeFileSync-94db751716dd2851f99b5c4c2981da1d1f4780f8.tar.gz
FreeFileSync-94db751716dd2851f99b5c4c2981da1d1f4780f8.tar.bz2
FreeFileSync-94db751716dd2851f99b5c4c2981da1d1f4780f8.zip
6.11
Diffstat (limited to 'zen')
-rw-r--r--zen/assert_static.h43
-rw-r--r--zen/basic_math.h2
-rw-r--r--zen/dir_watcher.cpp68
-rw-r--r--zen/dst_hack.cpp368
-rw-r--r--zen/dst_hack.h38
-rw-r--r--zen/file_access.cpp (renamed from zen/file_handling.cpp)419
-rw-r--r--zen/file_access.h (renamed from zen/file_handling.h)10
-rw-r--r--zen/file_id_def.h7
-rw-r--r--zen/file_io.cpp13
-rw-r--r--zen/file_io.h17
-rw-r--r--zen/file_io_base.h4
-rw-r--r--zen/file_traverser.cpp117
-rw-r--r--zen/file_traverser.h15
-rw-r--r--zen/fixed_list.h34
-rw-r--r--zen/format_unit.h2
-rw-r--r--zen/guid.h6
-rw-r--r--zen/i18n.h15
-rw-r--r--zen/int64.h38
-rw-r--r--zen/notify_removal.cpp7
-rw-r--r--zen/notify_removal.h11
-rw-r--r--zen/osx_string.h82
-rw-r--r--zen/osx_throw_exception.h56
-rw-r--r--zen/privilege.cpp144
-rw-r--r--zen/privilege.h17
-rw-r--r--zen/recycler.cpp26
-rw-r--r--zen/recycler.h1
-rw-r--r--zen/scope_guard.h6
-rw-r--r--zen/serialize.h49
-rw-r--r--zen/shell_execute.h2
-rw-r--r--zen/stl_tools.h70
-rw-r--r--zen/string_base.h80
-rw-r--r--zen/string_tools.h30
-rw-r--r--zen/string_traits.h12
-rw-r--r--zen/sys_error.h31
-rw-r--r--zen/thread.h14
-rw-r--r--zen/tick_count.h18
-rw-r--r--zen/time.h11
-rw-r--r--zen/utf.h18
-rw-r--r--zen/win_ver.h43
-rw-r--r--zen/xml_io.cpp4
-rw-r--r--zen/zstring.cpp101
-rw-r--r--zen/zstring.h57
42 files changed, 613 insertions, 1493 deletions
diff --git a/zen/assert_static.h b/zen/assert_static.h
deleted file mode 100644
index 17d5c370..00000000
--- a/zen/assert_static.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef ASSERTSTATIC_H_INCLUDED
-#define ASSERTSTATIC_H_INCLUDED
-
-/*
-//compile time assert based on Loki (http://loki-lib.sourceforge.net)
-
-#ifdef NDEBUG
-
-#define assert_static(x) //((void)0) -> leads to error when seen in namespace scope!
-
-#else // debugging enabled
-namespace static_check_impl
-{
-template<int>
-struct CompileTimeError;
-
-template<>
-struct CompileTimeError<true> {};
-}
-
-#define LOKI_CONCAT(X, Y) LOKI_CONCAT_SUB(X, Y)
-#define LOKI_CONCAT_SUB(X, Y) X ## Y
-
-#define assert_static(expr) \
- enum { LOKI_CONCAT(loki_enum_dummy_value, __LINE__) = sizeof(StaticCheckImpl::CompileTimeError<static_cast<bool>(expr) >) }
-
-// #define assert_static(expr) \
-// { Loki::CompileTimeError<((expr) != 0)> Static_Assert_Has_Failed; (void)Static_Assert_Has_Failed; }
-
-#endif
-*/
-
-//C++11: please get rid of this pointless string literal requirement!
-#define assert_static(X) \
- static_assert(X, "Static assert has failed!");
-
-#endif //ASSERTSTATIC_H_INCLUDED
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 69e861be..41f42dbe 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -145,7 +145,7 @@ T clampCpy(const T& val, const T& minVal, const T& maxVal)
}
template <class T> inline
-void clamp(T& val, const T& minVal, const T& maxVal)
+void clamp(T& val, const T& minVal, const T& maxVal)
{
assert(minVal <= maxVal);
if (val < minVal)
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 3751e5dd..9a685fc5 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -40,7 +40,7 @@ public:
boost::lock_guard<boost::mutex> dummy(lockAccess);
if (bytesWritten == 0) //according to docu this may happen in case of internal buffer overflow: report some "dummy" change
- changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, L"Overflow."));
+ changedFiles.emplace_back(DirWatcher::ACTION_CREATE, L"Overflow.");
else
{
const char* bufPos = &buffer[0];
@@ -67,14 +67,14 @@ public:
{
case FILE_ACTION_ADDED:
case FILE_ACTION_RENAMED_NEW_NAME: //harmonize with "move" which is notified as "create + delete"
- changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, fullpath));
+ changedFiles.emplace_back(DirWatcher::ACTION_CREATE, fullpath);
break;
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
- changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_DELETE, fullpath));
+ changedFiles.emplace_back(DirWatcher::ACTION_DELETE, fullpath);
break;
case FILE_ACTION_MODIFIED:
- changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_UPDATE, fullpath));
+ changedFiles.emplace_back(DirWatcher::ACTION_UPDATE, fullpath);
break;
}
}();
@@ -193,8 +193,8 @@ public:
nullptr); //__in_opt LPCTSTR lpName
if (overlapped.hEvent == nullptr)
{
- const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"CreateEvent", lastError), lastError);
+ const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls!
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"CreateEvent", ec), ec);
}
ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent));
@@ -213,8 +213,8 @@ public:
&overlapped, // __inout_opt LPOVERLAPPED lpOverlapped,
nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
{
- const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", lastError), lastError);
+ const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls!
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", ec), ec);
}
//async I/O is a resource that needs to be guarded since it will write to local variable "buffer"!
@@ -236,9 +236,9 @@ public:
&bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred,
false)) //__in BOOL bWait
{
- const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- if (lastError != ERROR_IO_INCOMPLETE)
- return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"GetOverlappedResult", lastError), lastError);
+ const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls!
+ if (ec != ERROR_IO_INCOMPLETE)
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"GetOverlappedResult", ec), ec);
//execute asynchronous procedure calls (APC) queued on this thread
::SleepEx(50, // __in DWORD dwMilliseconds,
@@ -287,7 +287,7 @@ public:
bool finished() const { return operationComplete; }
private:
- virtual void onRequestRemoval(HANDLE hnd)
+ void onRequestRemoval(HANDLE hnd) override
{
//must release hDir immediately => stop monitoring!
if (worker_.joinable()) //= join() precondition: play safe; can't trust Windows to only call-back once
@@ -300,7 +300,7 @@ private:
removalRequested = true;
} //don't throw!
- virtual void onRemovalFinished(HANDLE hnd, bool successful) { operationComplete = true; } //throw()!
+ void onRemovalFinished(HANDLE hnd, bool successful) override { operationComplete = true; } //throw()!
boost::thread& worker_;
bool removalRequested;
@@ -320,13 +320,13 @@ struct DirWatcher::Pimpl
DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
- pimpl_(new Pimpl)
+ pimpl_(zen::make_unique<Pimpl>())
{
pimpl_->shared = std::make_shared<SharedData>();
pimpl_->dirpath = directory;
ReadChangesAsync reader(directory, pimpl_->shared); //throw FileError
- pimpl_->volRemoval.reset(new HandleVolumeRemoval(reader.getDirHandle(), pimpl_->worker)); //throw FileError
+ pimpl_->volRemoval = zen::make_unique<HandleVolumeRemoval>(reader.getDirHandle(), pimpl_->worker); //throw FileError
pimpl_->worker = boost::thread(std::move(reader));
}
@@ -360,7 +360,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(50));
}
- output.push_back(Entry(ACTION_DELETE, pimpl_->dirpath)); //report removal as change to main directory
+ output.emplace_back(ACTION_DELETE, pimpl_->dirpath); //report removal as change to main directory
}
else //the normal case...
pimpl_->shared->fetchChanges(output); //throw FileError
@@ -376,15 +376,15 @@ class DirsOnlyTraverser : public zen::TraverseCallback
public:
DirsOnlyTraverser(std::vector<Zstring>& dirs) : dirs_(dirs) {}
- virtual void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details) {}
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) { return LINK_SKIP; }
- virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath)
+ void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details ) override {}
+ HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { return LINK_SKIP; }
+ TraverseCallback* onDir (const Zchar* shortName, const Zstring& dirpath ) override
{
dirs_.push_back(dirpath);
return this;
}
- virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); }
- virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); }
+ HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); }
+ HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); }
private:
std::vector<Zstring>& dirs_;
@@ -403,7 +403,7 @@ struct DirWatcher::Pimpl
DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
- pimpl_(new Pimpl)
+ pimpl_(zen::make_unique<Pimpl>())
{
//get all subdirectories
Zstring dirpathFmt = directory;
@@ -449,9 +449,15 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
IN_MOVED_TO |
IN_MOVE_SELF);
if (wd == -1)
- throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), L"inotify_add_watch", getLastError());
+ {
+ const auto ec = getLastError();
+ if (ec == ENOSPC) //fix misleading system message "No space left on device"
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), 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."));
+
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), formatSystemError(L"inotify_add_watch", ec));
+ }
- pimpl_->watchDescrs.insert(std::make_pair(wd, appendSeparator(subdir)));
+ pimpl_->watchDescrs.emplace(wd, appendSeparator(subdir));
}
guardDescr.dismiss();
@@ -502,15 +508,15 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
if ((evt.mask & IN_CREATE) ||
(evt.mask & IN_MOVED_TO))
- output.push_back(Entry(ACTION_CREATE, fullname));
+ output.emplace_back(ACTION_CREATE, fullname);
else if ((evt.mask & IN_MODIFY) ||
(evt.mask & IN_CLOSE_WRITE))
- output.push_back(Entry(ACTION_UPDATE, fullname));
+ output.emplace_back(ACTION_UPDATE, fullname);
else if ((evt.mask & IN_DELETE ) ||
(evt.mask & IN_DELETE_SELF) ||
(evt.mask & IN_MOVE_SELF ) ||
(evt.mask & IN_MOVED_FROM))
- output.push_back(Entry(ACTION_DELETE, fullname));
+ output.emplace_back(ACTION_DELETE, fullname);
}
}
bytePos += sizeof(struct ::inotify_event) + evt.len;
@@ -541,7 +547,7 @@ void eventCallback(ConstFSEventStreamRef streamRef,
if (eventFlags[i] & kFSEventStreamEventFlagItemCreated ||
eventFlags[i] & kFSEventStreamEventFlagMount)
- changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, paths[i]));
+ changedFiles.emplace_back(DirWatcher::ACTION_CREATE, paths[i]);
if (eventFlags[i] & kFSEventStreamEventFlagItemModified || //
eventFlags[i] & kFSEventStreamEventFlagItemXattrMod || //
eventFlags[i] & kFSEventStreamEventFlagItemChangeOwner || //aggregate these into a single event
@@ -549,11 +555,11 @@ void eventCallback(ConstFSEventStreamRef streamRef,
eventFlags[i] & kFSEventStreamEventFlagItemFinderInfoMod || //
eventFlags[i] & kFSEventStreamEventFlagItemRenamed || //OS X sends the same event flag for both old and new names!!!
eventFlags[i] & kFSEventStreamEventFlagMustScanSubDirs) //something changed in one of the subdirs: NOT expected due to kFSEventStreamCreateFlagFileEvents
- changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_UPDATE, paths[i]));
+ changedFiles.emplace_back(DirWatcher::ACTION_UPDATE, paths[i]);
if (eventFlags[i] & kFSEventStreamEventFlagItemRemoved ||
eventFlags[i] & kFSEventStreamEventFlagRootChanged || //root is (indirectly) deleted or renamed
eventFlags[i] & kFSEventStreamEventFlagUnmount)
- changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_DELETE, paths[i]));
+ changedFiles.emplace_back(DirWatcher::ACTION_DELETE, paths[i]);
//kFSEventStreamEventFlagEventIdsWrapped -> irrelevant!
//kFSEventStreamEventFlagHistoryDone -> not expected due to kFSEventStreamEventIdSinceNow below
@@ -571,7 +577,7 @@ struct DirWatcher::Pimpl
DirWatcher::DirWatcher(const Zstring& directory) :
- pimpl_(new Pimpl)
+ pimpl_(zen::make_unique<Pimpl>())
{
CFStringRef dirpathCf = osx::createCFString(directory.c_str()); //returns nullptr on error
if (!dirpathCf)
diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp
deleted file mode 100644
index b38f73b4..00000000
--- a/zen/dst_hack.cpp
+++ /dev/null
@@ -1,368 +0,0 @@
-#include "dst_hack.h"
-#include <bitset>
-#include "basic_math.h"
-#include "long_path_prefix.h"
-#include "utf.h"
-#include "assert_static.h"
-#include "int64.h"
-#include "file_error.h"
-#include "dll.h"
-
-using namespace zen;
-
-
-namespace
-{
-//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points)
-Zstring getVolumeName(const Zstring& filepath)
-{
- //this call is expensive: ~1.5 ms!
- // if (!::GetVolumePathName(filepath.c_str(), //__in LPCTSTR lpszFileName,
- // fsName, //__out LPTSTR lpszVolumePathName,
- // BUFFER_SIZE)) //__in DWORD cchBufferLength
- // ...
- // Zstring volumePath = appendSeparator(fsName);
-
- const Zstring nameFmt = appendSeparator(removeLongPathPrefix(filepath)); //throw()
-
- if (startsWith(nameFmt, Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\"
- {
- size_t nameSize = nameFmt.size();
- const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2);
- if (posFirstSlash != Zstring::npos)
- {
- nameSize = posFirstSlash + 1;
- const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1);
- if (posSecondSlash != Zstring::npos)
- nameSize = posSecondSlash + 1;
- }
- return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash!
- }
- else //local path: "C:\Folder\"
- {
- const size_t pos = nameFmt.find(Zstr(":\\"));
- if (pos == 1) //expect single letter volume
- return Zstring(nameFmt.c_str(), 3);
- }
-
- return Zstring();
-}
-}
-
-
-bool dst::isFatDrive(const Zstring& filepath) //throw()
-{
- const Zstring volumePath = getVolumeName(filepath);
- if (volumePath.empty())
- return false;
-
- const DWORD bufferSize = MAX_PATH + 1;
- wchar_t fsName[bufferSize] = {};
-
- //suprisingly fast: ca. 0.03 ms per call!
- if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName,
- nullptr, //__out LPTSTR lpVolumeNameBuffer,
- 0, //__in DWORD nVolumeNameSize,
- nullptr, //__out_opt LPDWORD lpVolumeSerialNumber,
- nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
- nullptr, //__out_opt LPDWORD lpFileSystemFlags,
- fsName, //__out LPTSTR lpFileSystemNameBuffer,
- bufferSize)) //__in DWORD nFileSystemNameSize
- {
- assert(false); //shouldn't happen
- return false;
- }
- //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised)
- return fsName == Zstring(L"FAT") ||
- fsName == Zstring(L"FAT32");
-}
-
-
-/*
-bool dst::isFatDrive(HANDLE hFile) //throw()
-{
-Requires Windows Vista!
- //dynamically load windows API function
- typedef BOOL (WINAPI* GetVolumeInformationByHandleWFunc)(HANDLE hFile,
- LPWSTR lpVolumeNameBuffer,
- DWORD nVolumeNameSize,
- LPDWORD lpVolumeSerialNumber,
- LPDWORD lpMaximumComponentLength,
- LPDWORD lpFileSystemFlags,
- LPWSTR lpFileSystemNameBuffer,
- DWORD nFileSystemNameSize);
-
- const SysDllFun<GetVolumeInformationByHandleWFunc> getVolumeInformationByHandle(L"kernel32.dll", "GetVolumeInformationByHandleW");
- if (!getVolumeInformationByHandle)
- {
- assert(false);
- return false;
- }
-
- const size_t BUFFER_SIZE = MAX_PATH + 1;
- wchar_t fsName[BUFFER_SIZE];
-
- if (!getVolumeInformationByHandle(hFile, //__in HANDLE hFile,
- nullptr, //__out LPTSTR lpVolumeNameBuffer,
- 0, //__in DWORD nVolumeNameSize,
- nullptr, //__out_opt LPDWORD lpVolumeSerialNumber,
- nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
- nullptr, //__out_opt LPDWORD lpFileSystemFlags,
- fsName, //__out LPTSTR lpFileSystemNameBuffer,
- BUFFER_SIZE)) //__in DWORD nFileSystemNameSize
- {
- assert(false); //shouldn't happen
- return false;
- }
- //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised)
- return fsName == Zstring(L"FAT") ||
- fsName == Zstring(L"FAT32");
-}
-*/
-
-
-namespace
-{
-//convert std::uint64_t and std::int64_t to FILETIME
-inline
-FILETIME toFiletime(std::uint64_t number)
-{
- ULARGE_INTEGER cvt = {};
- cvt.QuadPart = number;
-
- const FILETIME output = { cvt.LowPart, cvt.HighPart };
- return output;
-}
-
-inline
-FILETIME toFiletime(std::int64_t number)
-{
- return toFiletime(static_cast<std::uint64_t>(number));
-}
-
-inline
-std::uint64_t toUInt64(const FILETIME& fileTime)
-{
- return get64BitUInt(fileTime.dwLowDateTime, fileTime.dwHighDateTime);
-}
-
-
-inline
-std::int64_t toInt64(const FILETIME& fileTime)
-{
- return get64BitUInt(fileTime.dwLowDateTime, fileTime.dwHighDateTime); //convert unsigned to signed in return
-}
-
-
-inline
-FILETIME utcToLocal(const FILETIME& utcTime) //throw std::runtime_error
-{
- //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC:
- FILETIME localTime = {};
- if (!::FileTimeToLocalFileTime(
- &utcTime, //__in const FILETIME *lpFileTime,
- &localTime)) //__out LPFILETIME lpLocalFileTime
- {
- const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- throw std::runtime_error(utfCvrtTo<std::string>(_("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" +
- L"High: " + numberTo<std::wstring>(utcTime.dwHighDateTime) + L" " +
- L"Low: " + numberTo<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"FileTimeToLocalFileTime", lastError)));
- }
- return localTime;
-}
-
-
-inline
-FILETIME localToUtc(const FILETIME& localTime) //throw std::runtime_error
-{
- //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC:
- FILETIME utcTime = {};
- if (!::LocalFileTimeToFileTime(
- &localTime, //__in const FILETIME *lpLocalFileTime,
- &utcTime)) //__out LPFILETIME lpFileTime
- {
- const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- throw std::runtime_error(utfCvrtTo<std::string>(_("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" +
- L"High: " + numberTo<std::wstring>(localTime.dwHighDateTime) + L" " +
- L"Low: " + numberTo<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"LocalFileTimeToFileTime", lastError)));
- }
- return utcTime;
-}
-
-
-//struct FILETIME {DWORD dwLowDateTime; DWORD dwHighDateTime;};
-const FILETIME FAT_MIN_TIME = { 13374976, 27846544 }; //1980 \ both are valid max/min FAT dates for 2 second precision
-const FILETIME FAT_MAX_TIME = { 14487552, 37251238 }; //2107 /
-
-//http://en.wikipedia.org/wiki/File_Allocation_Table
-const size_t PRECISION_WRITE_TIME = 20000000; //number of 100 ns per step -> 2 s
-const size_t PRECISION_CREATE_TIME = 100000; // -> 1/100 s
-
-/*
-Number of bits of information in create time: ln_2((FAT_MAX_TIME - FAT_MIN_TIME) / PRECISION_CREATE_TIME) = 38.55534023
-Number of bits of information in write time: 30.91148404
-*/
-//total size available to store data:
-const size_t CREATE_TIME_INFO_BITS = 38;
-// I. indicator that offset in II) is present
-const size_t INDICATOR_EXISTING_BITS = 1;
-// II. local<->UTC time offset
-const size_t UTC_LOCAL_OFFSET_BITS = 7;
-// III. indicator that offset in II) corresponds to current local write time (this could be a hash of the write time)
-const size_t WRITE_TIME_HASH_BITS = CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS - UTC_LOCAL_OFFSET_BITS;
-
-
-template <size_t precision> inline
-FILETIME encodeRawInformation(std::uint64_t rawInfo)
-{
- rawInfo *= precision;
- rawInfo += toUInt64(FAT_MIN_TIME);
-
- assert(rawInfo <= toUInt64(FAT_MAX_TIME));
- return toFiletime(rawInfo);
-}
-
-
-template <size_t precision> inline
-std::uint64_t extractRawInformation(const FILETIME& createTime)
-{
- assert(toUInt64(FAT_MIN_TIME) <= toUInt64(createTime));
- assert(toUInt64(createTime) <= toUInt64(FAT_MAX_TIME));
-
- //FAT create time ranges from 1980 - 2107 (2^7 years) with 1/100 seconds precision
- std::uint64_t rawInfo = toUInt64(createTime);
-
- rawInfo -= toUInt64(FAT_MIN_TIME);
- rawInfo /= precision; //reduce precision (FILETIME has unit 10^-7 s)
-
- assert(toUInt64(encodeRawInformation<precision>(rawInfo)) == toUInt64(createTime)); //must be reversible
- return rawInfo;
-}
-
-
-//convert write time to it's minimal representation (no restriction to FAT range "1980 - 2107")
-inline
-std::uint64_t extractRawWriteTime(const FILETIME& writeTime)
-{
- std::uint64_t rawInfo = toUInt64(writeTime);
- assert(rawInfo % PRECISION_WRITE_TIME == 0U);
- rawInfo /= PRECISION_WRITE_TIME; //reduce precision (FILETIME has unit 10^-7 s)
- return rawInfo;
-}
-
-
-//files with different resolution than 2 seconds are rounded up when written to FAT
-inline
-FILETIME roundToFatWriteTime(const FILETIME& writeTime)
-{
- std::uint64_t rawData = toUInt64(writeTime);
-
- if (rawData % PRECISION_WRITE_TIME != 0U)
- rawData += PRECISION_WRITE_TIME;
-
- rawData /= PRECISION_WRITE_TIME;
- rawData *= PRECISION_WRITE_TIME;
- return toFiletime(rawData);
-}
-
-
-//get 7-bit value representing time shift in number of quarter-hours
-std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift()
-{
- FILETIME utcTime = FAT_MIN_TIME;
- utcTime.dwHighDateTime += 5000000; //some arbitrary valid time
-
- const FILETIME localTime = utcToLocal(utcTime);
-
- const int timeShiftSec = static_cast<int>((toInt64(localTime) - toInt64(utcTime)) / 10000000); //time shift in seconds
-
- const int timeShiftQuarter = timeShiftSec / (60 * 15); //time shift in quarter-hours
-
- const int absValue = numeric::abs(timeShiftQuarter); //MSVC C++0x bug: std::bitset<>(unsigned long) is ambiguous
-
- if (std::bitset < UTC_LOCAL_OFFSET_BITS - 1 > (absValue).to_ulong() != static_cast<unsigned long>(absValue) || //time shifts that big shouldn't be possible!
- timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity!
- {
- const std::wstring errorMsg = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + L"(" + numberTo<std::wstring>(timeShiftSec) + L")";
- throw std::runtime_error(utfCvrtTo<std::string>(errorMsg));
- }
-
- std::bitset<UTC_LOCAL_OFFSET_BITS> output(absValue);
- output[UTC_LOCAL_OFFSET_BITS - 1] = timeShiftQuarter < 0; //sign bit
- return output;
-}
-
-
-//get time-zone shift in seconds
-inline
-int convertUtcLocalShift(std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift)
-{
- const bool hasSign = rawShift[UTC_LOCAL_OFFSET_BITS - 1];
- rawShift[UTC_LOCAL_OFFSET_BITS - 1] = false;
-
- assert_static(UTC_LOCAL_OFFSET_BITS <= sizeof(unsigned long) * 8);
- return hasSign ?
- static_cast<int>(rawShift.to_ulong()) * 15 * 60 * -1 :
- static_cast<int>(rawShift.to_ulong()) * 15 * 60;
-}
-}
-
-
-bool dst::fatHasUtcEncoded(const RawTime& rawTime) //"createTimeRaw" as retrieved by ::FindFirstFile() and ::GetFileAttributesEx(); throw std::runtime_error
-{
- if (toUInt64(rawTime.createTimeRaw) < toUInt64(FAT_MIN_TIME) ||
- toUInt64(FAT_MAX_TIME) < toUInt64(rawTime.createTimeRaw))
- {
- assert(false); //shouldn't be possible according to FAT specification
- return false;
- }
-
- const std::uint64_t rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw));
-
- assert_static(WRITE_TIME_HASH_BITS == 30);
- return (extractRawWriteTime(utcToLocal(rawTime.writeTimeRaw)) & 0x3FFFFFFFU) == (rawInfo & 0x3FFFFFFFU) && //ensure write time wasn't changed externally
- rawInfo >> (CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS) == 1U; //extended data available
-}
-
-
-dst::RawTime dst::fatEncodeUtcTime(const FILETIME& writeTimeRealUtc) //throw std::runtime_error
-{
- const FILETIME fatWriteTimeUtc = roundToFatWriteTime(writeTimeRealUtc); //writeTimeRealUtc may have incompatible precision (NTFS)
-
- //create time lets us store 40 bit of information
-
- //indicator that utc time is encoded -> hopefully results in a date long way in the future; but even if this bit is accidentally set, we still have the hash!
- std::uint64_t data = 1U;
-
- const std::bitset<UTC_LOCAL_OFFSET_BITS> utcShift = getUtcLocalShift();
- data <<= UTC_LOCAL_OFFSET_BITS;
- data |= utcShift.to_ulong();
-
- data <<= WRITE_TIME_HASH_BITS;
- data |= extractRawWriteTime(utcToLocal(fatWriteTimeUtc)) & 0x3FFFFFFFU; //trim to last 30 bit of information
- assert_static(WRITE_TIME_HASH_BITS == 30);
-
- const FILETIME encodedData = localToUtc(encodeRawInformation<PRECISION_CREATE_TIME>(data)); //localToUtc: make sure data is physically saved as FAT local time
- assert(toUInt64(FAT_MIN_TIME) <= toUInt64(encodedData));
- assert(toUInt64(encodedData) <= toUInt64(FAT_MAX_TIME));
-
- return RawTime(encodedData, fatWriteTimeUtc); //keep compatible with other applications, at least until DST shift actually occurs
-}
-
-
-FILETIME dst::fatDecodeUtcTime(const RawTime& rawTime) //return real UTC time; throw (std::runtime_error)
-{
- if (!fatHasUtcEncoded(rawTime))
- return rawTime.writeTimeRaw;
-
- const std::uint64_t rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw));
-
- const std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift(static_cast<int>((rawInfo >> WRITE_TIME_HASH_BITS) & 0x7FU)); //static_cast<int>: a shame MSC... "unsigned long long" should be supported instead!
- assert_static(UTC_LOCAL_OFFSET_BITS == 7);
-
- const std::int64_t timeShiftSec = convertUtcLocalShift(rawShift);
- const FILETIME writeTimeLocal = utcToLocal(rawTime.writeTimeRaw);
-
- const std::int64_t realUTC = toInt64(writeTimeLocal) - timeShiftSec * 10000000;
- return toFiletime(realUTC);
-}
diff --git a/zen/dst_hack.h b/zen/dst_hack.h
deleted file mode 100644
index 600107bb..00000000
--- a/zen/dst_hack.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef DST_HACK_H_INCLUDED
-#define DST_HACK_H_INCLUDED
-
-#include "win.h" //includes "windows.h"
-#include "zstring.h"
-#include <stdexcept>
-
-
-namespace dst
-{
-/*
-Solve DST +-1h and time zone shift issues on FAT drives
--------------------------------------------------------
-- (local) last write time is not touched!
-- all additional metadata is encoded in local create time:
- I. indicator that offset in II) is present
- II. local<->UTC time offset
- III. indicator that offset in II) corresponds to current local write time (a hash of local last write time)
-*/
-
-bool isFatDrive(const Zstring& fileName); //throw ()
-
-//all subsequent functions may throw the std::runtime_error exception!
-
-struct RawTime //time as retrieved by ::FindFirstFile() and ::GetFileAttributesEx()
-{
- RawTime(const FILETIME& create, const FILETIME& lastWrite) : createTimeRaw(create), writeTimeRaw(lastWrite) {}
- FILETIME createTimeRaw;
- FILETIME writeTimeRaw;
-};
-//save UTC time resistant against DST/time zone shifts
-bool fatHasUtcEncoded(const RawTime& rawTime); //throw std::runtime_error; as retrieved by ::FindFirstFile() and ::GetFileAttributesEx()
-
-RawTime fatEncodeUtcTime(const FILETIME& writeTimeRealUtc); //throw std::runtime_error
-FILETIME fatDecodeUtcTime(const RawTime& rawTime); //throw std::runtime_error; return last write time in real UTC
-}
-
-#endif // DST_HACK_H_INCLUDED
diff --git a/zen/file_handling.cpp b/zen/file_access.cpp
index 75062462..c4768dd2 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_access.cpp
@@ -4,7 +4,7 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#include "file_handling.h"
+#include "file_access.h"
#include <map>
#include <algorithm>
#include <stdexcept>
@@ -13,7 +13,6 @@
#include "scope_guard.h"
#include "symlink_target.h"
#include "file_io.h"
-#include "assert_static.h"
#include "file_id_def.h"
#ifdef ZEN_WIN
@@ -21,7 +20,6 @@
#include "privilege.h"
#include "dll.h"
#include "long_path_prefix.h"
-#include "dst_hack.h"
#include "win_ver.h"
#include "IFileOperation/file_op.h"
@@ -134,6 +132,70 @@ bool zen::somethingExists(const Zstring& objname)
namespace
{
#ifdef ZEN_WIN
+//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points)
+Zstring getVolumeNameFast(const Zstring& filepath)
+{
+ //this call is expensive: ~1.5 ms!
+ // if (!::GetVolumePathName(filepath.c_str(), //__in LPCTSTR lpszFileName,
+ // fsName, //__out LPTSTR lpszVolumePathName,
+ // BUFFER_SIZE)) //__in DWORD cchBufferLength
+ // ...
+ // Zstring volumePath = appendSeparator(fsName);
+
+ const Zstring nameFmt = appendSeparator(removeLongPathPrefix(filepath)); //throw()
+
+ if (startsWith(nameFmt, Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\"
+ {
+ size_t nameSize = nameFmt.size();
+ const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2);
+ if (posFirstSlash != Zstring::npos)
+ {
+ nameSize = posFirstSlash + 1;
+ const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1);
+ if (posSecondSlash != Zstring::npos)
+ nameSize = posSecondSlash + 1;
+ }
+ return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash!
+ }
+ else //local path: "C:\Folder\"
+ {
+ const size_t pos = nameFmt.find(Zstr(":\\"));
+ if (pos == 1) //expect single letter volume
+ return Zstring(nameFmt.c_str(), 3);
+ }
+
+ return Zstring();
+}
+
+
+bool isFatDrive(const Zstring& filepath) //throw()
+{
+ const Zstring volumePath = getVolumeNameFast(filepath);
+ if (volumePath.empty())
+ return false;
+
+ const DWORD bufferSize = MAX_PATH + 1;
+ wchar_t fsName[bufferSize] = {};
+
+ //suprisingly fast: ca. 0.03 ms per call!
+ if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName,
+ nullptr, //__out LPTSTR lpVolumeNameBuffer,
+ 0, //__in DWORD nVolumeNameSize,
+ nullptr, //__out_opt LPDWORD lpVolumeSerialNumber,
+ nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
+ nullptr, //__out_opt LPDWORD lpFileSystemFlags,
+ fsName, //__out LPTSTR lpFileSystemNameBuffer,
+ bufferSize)) //__in DWORD nFileSystemNameSize
+ {
+ assert(false); //shouldn't happen
+ return false;
+ }
+ //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised)
+ return fsName == Zstring(L"FAT") ||
+ fsName == Zstring(L"FAT32");
+}
+
+
//(try to) enhance error messages by showing which processes lock the file
Zstring getLockingProcessNames(const Zstring& filepath) //throw(), empty string if none found or error occurred
{
@@ -159,40 +221,38 @@ Zstring getLockingProcessNames(const Zstring& filepath) //throw(), empty string
std::uint64_t zen::getFilesize(const Zstring& filepath) //throw FileError
{
#ifdef ZEN_WIN
- WIN32_FIND_DATA fileInfo = {};
{
+ 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", fmtFileName(filepath)), L"FindFirstFile", getLastError());
::FindClose(searchHandle);
+
+ if (!isSymlink(fileInfo))
+ return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
}
// WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {};
- // if (!::GetFileAttributesEx(applyLongPathPrefix(sourceObj).c_str(), //__in LPCTSTR lpFileName,
- // GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
- // &sourceAttr)) //__out LPVOID lpFileInformation
-
- if (!isSymlink(fileInfo))
- return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
- else
- {
- //open handle to target of symbolic link
- const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).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_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", fmtFileName(filepath)), L"CreateFile", getLastError());
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
+ // if (!::GetFileAttributesEx(applyLongPathPrefix(filepath).c_str(), //__in LPCTSTR lpFileName,
+ // GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ // &sourceAttr)) //__out LPVOID lpFileInformation
+
+ //open handle to target of symbolic link
+ const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).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_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", fmtFileName(filepath)), L"CreateFile", getLastError());
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
- BY_HANDLE_FILE_INFORMATION fileInfoHnd = {};
- if (!::GetFileInformationByHandle(hFile, &fileInfoHnd))
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileInformationByHandle", getLastError());
+ BY_HANDLE_FILE_INFORMATION fileInfoHnd = {};
+ if (!::GetFileInformationByHandle(hFile, &fileInfoHnd))
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileInformationByHandle", getLastError());
- return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh);
- }
+ return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh);
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
@@ -494,11 +554,11 @@ public:
files_(files),
dirs_(dirs) {}
- virtual void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details)
+ void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override
{
files_.push_back(filepath);
}
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details)
+ HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override
{
if (dirExists(linkpath)) //dir symlink
dirs_.push_back(shortName);
@@ -506,13 +566,13 @@ public:
files_.push_back(shortName);
return LINK_SKIP;
}
- virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath)
+ TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) override
{
dirs_.push_back(dirpath);
return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively!
}
- virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); }
- virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); }
+ HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); }
+ HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); }
private:
CollectFilesFlat (const CollectFilesFlat&) = delete;
@@ -525,7 +585,7 @@ private:
void removeDirectoryImpl(const Zstring& directory, //throw FileError
const std::function<void (const Zstring& filepath)>& onBeforeFileDeletion,
- const std::function<void (const Zstring& dirpath)>& onBeforeDirDeletion)
+ const std::function<void (const Zstring& dirpath )>& onBeforeDirDeletion)
{
assert(somethingExists(directory)); //[!]
@@ -592,7 +652,10 @@ void removeDirectoryImpl(const Zstring& directory, //throw FileError
#ifdef ZEN_WIN
-void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const FILETIME& lastWriteTime, ProcSymlink procSl) //throw FileError
+void setFileTimeRaw(const Zstring& filepath,
+ const FILETIME* creationTime, //optional
+ const FILETIME& lastWriteTime,
+ ProcSymlink procSl) //throw FileError
{
{
//extra scope for debug check below
@@ -696,7 +759,7 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const
auto isNullTime = [](const FILETIME& ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; };
if (!::SetFileTime(hFile, //__in HANDLE hFile,
- !isNullTime(creationTime) ? &creationTime : nullptr, //__in_opt const FILETIME *lpCreationTime,
+ creationTime, //__in_opt const FILETIME *lpCreationTime,
nullptr, //__in_opt const FILETIME *lpLastAccessTime,
&lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime
{
@@ -736,8 +799,8 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const
FILE_BASIC_INFO basicInfo = {}; //undocumented: file times of "0" stand for "don't change"
basicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; //[!] the bug in the ticket above requires we set this together with file times!!!
basicInfo.LastWriteTime = toLargeInteger(lastWriteTime); //
- if (!isNullTime(creationTime))
- basicInfo.CreationTime = toLargeInteger(creationTime);
+ if (creationTime)
+ basicInfo.CreationTime = toLargeInteger(*creationTime);
//set file time + attributes
setFileInfo(basicInfo); //throw FileError
@@ -759,7 +822,7 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const
//add more meaningful message: FAT accepts only a subset of the NTFS date range
if (lastError == ERROR_INVALID_PARAMETER &&
- dst::isFatDrive(filepath))
+ isFatDrive(filepath))
{
//we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!!
auto fmtDate = [](const FILETIME& ft) -> Zstring
@@ -800,7 +863,7 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const
errorMsg += std::wstring(L"\nA FAT volume can only store dates between 1980 and 2107:\n") +
L"\twrite (UTC): \t" + fmtDate(lastWriteTime) +
- (!isNullTime(creationTime) ? L"\n\tcreate (UTC): \t" + fmtDate(creationTime) : L"");
+ (creationTime ? L"\n\tcreate (UTC): \t" + fmtDate(*creationTime) : L"");
}
if (lastError != ERROR_SUCCESS)
@@ -827,8 +890,8 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const
nullptr,
&lastWriteTimeDbg));
- assert(std::abs(filetimeToTimeT(lastWriteTimeDbg) - filetimeToTimeT(lastWriteTime)) <= 2); //respect 2 second FAT/FAT32 precision
- //assert(std::abs(filetimeToTimeT(creationTimeDbg ) - filetimeToTimeT(creationTime )) <= 2); -> creation time not available for Linux-hosted Samba shares!
+ assert(std::abs(filetimeToTimeT(lastWriteTimeDbg) - filetimeToTimeT(lastWriteTime)) <= 2); //respect 2 second FAT/FAT32 precision
+ //assert(std::abs(filetimeToTimeT(creationTimeDbg ) - filetimeToTimeT(creationTime )) <= 2); -> creation time not available for Linux-hosted Samba shares!
#endif
}
#endif
@@ -849,22 +912,7 @@ void zen::removeDirectory(const Zstring& directory, //throw FileError
void zen::setFileTime(const Zstring& filepath, std::int64_t modTime, ProcSymlink procSl) //throw FileError
{
#ifdef ZEN_WIN
- FILETIME creationTime = {};
- FILETIME lastWriteTime = timetToFileTime(modTime);
-
- //####################################### DST hack ###########################################
- warn_static("let's tentatively disable the DST hack:")
-#if 0
- if (dst::isFatDrive(filepath)) //throw(); hacky: does not consider symlinks pointing to FAT!
- {
- const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTime); //throw std::runtime_error
- creationTime = encodedTime.createTimeRaw;
- lastWriteTime = encodedTime.writeTimeRaw;
- }
-#endif
- //####################################### DST hack ###########################################
-
- setFileTimeRaw(filepath, creationTime, lastWriteTime, procSl); //throw FileError
+ setFileTimeRaw(filepath, nullptr, timetToFileTime(modTime), procSl); //throw FileError
#elif defined ZEN_LINUX || defined ZEN_MAC
//sigh, we can't use utimensat on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit???
@@ -878,7 +926,7 @@ void zen::setFileTime(const Zstring& filepath, std::int64_t modTime, ProcSymlink
//=> fallback to "retarded-idiot version"! -- DarkByte
- //OS X: utime() is obsoleted by utimes()! utimensat() not yet implemented
+ //OS X: utime() is obsoleted by utimes()! utimensat() not yet implemented
struct ::timeval newTimes[2] = {};
newTimes[0].tv_sec = ::time(nullptr); //access time (seconds)
@@ -1300,7 +1348,7 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
if (!templateDir.empty())
{
#ifdef ZEN_WIN
- //try to copy file attributes (dereference symlinks and junctions)
+ //optional: try to copy file attributes (dereference symlinks and junctions)
const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), //_In_ LPCTSTR lpFileName,
0, //_In_ DWORD dwDesiredAccess,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
@@ -1343,14 +1391,14 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
{
USHORT cmpState = COMPRESSION_FORMAT_DEFAULT;
DWORD bytesReturned = 0;
- /*bool rv = */::DeviceIoControl(hDirTrg, //handle to file or directory
- FSCTL_SET_COMPRESSION, //dwIoControlCode
- &cmpState, //input buffer
- sizeof(cmpState), //size of input buffer
- nullptr, //lpOutBuffer
- 0, //OutBufferSize
- &bytesReturned, //number of bytes returned
- nullptr); //OVERLAPPED structure
+ /*bool rv = */::DeviceIoControl(hDirTrg, //_In_ HANDLE hDevice,
+ FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode,
+ &cmpState, //_In_opt_ LPVOID lpInBuffer,
+ sizeof(cmpState), //_In_ DWORD nInBufferSize,
+ nullptr, //_Out_opt_ LPVOID lpOutBuffer,
+ 0, //_In_ DWORD nOutBufferSize,
+ &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
+ nullptr); //_Inout_opt_ LPOVERLAPPED lpOverlapped
}
//(try to) set creation and modification time
@@ -1385,12 +1433,12 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY);
}();
- //dynamically load windows API function
typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags);
const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW");
if (!createSymbolicLink)
- throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\""));
+ throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)),
+ replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\""));
const wchar_t functionName[] = L"CreateSymbolicLinkW";
if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required...
@@ -1400,9 +1448,9 @@ 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(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), functionName, getLastError());
+ throwFileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)), functionName, getLastError());
- //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist
+ //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist!
zen::ScopeGuard guardNewLink = zen::makeGuard([&]
{
try
@@ -1425,7 +1473,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
&sourceAttr)) //__out LPVOID lpFileInformation
throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"GetFileAttributesEx", getLastError());
- setFileTimeRaw(targetLink, sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError
+ setFileTimeRaw(targetLink, &sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat srcInfo = {};
@@ -1547,7 +1595,7 @@ bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //thr
void copyFileWindowsSparse(const Zstring& sourceFile,
const Zstring& targetFile,
const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus,
- InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
+ InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked
{
assert(canCopyAsSparse(sourceFile, targetFile));
@@ -1626,8 +1674,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
lastError == 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 (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr);
throw FileError(errorMsg, errorDescr);
}
@@ -1655,14 +1702,14 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
{
USHORT cmpState = COMPRESSION_FORMAT_DEFAULT;
DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hFileTarget, //handle to file or directory
- FSCTL_SET_COMPRESSION, //dwIoControlCode
- &cmpState, //input buffer
- sizeof(cmpState), //size of input buffer
- nullptr, //lpOutBuffer
- 0, //OutBufferSize
- &bytesReturned, //number of bytes returned
- nullptr)) //OVERLAPPED structure
+ if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice,
+ FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode,
+ &cmpState, //_In_opt_ LPVOID lpInBuffer,
+ sizeof(cmpState), //_In_ DWORD nInBufferSize,
+ nullptr, //_Out_opt_ LPVOID lpOutBuffer,
+ 0, //_In_ DWORD nOutBufferSize,
+ &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
+ nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
{} //may legitimately fail with ERROR_INVALID_FUNCTION if:
// - target folder is encrypted
// - target volume does not support compressed attribute -> unlikely in this context
@@ -1678,14 +1725,14 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
//if (sourceIsSparse && targetSupportsSparse) -> no need to check, this is our precondition!
{
DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hFileTarget, //handle to file
- FSCTL_SET_SPARSE, //dwIoControlCode
- nullptr, //input buffer
- 0, //size of input buffer
- nullptr, //lpOutBuffer
- 0, //OutBufferSize
- &bytesReturned, //number of bytes returned
- nullptr)) //OVERLAPPED structure
+ if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice,
+ FSCTL_SET_SPARSE, //_In_ DWORD dwIoControlCode,
+ nullptr, //_In_opt_ LPVOID lpInBuffer,
+ 0, //_In_ DWORD nInBufferSize,
+ nullptr, //_Out_opt_ LPVOID lpOutBuffer,
+ 0, //_In_ DWORD nOutBufferSize,
+ &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
+ nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError());
}
@@ -1805,7 +1852,7 @@ public:
//call context: copyCallbackInternal()
void reportErrorShouldCopyAsSparse() { shouldCopyAsSparse = true; }
- void reportUserException(const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { exceptionInUserCallback = onUpdateCopyStatus; }
+ void reportUserException(const std::exception_ptr& e) { exception = e; }
void reportError(const std::wstring& msg, const std::wstring& description) { errorMsg = std::make_pair(msg, description); }
@@ -1815,11 +1862,8 @@ public:
if (shouldCopyAsSparse)
throw ErrorShouldCopyAsSparse(L"sparse dummy value");
- if (exceptionInUserCallback)
- {
- exceptionInUserCallback(0); //should throw again!!!
- assert(false); //
- }
+ if (exception)
+ std::rethrow_exception(exception);
if (!errorMsg.first.empty())
throw FileError(errorMsg.first, errorMsg.second);
@@ -1828,7 +1872,7 @@ public:
private:
bool shouldCopyAsSparse; //
std::pair<std::wstring, std::wstring> errorMsg; //these are exclusive!
- std::function<void(std::int64_t bytesDelta)> exceptionInUserCallback; // -> optional
+ std::exception_ptr exception;
};
@@ -1924,15 +1968,16 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
{
USHORT cmpState = COMPRESSION_FORMAT_DEFAULT;
DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hDestinationFile, //handle to file or directory
- FSCTL_SET_COMPRESSION, //dwIoControlCode
- &cmpState, //input buffer
- sizeof(cmpState), //size of input buffer
- nullptr, //lpOutBuffer
- 0, //OutBufferSize
- &bytesReturned, //number of bytes returned
- nullptr)) //OVERLAPPED structure
+ if (!::DeviceIoControl(hDestinationFile, //_In_ HANDLE hDevice,
+ FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode,
+ &cmpState, //_In_opt_ LPVOID lpInBuffer,
+ sizeof(cmpState), //_In_ DWORD nInBufferSize,
+ nullptr, //_Out_opt_ LPVOID lpOutBuffer,
+ 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
// - target volume does not support compressed attribute
//#############################################################################
@@ -1949,9 +1994,7 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
}
catch (...)
{
- //#warning migrate to std::exception_ptr when available
-
- cbd.errorHandler.reportUserException(cbd.onUpdateCopyStatus_);
+ cbd.errorHandler.reportUserException(std::current_exception());
return PROGRESS_CANCEL;
}
return PROGRESS_CONTINUE;
@@ -1965,7 +2008,7 @@ const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destinat
void copyFileWindowsDefault(const Zstring& sourceFile,
const Zstring& targetFile,
const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus,
- InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse
+ InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse
{
//try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors
try { activatePrivilege(SE_BACKUP_NAME); }
@@ -1979,7 +2022,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS;
if (supportNonEncryptedDestination)
- copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrytped location
+ copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrypted location
//if (vistaOrLater()) //see http://blogs.technet.com/b/askperf/archive/2007/05/08/slow-large-file-copy-issues.aspx
// copyFlags |= COPY_FILE_NO_BUFFERING; //no perf difference at worst, huge improvement for large files (20% in test NTFS -> NTFS)
@@ -1989,13 +2032,12 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
CallbackData cbd(onUpdateCopyStatus, sourceFile, targetFile);
- const bool success = ::CopyFileEx( //same performance like CopyFile()
- applyLongPathPrefix(sourceFile).c_str(), //__in LPCTSTR lpExistingFileName,
- applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpNewFileName,
- copyCallbackInternal, //__in_opt LPPROGRESS_ROUTINE lpProgressRoutine,
- &cbd, //__in_opt LPVOID lpData,
- nullptr, //__in_opt LPBOOL pbCancel,
- copyFlags) != FALSE; //__in DWORD dwCopyFlags
+ const bool success = ::CopyFileEx(applyLongPathPrefix(sourceFile).c_str(), //__in LPCTSTR lpExistingFileName,
+ applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpNewFileName,
+ copyCallbackInternal, //__in_opt LPPROGRESS_ROUTINE lpProgressRoutine,
+ &cbd, //__in_opt LPVOID lpData,
+ nullptr, //__in_opt LPBOOL pbCancel,
+ copyFlags) != FALSE; //__in DWORD dwCopyFlags
cbd.errorHandler.evaluateErrors(); //throw ?, process errors in callback first!
if (!success)
@@ -2005,7 +2047,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
//don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition!
//trying to copy huge sparse files may directly fail with ERROR_DISK_FULL before entering the callback function
- if (canCopyAsSparse(sourceFile, targetFile)) //throw ()
+ if (canCopyAsSparse(sourceFile, targetFile)) //noexcept
throw ErrorShouldCopyAsSparse(L"sparse dummy value2");
//assemble error message...
@@ -2030,20 +2072,16 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
throw ErrorTargetExisting(errorMsg, errorDescr);
}
- if (lastError == ERROR_PATH_NOT_FOUND)
- {
- guardTarget.dismiss(); //not relevant
- throw ErrorTargetPathMissing(errorMsg, errorDescr); //could this also be source path missing!?
- }
+ //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); //could this also be source path missing!?
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 &&
- dst::isFatDrive(targetFile) &&
+ isFatDrive(targetFile) &&
getFilesize(sourceFile) >= 4U * std::uint64_t(1024U * 1024 * 1024)) //throw FileError
errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabyte.";
- //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us
+ //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us
//note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filepath is of a restricted type.
}
@@ -2055,51 +2093,15 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
if (newAttrib)
{
newAttrib->fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh);
- //newAttrib->modificationTime = -> set further below
+ newAttrib->modificationTime = filetimeToTimeT(cbd.fileInfoSrc.ftLastWriteTime);
newAttrib->sourceFileId = extractFileId(cbd.fileInfoSrc);
newAttrib->targetFileId = extractFileId(cbd.fileInfoTrg);
}
+ warn_static("new perf check + investigate improvements now that DST hcak is gone! =>")
- {
- FILETIME creationtime = cbd.fileInfoSrc.ftCreationTime;
- FILETIME lastWriteTimeRaw = cbd.fileInfoSrc.ftLastWriteTime;
- //####################################### DST hack ###########################################
- warn_static("let's tentatively disable the DST hack:")
-#if 0
- if (dst::isFatDrive(sourceFile)) //throw(); hacky: does not consider symlinks pointing to FAT!
- {
- const dst::RawTime rawTime(creationtime, lastWriteTimeRaw);
- if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error
- {
- lastWriteTimeRaw = dst::fatDecodeUtcTime(rawTime); //return last write time in real UTC, throw (std::runtime_error)
- ::GetSystemTimeAsFileTime(&creationtime); //real creation time information is not available...
- }
- }
-#endif
- //####################################### DST hack ###########################################
-
- if (newAttrib)
- newAttrib->modificationTime = filetimeToTimeT(lastWriteTimeRaw);
-
- //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking!
- // - perf-loss on USB sticks with many small files of about 30%!
- FILETIME creationTimeOut = creationtime;
- FILETIME lastWriteTimeOut = lastWriteTimeRaw;
-
- //####################################### DST hack ###########################################
- warn_static("let's tentatively disable the DST hack:")
-#if 0
- if (dst::isFatDrive(targetFile)) //throw(); target cannot be a symlink in this context!
- {
- const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTimeRaw); //throw std::runtime_error
- creationTimeOut = encodedTime.createTimeRaw;
- lastWriteTimeOut = encodedTime.writeTimeRaw;
- }
-#endif
- //####################################### DST hack ###########################################
-
- setFileTimeRaw(targetFile, creationTimeOut, lastWriteTimeOut, ProcSymlink::FOLLOW); //throw FileError
- }
+ //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking!
+ // - perf-loss on USB sticks with many small files of about 30%!
+ setFileTimeRaw(targetFile, &cbd.fileInfoSrc.ftCreationTime, cbd.fileInfoSrc.ftLastWriteTime, ProcSymlink::FOLLOW); //throw FileError
guardTarget.dismiss(); //target has been created successfully!
}
@@ -2111,11 +2113,11 @@ void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targ
{
try
{
- copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw ErrorShouldCopyAsSparse et al.
+ copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse
}
- catch (ErrorShouldCopyAsSparse&) //we cheaply check for this condition within callback of ::CopyFileEx()!
+ catch (ErrorShouldCopyAsSparse&) //we quickly check for this condition within callback of ::CopyFileEx()!
{
- copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr);
+ copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked
}
}
@@ -2129,7 +2131,7 @@ void copyFileWindows(const Zstring& sourceFile,
{
try
{
- copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
+ copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked
}
catch (const ErrorTargetExisting&)
{
@@ -2149,22 +2151,19 @@ void copyFileWindows(const Zstring& sourceFile,
void copyFileLinuxMac(const Zstring& sourceFile,
const Zstring& targetFile,
const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus,
- InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting
{
- //open sourceFile for reading
FileInputUnbuffered fileIn(sourceFile); //throw FileError
struct ::stat sourceInfo = {};
- if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source
+ if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0)
throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"fstat", getLastError());
zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: place guard before lifetime of FileOutput
try
{
- //create targetFile and open it for writing
- FileOutputUnbuffered fileOut(targetFile, sourceInfo.st_mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ FileOutputUnbuffered fileOut(targetFile, sourceInfo.st_mode); //throw FileError, ErrorTargetExisting
- //copy contents of sourceFile to targetFile
std::vector<char> buffer(128 * 1024); //see comment in FileInputUnbuffered::read
do
{
@@ -2172,7 +2171,6 @@ void copyFileLinuxMac(const Zstring& sourceFile,
fileOut.write(&buffer[0], bytesRead); //throw FileError
- //invoke callback method to update progress indicators
if (onUpdateCopyStatus)
onUpdateCopyStatus(bytesRead); //throw X!
}
@@ -2229,20 +2227,31 @@ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::Backu
inline
void copyFileSelectOs(const Zstring& sourceFile,
const Zstring& targetFile,
+ bool copyFilePermissions,
const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus,
InSyncAttributes* sourceAttr)
{
#ifdef ZEN_WIN
- copyFileWindows(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
+ copyFileWindows(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked
#elif defined ZEN_LINUX || defined ZEN_MAC
- copyFileLinuxMac(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ copyFileLinuxMac(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting
#endif
+
+ if (copyFilePermissions)
+ {
+ //at this point we know we created a new file, so it's fine to delete it for cleanup!
+ zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}});
+
+ copyObjectPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError
+
+ guardTargetFile.dismiss(); //target has been created successfully!
+ }
}
}
-void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissing, ErrorFileLocked
+void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked
const Zstring& targetFile,
bool copyFilePermissions,
bool transactionalCopy,
@@ -2252,13 +2261,12 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath
{
if (transactionalCopy)
{
- Zstring tmpTarget = targetFile + TEMP_FILE_ENDING; //use temporary file until a correct date has been set
+ Zstring tmpTarget = targetFile + TEMP_FILE_ENDING;
- //raw file copy
for (int i = 0;; ++i)
try
{
- copyFileSelectOs(sourceFile, tmpTarget, onUpdateCopyStatus, sourceAttr); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
+ copyFileSelectOs(sourceFile, tmpTarget, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked
break;
}
catch (const ErrorTargetExisting&) //optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses
@@ -2274,7 +2282,6 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath
if (onDeleteTargetFile)
onDeleteTargetFile(); //throw X
- //rename temporary file:
//perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick!
renameFile(tmpTarget, targetFile); //throw FileError
@@ -2284,45 +2291,23 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath
This "feature" is called "File System Tunneling":
http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx
http://support.microsoft.com/kb/172190/en-us
-
- However during the next comparison the DST hack will be applied correctly since the DST-hash of the mod.time is invalid.
-
- EXCEPTION: the hash may match!!! reproduce:
- 1. set system time back to date within previous DST
- 2. save some file on FAT32 usb stick and FFS-compare to make sure the DST hack is applied correctly
- 4. pull out usb stick, put back in
- 3. restore system time
- 4. copy file from USB to local drive via explorer
- =>
- NTFS <-> FAT, file exists on both sides; mod times match, DST hack on USB stick causes 1-hour offset when comparing in FFS.
- When syncing, modification time is copied correctly, but new DST hack fails to apply and old creation time is reused (see above).
- Unfortunately, the old DST hash matches mod time! => On next comparison FFS will *still* see both sides as different!!!!!!!!!
*/
guardTempFile.dismiss();
}
else
{
+ /*
+ Note: non-transactional file copy solves at least four problems:
+ -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER
+ -> network renaming issues
+ -> allow for true delete before copy to handle low disk space problems
+ -> higher performance on non-buffered drives (e.g. usb sticks)
+ */
+
if (onDeleteTargetFile)
onDeleteTargetFile();
- copyFileSelectOs(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
- }
- /*
- Note: non-transactional file copy solves at least four problems:
- -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER
- -> network renaming issues
- -> allow for true delete before copy to handle low disk space problems
- -> higher performance on non-buffered drives (e.g. usb sticks)
- */
-
- //file permissions
- if (copyFilePermissions)
- {
- zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}});
-
- copyObjectPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError
-
- guardTargetFile.dismiss(); //target has been created successfully!
+ copyFileSelectOs(sourceFile, targetFile, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked
}
}
diff --git a/zen/file_handling.h b/zen/file_access.h
index b58f6c07..7cac889c 100644
--- a/zen/file_handling.h
+++ b/zen/file_access.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef FILE_HANDLING_H_8017341345614857
-#define FILE_HANDLING_H_8017341345614857
+#ifndef FILE_ACCESS_H_8017341345614857
+#define FILE_ACCESS_H_8017341345614857
#include <functional>
#include "zstring.h"
@@ -57,15 +57,13 @@ struct InSyncAttributes
FileId targetFileId;
};
-void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissing, ErrorFileLocked (Windows-only)
+void copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked (Windows-only)
const Zstring& targetFile, //symlink handling: dereference source
bool copyFilePermissions,
bool transactionalCopy,
//if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing!
//if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it.
const std::function<void()>& onDeleteTargetFile, //may be nullptr; may throw!
- //Linux: unconditionally
- //Windows: first exception is swallowed, updateCopyStatus() is then called again where it should throw again and the exception will propagate as expected
//accummulated delta != file size! consider ADS, sparse, compressed files
const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, //may be nullptr; may throw!
@@ -79,4 +77,4 @@ const Zchar TEMP_FILE_ENDING[] = Zstr(".ffs_tmp"); //don't use Zstring as global
void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError
}
-#endif //FILE_HANDLING_H_8017341345614857
+#endif //FILE_ACCESS_H_8017341345614857
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
index daadd391..3b217b3d 100644
--- a/zen/file_id_def.h
+++ b/zen/file_id_def.h
@@ -8,7 +8,6 @@
#define FILE_ID_INTERNAL_HEADER_013287632486321493
#include <utility>
-#include "assert_static.h"
#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
@@ -44,9 +43,9 @@ FileId extractFileId(DWORD volumeSerialNumber, ULONGLONG fileIndex)
FileId(volumeSerialNumber, fileIndex) : FileId();
}
-assert_static(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber));
-assert_static(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow));
-assert_static(sizeof(FileId().second) == sizeof(ULARGE_INTEGER));
+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), "");
#elif defined ZEN_LINUX || defined ZEN_MAC
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index e11a1201..8f4b6cb2 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -187,7 +187,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number
FileOutput::FileOutput(FileHandle handle, const Zstring& filepath) : FileOutputBase(filepath), fileHandle(handle) {}
-FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetExisting
FileOutputBase(filepath)
{
#ifdef ZEN_WIN
@@ -246,8 +246,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
lastError == 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 (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr);
throw FileError(errorMsg, errorDescr);
}
@@ -267,8 +266,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
if (lastError == EEXIST)
throw ErrorTargetExisting(errorMsg, errorDescr);
- if (lastError == ENOENT)
- throw ErrorTargetPathMissing(errorMsg, errorDescr);
+ //if (lastError == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr);
throw FileError(errorMsg, errorDescr);
}
@@ -348,7 +346,7 @@ size_t FileInputUnbuffered::read(void* buffer, size_t bytesToRead) //throw FileE
}
-FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) : FileOutputBase(filepath) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) : FileOutputBase(filepath) //throw FileError, ErrorTargetExisting
{
//checkForUnsupportedType(filepath); -> not needed, open() + O_EXCL shoul fail fast
@@ -363,8 +361,7 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode)
if (lastError == EEXIST)
throw ErrorTargetExisting(errorMsg, errorDescr);
- if (lastError == ENOENT)
- throw ErrorTargetPathMissing(errorMsg, errorDescr);
+ //if (lastError == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr);
throw FileError(errorMsg, errorDescr);
}
diff --git a/zen/file_io.h b/zen/file_io.h
index 5ae6eb1c..7ccef05b 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -26,7 +26,7 @@ static const char LINE_BREAK[] = "\r\n";
static const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too
#endif
-//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks)
+//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support + following symlinks
#ifdef ZEN_WIN
typedef HANDLE FileHandle;
@@ -41,8 +41,7 @@ public:
FileInput(FileHandle handle, const Zstring& filepath); //takes ownership!
~FileInput();
- virtual size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read
- //expected to fill buffer completely unless "end of file"
+ size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read
private:
FileHandle fileHandle;
@@ -52,17 +51,19 @@ private:
class FileOutput : public FileOutputBase
{
public:
- FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetExisting
FileOutput(FileHandle handle, const Zstring& filepath); //takes ownership!
~FileOutput();
- virtual void write(const void* buffer, size_t bytesToWrite) override; //throw FileError
+ void write(const void* buffer, size_t bytesToWrite) override; //throw FileError
private:
FileHandle fileHandle;
};
#if defined ZEN_LINUX || defined ZEN_MAC
+warn_static("get rid of FileInputUnbuffered/FileOutputUnbuffered, use fdopen instead")
+
class FileInputUnbuffered : public FileInputBase
{
public:
@@ -70,7 +71,7 @@ public:
~FileInputUnbuffered();
//considering safe-read.c it seems buffer size should be a multiple of 8192
- virtual size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read
+ size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read
//do NOT rely on partially filled buffer meaning EOF!
int getDescriptor() { return fdFile;}
@@ -83,11 +84,11 @@ class FileOutputUnbuffered : public FileOutputBase
{
public:
//creates a new file (no overwrite allowed!)
- FileOutputUnbuffered(const Zstring& filepath, mode_t mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ FileOutputUnbuffered(const Zstring& filepath, mode_t mode); //throw FileError, ErrorTargetExisting
FileOutputUnbuffered(int fd, const Zstring& filepath); //takes ownership!
~FileOutputUnbuffered();
- virtual void write(const void* buffer, size_t bytesToWrite) override; //throw FileError
+ void write(const void* buffer, size_t bytesToWrite) override; //throw FileError
int getDescriptor() { return fdFile;}
private:
diff --git a/zen/file_io_base.h b/zen/file_io_base.h
index 8e9bf12e..e56ea189 100644
--- a/zen/file_io_base.h
+++ b/zen/file_io_base.h
@@ -21,8 +21,8 @@ protected:
~FileBase() {}
private:
- FileBase(const FileBase&); //=delete
- FileBase& operator=(const FileBase&); //
+ FileBase (const FileBase&) = delete;
+ FileBase& operator=(const FileBase&) = delete;
const Zstring filename_;
};
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index d8f2d0cf..7b423faa 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -6,15 +6,13 @@
#include "file_traverser.h"
#include "sys_error.h"
-#include "assert_static.h"
#include "symlink_target.h"
#include "int64.h"
#ifdef ZEN_WIN
#include "win_ver.h"
#include "long_path_prefix.h"
-#include "dst_hack.h"
-#include "file_handling.h" //remove this huge dependency when getting rid of DST hack!! until then we need "setFileTime"
+#include "file_access.h"
#include "dll.h"
#include "FindFilePlus/find_file_plus.h"
@@ -30,7 +28,7 @@
using namespace zen;
-
+
namespace
{
//implement "retry" in a generic way:
@@ -179,8 +177,8 @@ struct Win32Traverser
{
struct DirHandle
{
- DirHandle(HANDLE hnd, const WIN32_FIND_DATA& d) : searchHandle(hnd), haveData(true), data(d) {}
- explicit DirHandle(HANDLE hnd) : searchHandle(hnd), haveData(false) {}
+ DirHandle(HANDLE hnd, const WIN32_FIND_DATA& d) : searchHandle(hnd), haveData(true), data(d) {}
+ explicit DirHandle(HANDLE hnd) : searchHandle(hnd), haveData(false) {}
HANDLE searchHandle;
bool haveData;
@@ -193,7 +191,7 @@ struct Win32Traverser
{
const Zstring& dirpathPf = appendSeparator(dirpath);
- WIN32_FIND_DATA fileData = {};
+ WIN32_FIND_DATA fileData = {};
HANDLE hnd = ::FindFirstFile(applyLongPathPrefix(dirpathPf + L'*').c_str(), &fileData);
//no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
if (hnd == INVALID_HANDLE_VALUE)
@@ -208,7 +206,7 @@ struct Win32Traverser
}
throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"FindFirstFile", lastError);
}
- return DirHandle(hnd, fileData);
+ return DirHandle(hnd, fileData);
}
static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw()
@@ -271,11 +269,11 @@ struct FilePlusTraverser
static DirHandle create(const Zstring& dirpath) //throw FileError
{
- const findplus::FindHandle hnd = ::openDir(applyLongPathPrefix(dirpath).c_str());
+ const findplus::FindHandle hnd = ::openDir(applyLongPathPrefix(dirpath).c_str());
if (!hnd)
- throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"openDir", getLastError());
+ throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"openDir", getLastError());
- return DirHandle(hnd);
+ return DirHandle(hnd);
}
static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw()
@@ -325,13 +323,13 @@ struct FilePlusTraverser
class DirTraverser
{
public:
- static void execute(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback)
+ static void execute(const Zstring& baseDirectory, TraverseCallback& sink)
{
- DirTraverser(baseDirectory, sink, dstCallback);
+ DirTraverser(baseDirectory, sink);
}
private:
- DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback);
+ DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink);
DirTraverser (const DirTraverser&) = delete;
DirTraverser& operator=(const DirTraverser&) = delete;
@@ -340,58 +338,6 @@ private:
template <class Trav>
void traverseWithException(const Zstring& dirpath, TraverseCallback& sink, DWORD volumeSerial /*may be 0!*/); //throw FileError, NeedFallbackToWin32Traverser
-
- //####################################### DST hack ###########################################
- void applyDstHack(zen::DstHackCallback& dstCallback)
- {
- int failedAttempts = 0;
- int filesToValidate = 50; //don't let data verification become a performance issue
-
- for (auto it = markForDstHack.begin(); it != markForDstHack.end(); ++it)
- {
- if (failedAttempts >= 10) //some cloud storages don't support changing creation/modification times => don't waste (a lot of) time trying to
- return;
-
- dstCallback.requestUiRefresh(it->first);
-
- try
- {
- //set modification time including DST hack: this function is too clever to not introduce this dependency
- setFileTime(it->first, it->second, ProcSymlink::FOLLOW); //throw FileError
- }
- catch (FileError&)
- {
- ++failedAttempts;
- assert(false); //don't throw exceptions due to dst hack here
- continue;
- }
-
- //even at this point it's not sure whether data was written correctly, again cloud storages tend to lie about success status
- if (filesToValidate-- > 0)
- {
- const dst::RawTime encodedTime = dst::fatEncodeUtcTime(timetToFileTime(it->second)); //throw std::runtime_error
-
- //dst hack: verify data written; attention: this check may fail for "sync.ffs_lock"
- WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {};
- ::GetFileAttributesEx(zen::applyLongPathPrefix(it->first).c_str(), //__in LPCTSTR lpFileName,
- GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
- &debugeAttr); //__out LPVOID lpFileInformation
-
- if (::CompareFileTime(&debugeAttr.ftCreationTime, &encodedTime.createTimeRaw) != 0 ||
- ::CompareFileTime(&debugeAttr.ftLastWriteTime, &encodedTime.writeTimeRaw) != 0)
- {
- ++failedAttempts;
- assert(false); //don't throw exceptions due to dst hack here
- continue;
- }
- }
- }
- }
-
- const bool needDstHack;
- typedef std::vector<std::pair<Zstring, std::int64_t>> FilenameTimeList;
- FilenameTimeList markForDstHack;
- //####################################### DST hack ###########################################
};
@@ -420,8 +366,7 @@ void DirTraverser::traverse<FilePlusTraverser>(const Zstring& dirpath, TraverseC
inline
-DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) :
- needDstHack(dstCallback ? dst::isFatDrive(baseDirectory) : false)
+DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink)
{
try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail)
{
@@ -433,11 +378,6 @@ DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink,
traverse<FilePlusTraverser>(baseDirectory, sink, retrieveVolumeSerial(baseDirectory)); //retrieveVolumeSerial returns 0 on error
else //fallback
traverse<Win32Traverser>(baseDirectory, sink, 0);
-
- //apply daylight saving time hack AFTER file traversing, to give separate feedback to user
- if (needDstHack)
- if (dstCallback) //bound if "needDstHack == true"
- applyDstHack(*dstCallback);
}
@@ -507,20 +447,7 @@ void DirTraverser::traverseWithException(const Zstring& dirpath, TraverseCallbac
}
else //a file
{
- TraverseCallback::FileInfo fileInfo = Trav::extractFileInfo(findData, volumeSerial);
-
- //####################################### DST hack ###########################################
- if (needDstHack)
- {
- const dst::RawTime rawTime(Trav::getCreateTimeRaw(findData), Trav::getModTimeRaw(findData));
-
- if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error
- fileInfo.lastWriteTime = filetimeToTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw std::runtime_error
- else
- markForDstHack.push_back(std::make_pair(itempath, filetimeToTimeT(rawTime.writeTimeRaw)));
- }
- //####################################### DST hack ###########################################
-
+ const TraverseCallback::FileInfo fileInfo = Trav::extractFileInfo(findData, volumeSerial);
sink.onFile(shortName, itempath, fileInfo);
}
}
@@ -531,13 +458,13 @@ void DirTraverser::traverseWithException(const Zstring& dirpath, TraverseCallbac
class DirTraverser
{
public:
- static void execute(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback)
+ static void execute(const Zstring& baseDirectory, TraverseCallback& sink)
{
- DirTraverser(baseDirectory, sink, dstCallback);
+ DirTraverser(baseDirectory, sink);
}
private:
- DirTraverser(const Zstring& baseDirectory, zen::TraverseCallback& sink, zen::DstHackCallback* dstCallback)
+ DirTraverser(const Zstring& baseDirectory, zen::TraverseCallback& sink)
{
const Zstring directoryFormatted = //remove trailing slash
baseDirectory.size() > 1 && endsWith(baseDirectory, FILE_NAME_SEPARATOR) ? //exception: allow '/'
@@ -701,12 +628,4 @@ private:
}
-void zen::traverseFolder(const Zstring& dirpath, TraverseCallback& sink, DstHackCallback* dstCallback)
-{
- warn_static("let's tentatively disable the DST hack:")
- DirTraverser::execute(dirpath, sink, nullptr);
- return;
-#if 0
- DirTraverser::execute(dirpath, sink, dstCallback);
-#endif
-}
+void zen::traverseFolder(const Zstring& dirpath, TraverseCallback& sink) { DirTraverser::execute(dirpath, sink); }
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index f240d8c7..c9efbdb1 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -58,23 +58,10 @@ struct TraverseCallback
virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) = 0; //failed to get data for single file/dir/symlink only!
};
-
-#ifdef ZEN_WIN
-struct DstHackCallback
-{
- virtual ~DstHackCallback() {}
- virtual void requestUiRefresh(const Zstring& filepath) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user
-};
-#elif defined ZEN_LINUX || defined ZEN_MAC
-struct DstHackCallback; //DST hack not required on Unix
-#endif
-
//custom traverser with detail information about files
//- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...)
//- directory may end with PATH_SEPARATOR
-void traverseFolder(const Zstring& dirpath, //throw()
- TraverseCallback& sink,
- DstHackCallback* dstCallback = nullptr); //apply DST hack if callback is supplied
+void traverseFolder(const Zstring& dirpath, TraverseCallback& sink); //noexcept
}
#endif // FILETRAVERSER_H_INCLUDED
diff --git a/zen/fixed_list.h b/zen/fixed_list.h
index 63ef3f2f..627584df 100644
--- a/zen/fixed_list.h
+++ b/zen/fixed_list.h
@@ -11,22 +11,15 @@
namespace zen
{
-//std::list(C++11) compatible class for inplace element construction supporting non-copyable/movable types
+//std::list(C++11)-like class for inplace element construction supporting non-copyable/movable types
//may be replaced by C++11 std::list when available...or never...
template <class T>
class FixedList
{
struct Node
{
- Node() : next(nullptr), val() {}
- //no variadic templates on VC2010... :(
- template <class A> Node(A&& a) : next(nullptr), val(std::forward<A>(a)) {}
- template <class A, class B> Node(A&& a, B&& b) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b)) {}
- template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c)) {}
- template <class A, class B, class C, class D> Node(A&& a, B&& b, C&& c, D&& d) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d)) {}
- template <class A, class B, class C, class D, class E> Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e)) {}
- template <class A, class B, class C, class D, class E, class F> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f)) {}
- template <class A, class B, class C, class D, class E, class F, class G> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f), std::forward<G>(g)) {}
+ template <class... Args>
+ Node(Args&& ... args) : next(nullptr), val(std::forward<Args>(args)...) {}
Node* next; //singly linked list is sufficient
T val;
@@ -48,20 +41,20 @@ public:
ListIterator& operator++() { iter = iter->next; return *this; }
inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.iter == rhs.iter; }
inline friend bool operator!=(const ListIterator& lhs, const ListIterator& rhs) { return !(lhs == rhs); }
- U& operator* () { return iter->val; }
- U* operator->() { return &iter->val; }
+ U& operator* () const { return iter->val; }
+ U* operator->() const { return &iter->val; }
private:
NodeT* iter;
};
typedef T value_type;
- typedef ListIterator<Node, T> iterator;
+ typedef ListIterator< Node, T> iterator;
typedef ListIterator<const Node, const T> const_iterator;
- typedef T& reference;
+ typedef T& reference;
typedef const T& const_reference;
iterator begin() { return firstInsert; }
- iterator end() { return iterator(); }
+ iterator end () { return iterator(); }
const_iterator begin() const { return firstInsert; }
const_iterator end () const { return const_iterator(); }
@@ -75,14 +68,8 @@ public:
reference& back() { return lastInsert->val; }
const_reference& back() const { return lastInsert->val; }
- void emplace_back() { pushNode(new Node); }
- template <class A> void emplace_back(A&& a) { pushNode(new Node(std::forward<A>(a))); }
- template <class A, class B> void emplace_back(A&& a, B&& b) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b))); }
- template <class A, class B, class C> void emplace_back(A&& a, B&& b, C&& c) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c))); }
- template <class A, class B, class C, class D> void emplace_back(A&& a, B&& b, C&& c, D&& d) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d))); }
- template <class A, class B, class C, class D, class E> void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e))); }
- template <class A, class B, class C, class D, class E, class F> void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f))); }
- template <class A, class B, class C, class D, class E, class F, class G> void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f), std::forward<G>(g))); }
+ template <class... Args>
+ void emplace_back(Args&& ... args) { pushNode(new Node(std::forward<Args>(args)...)); }
template <class Predicate>
void remove_if(Predicate pred)
@@ -126,6 +113,7 @@ public:
}
bool empty() const { return firstInsert == nullptr; }
+
size_t size() const { return sz; }
private:
diff --git a/zen/format_unit.h b/zen/format_unit.h
index 237b9f04..9416e3e5 100644
--- a/zen/format_unit.h
+++ b/zen/format_unit.h
@@ -44,7 +44,7 @@ std::wstring includeNumberSeparator(const std::wstring& number);
template <class NumberType> inline
std::wstring toGuiString(NumberType number)
{
- assert_static(IsInteger<NumberType>::value);
+ static_assert(IsInteger<NumberType>::value, "");
return ffs_Impl::includeNumberSeparator(zen::numberTo<std::wstring>(number));
}
}
diff --git a/zen/guid.h b/zen/guid.h
index b2dd90aa..83845e5c 100644
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef GUID_H_INCLUDED
-#define GUID_H_INCLUDED
+#ifndef GUID_H_INCLUDED_80425780237502345
+#define GUID_H_INCLUDED_80425780237502345
#include <string>
#include <boost/uuid/uuid.hpp>
@@ -35,4 +35,4 @@ std::string generateGUID() //creates a 16 byte GUID
}
}
-#endif // GUID_H_INCLUDED
+#endif //GUID_H_INCLUDED_80425780237502345
diff --git a/zen/i18n.h b/zen/i18n.h
index 3790e528..bf284c7f 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -37,7 +37,7 @@ struct TranslationHandler
virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) = 0;
};
-void setTranslator(TranslationHandler* newHandler = nullptr); //takes ownership
+void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler = nullptr); //take ownership
TranslationHandler* getTranslator();
@@ -58,7 +58,10 @@ namespace implementation
inline
std::wstring translate(const std::wstring& text)
{
- return getTranslator() ? getTranslator()->translate(text) : text;
+ if (TranslationHandler* t = getTranslator())
+ return t->translate(text);
+
+ return text;
}
//translate plural forms: "%x day" "%x days"
@@ -68,13 +71,13 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural,
{
assert(contains(plural, L"%x"));
- if (getTranslator())
+ if (TranslationHandler* t = getTranslator())
{
- std::wstring translation = getTranslator()->translate(singular, plural, n);
+ std::wstring translation = t->translate(singular, plural, n);
assert(!contains(translation, L"%x"));
return translation;
}
- else
+
return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n));
}
@@ -94,7 +97,7 @@ std::unique_ptr<TranslationHandler>& globalHandler()
}
inline
-void setTranslator(TranslationHandler* newHandler) { implementation::globalHandler().reset(newHandler); } //takes ownership
+void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler) { implementation::globalHandler() = std::move(newHandler); }
inline
TranslationHandler* getTranslator() { return implementation::globalHandler().get(); }
diff --git a/zen/int64.h b/zen/int64.h
index 4183c8da..671f3372 100644
--- a/zen/int64.h
+++ b/zen/int64.h
@@ -18,29 +18,29 @@ namespace zen
{
#ifdef ZEN_WIN
inline
- std::int64_t get64BitInt(DWORD low, LONG high)
- {
- static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), "");
+std::int64_t get64BitInt(DWORD low, LONG high)
+{
+ static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), "");
- LARGE_INTEGER cvt = {};
- cvt.LowPart = low;
- cvt.HighPart = high;
- return cvt.QuadPart;
- }
+ LARGE_INTEGER cvt = {};
+ cvt.LowPart = low;
+ cvt.HighPart = high;
+ return cvt.QuadPart;
+}
std::int64_t get64BitInt(std::uint64_t low, std::int64_t high) = delete;
inline
std::uint64_t get64BitUInt(DWORD low, DWORD high)
- {
- static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), "");
+{
+ static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), "");
- ULARGE_INTEGER cvt = {};
- cvt.LowPart = low;
- cvt.HighPart = high;
- return cvt.QuadPart;
- }
+ ULARGE_INTEGER cvt = {};
+ cvt.LowPart = low;
+ cvt.HighPart = high;
+ return cvt.QuadPart;
+}
std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete;
@@ -52,15 +52,15 @@ std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete;
inline
std::int64_t filetimeToTimeT(const FILETIME& ft)
{
- return static_cast<std::int64_t>(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics!
+ return static_cast<std::int64_t>(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics!
//timeshift between ansi C time and FILETIME in seconds == 11644473600s
}
inline
FILETIME timetToFileTime(std::int64_t utcTime)
-{
- ULARGE_INTEGER cvt = {};
- cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics!
+{
+ ULARGE_INTEGER cvt = {};
+ cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics!
const FILETIME output = { cvt.LowPart, cvt.HighPart };
return output;
diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp
index 45a39c48..639264a6 100644
--- a/zen/notify_removal.cpp
+++ b/zen/notify_removal.cpp
@@ -169,7 +169,7 @@ private:
Pimpl (const Pimpl&) = delete;
Pimpl& operator=(const Pimpl&) = delete;
- virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) //throw()!
+ void onMessage(UINT message, WPARAM wParam, LPARAM lParam) override //throw()!
{
//DBT_DEVICEQUERYREMOVE example: http://msdn.microsoft.com/en-us/library/aa363427(v=VS.85).aspx
if (message == WM_DEVICECHANGE)
@@ -205,10 +205,7 @@ private:
//####################################################################################################
-NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir)
-{
- pimpl.reset(new Pimpl(*this, hDir));
-}
+NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) : pimpl(zen::make_unique<Pimpl>(*this, hDir)) {}
NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~unique_ptr() works with complete type
diff --git a/zen/notify_removal.h b/zen/notify_removal.h
index aca0912f..ba560f96 100644
--- a/zen/notify_removal.h
+++ b/zen/notify_removal.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef NOTIFY_H_INCLUDED
-#define NOTIFY_H_INCLUDED
+#ifndef NOTIFY_H_INCLUDED_257804267562
+#define NOTIFY_H_INCLUDED_257804267562
#include <memory>
#include "win.h" //includes "windows.h"
@@ -24,12 +24,11 @@ private:
//NOTE: onRemovalFinished is NOT guaranteed to execute after onRequestRemoval()! but most likely will
virtual void onRemovalFinished(HANDLE hnd, bool successful) = 0; //throw()!
- NotifyRequestDeviceRemoval(NotifyRequestDeviceRemoval&); //no copying
- void operator=(NotifyRequestDeviceRemoval&); //
+ NotifyRequestDeviceRemoval (NotifyRequestDeviceRemoval&) = delete;
+ NotifyRequestDeviceRemoval& operator=(NotifyRequestDeviceRemoval&) = delete;
class Pimpl;
std::unique_ptr<Pimpl> pimpl;
};
-
-#endif // NOTIFY_H_INCLUDED
+#endif //NOTIFY_H_INCLUDED_257804267562
diff --git a/zen/osx_string.h b/zen/osx_string.h
deleted file mode 100644
index ba83ca27..00000000
--- a/zen/osx_string.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef OSX_STRING_1873641732143214324
-#define OSX_STRING_1873641732143214324
-
-#include <CoreFoundation/CoreFoundation.h> //CFString
-#include "zstring.h"
-
-namespace osx
-{
-Zstring cfStringToZstring(const CFStringRef& cfStr);
-
-CFStringRef createCFString (const char* utf8Str); //returns nullptr on error
-CFMutableStringRef createMutableCFString(const char* utf8Str); //pass ownership! => ZEN_ON_SCOPE_EXIT(::CFRelease(str));
-
-
-
-
-
-
-
-
-
-
-
-
-
-//################# implementation #####################
-inline
-Zstring cfStringToZstring(const CFStringRef& cfStr)
-{
- if (cfStr)
- {
- //perf: try to get away cheap:
- if (const char* utf8Str = ::CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8))
- return utf8Str;
-
- CFIndex length = ::CFStringGetLength(cfStr);
- if (length > 0)
- {
- CFIndex bufferSize = ::CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
- Zstring buffer;
- buffer.resize(bufferSize);
-
- if (::CFStringGetCString(cfStr, &*buffer.begin(), bufferSize, kCFStringEncodingUTF8))
- {
- buffer.resize(zen::strLength(buffer.c_str())); //caveat: memory consumption of returned string!
- return buffer;
- }
- }
- }
- return Zstring();
-}
-
-
-inline
-CFStringRef createCFString(const char* utf8Str)
-{
- //don't bother with CFStringCreateWithBytes: it's slightly slower, despite passing length info
- return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc,
- utf8Str, //const char *cStr,
- kCFStringEncodingUTF8); //CFStringEncoding encoding
-}
-
-
-inline
-CFMutableStringRef createMutableCFString(const char* utf8Str)
-{
- if (CFMutableStringRef strRef = ::CFStringCreateMutable(NULL, 0))
- {
- ::CFStringAppendCString(strRef, utf8Str, kCFStringEncodingUTF8);
- return strRef;
- }
- return nullptr;
-}
-}
-
-#endif //OSX_STRING_1873641732143214324
diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h
deleted file mode 100644
index 07e3af3e..00000000
--- a/zen/osx_throw_exception.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef OSX_EXCEPTION_89274305834255
-#define OSX_EXCEPTION_89274305834255
-
-#import <Cocoa/Cocoa.h>
-#include "sys_error.h"
-#include "utf.h"
-
-namespace osx
-{
-//for use in Objective C implementation files only!
-void throwSysError(NSException* e); //throw SysError
-
-#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw SysError
-/*
-Example: ZEN_OSX_ASSERT(obj);
-
-Equivalent to:
- if (!obj)
- throw zen::SysError(L"Assertion failed: \"obj\".");
-*/
-
-
-
-
-
-
-//######################## implmentation ############################
-inline
-void throwSysError(NSException* e) //throw SysError
-{
- std::string msg;
- if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us!
- msg += name;
- if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding])
- {
- msg += "\n";
- msg += descr;
- }
- throw zen::SysError(zen::utfCvrtTo<std::wstring>(msg));
- /*
- e.g.
- NSInvalidArgumentException
- *** +[NSString stringWithCString:encoding:]: NULL cString
- */
-}
-}
-
-#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".");
-
-#endif //OSX_EXCEPTION_89274305834255
diff --git a/zen/privilege.cpp b/zen/privilege.cpp
deleted file mode 100644
index c2db4701..00000000
--- a/zen/privilege.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-#include "privilege.h"
-#include <map>
-//#include <mutex>
-#include "win.h" //includes "windows.h"
-#include "thread.h"
-#include "zstring.h"
-#include "scope_guard.h"
-#include "win_ver.h"
-
-using namespace zen;
-
-
-namespace
-{
-bool privilegeIsActive(const wchar_t* privilege) //throw FileError
-{
- HANDLE hToken = nullptr;
- if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
- TOKEN_QUERY, //__in DWORD DesiredAccess,
- &hToken)) //__out PHANDLE TokenHandle
- throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError());
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));
-
- LUID luid = {};
- if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName,
- privilege, //__in LPCTSTR lpName,
- &luid )) //__out PLUID lpLuid
- throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError());
-
- PRIVILEGE_SET priv = {};
- priv.PrivilegeCount = 1;
- priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
- priv.Privilege[0].Luid = luid;
- priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
-
- BOOL alreadyGranted = FALSE;
- if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken,
- &priv, //__inout PPRIVILEGE_SET RequiredPrivileges,
- &alreadyGranted)) //__out LPBOOL pfResult
- throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"PrivilegeCheck", getLastError());
-
- return alreadyGranted != FALSE;
-}
-
-
-void setPrivilege(const wchar_t* privilege, bool enable) //throw FileError
-{
- HANDLE hToken = nullptr;
- if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
- TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess,
- &hToken)) //__out PHANDLE TokenHandle
- throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError());
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));
-
- LUID luid = {};
- if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName,
- privilege, //__in LPCTSTR lpName,
- &luid )) //__out PLUID lpLuid
- throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError());
-
- TOKEN_PRIVILEGES tp = {};
- tp.PrivilegeCount = 1;
- tp.Privileges[0].Luid = luid;
- tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
-
- if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle,
- false, //__in BOOL DisableAllPrivileges,
- &tp, //__in_opt PTOKEN_PRIVILEGES NewState,
- 0, //__in DWORD BufferLength,
- nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState,
- nullptr)) //__out_opt PDWORD ReturnLength
- throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", getLastError());
-
- DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success!
- {
-#ifdef __MINGW32__ //Shobjidl.h
-#define ERROR_ELEVATION_REQUIRED 740L
-#endif
- if (vistaOrLater()) //replace this useless error code with what it *really* means!
- lastError = ERROR_ELEVATION_REQUIRED;
-
- throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", lastError);
- }
-}
-
-
-class Privileges
-{
-public:
- static Privileges& getInstance()
- {
- //meyers singleton: avoid static initialization order problem in global namespace!
- static Privileges inst;
- return inst;
- }
-
- void ensureActive(const wchar_t* privilege) //throw FileError
- {
- boost::lock_guard<boost::mutex> dummy(lockPrivileges);
-
- if (activePrivileges.find(privilege) != activePrivileges.end())
- return; //privilege already active
-
- if (privilegeIsActive(privilege)) //privilege was already active before starting this tool
- activePrivileges.insert(std::make_pair(privilege, false));
- else
- {
- setPrivilege(privilege, true);
- activePrivileges.insert(std::make_pair(privilege, true));
- }
- }
-
-private:
- Privileges() {}
- Privileges (const Privileges&) = delete;
- Privileges& operator=(const Privileges&) = delete;
-
- ~Privileges() //clean up: deactivate all privileges that have been activated by this application
- {
- for (const auto& priv : activePrivileges)
- if (priv.second)
- {
- try
- {
- setPrivilege(priv.first.c_str(), false); //throw FileError
- }
- catch (FileError&) {}
- }
- }
-
- std::map<Zstring, bool> activePrivileges; //bool: enabled by this application
-boost::mutex lockPrivileges;
-};
-
-//caveat: function scope static initialization is not thread-safe in VS 2010!
-auto& dummy = Privileges::getInstance();
-}
-
-
-void zen::activatePrivilege(const wchar_t* privilege) //throw FileError
-{
- Privileges::getInstance().ensureActive(privilege);
-}
diff --git a/zen/privilege.h b/zen/privilege.h
deleted file mode 100644
index e9b83be9..00000000
--- a/zen/privilege.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef PRIVILEGE_H_INCLUDED
-#define PRIVILEGE_H_INCLUDED
-
-#include "file_error.h"
-
-namespace zen
-{
-void activatePrivilege(const wchar_t* privilege); //throw FileError; thread-safe!!!
-}
-
-#endif // PRIVILEGE_H_INCLUDED
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 930b05ac..5b5e44d4 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -5,12 +5,11 @@
// **************************************************************************
#include "recycler.h"
-#include "file_handling.h"
+#include "file_access.h"
#ifdef ZEN_WIN
#include "thread.h"
#include "dll.h"
-#include "assert_static.h"
#include "win_ver.h"
#include "long_path_prefix.h"
#include "IFileOperation/file_op.h"
@@ -46,11 +45,10 @@ Nevertheless, let's use IFileOperation for better error reporting (including det
struct CallbackData
{
CallbackData(const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus) :
- notifyDeletionStatus_(notifyDeletionStatus),
- exceptionInUserCallback(false) {}
+ notifyDeletionStatus_(notifyDeletionStatus) {}
- const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus_; //optional!
- bool exceptionInUserCallback;
+ const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus_; //in, optional
+ std::exception_ptr exception; //out
};
@@ -65,7 +63,7 @@ bool onRecyclerCallback(const wchar_t* itempath, void* sink)
}
catch (...)
{
- cbd.exceptionInUserCallback = true; //try again outside the C call stack!
+ cbd.exception = std::current_exception();
return false;
}
return true;
@@ -85,8 +83,8 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func
if (vistaOrLater()) //new recycle bin usage: available since Vista
{
#define DEF_DLL_FUN(name) const DllFun<fileop::FunType_##name> name(fileop::getDllName(), fileop::funName_##name);
-DEF_DLL_FUN(moveToRecycleBin);
-DEF_DLL_FUN(getLastErrorMessage);
+ DEF_DLL_FUN(moveToRecycleBin);
+ DEF_DLL_FUN(getLastErrorMessage);
#undef DEF_DLL_FUN
if (!moveToRecycleBin || !getLastErrorMessage)
@@ -100,12 +98,8 @@ DEF_DLL_FUN(getLastErrorMessage);
CallbackData cbd(notifyDeletionStatus);
if (!moveToRecycleBin(&cNames[0], cNames.size(), onRecyclerCallback, &cbd))
{
- if (cbd.exceptionInUserCallback)
- {
- assert(notifyDeletionStatus);
- notifyDeletionStatus(Zstring()); //should throw again!!!
- assert(false);
- }
+ if (cbd.exception)
+ std::rethrow_exception(cbd.exception);
std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1!
if (itempaths.size() > 1)
@@ -188,7 +182,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError
#elif defined ZEN_MAC
//we cannot use FSPathMoveObjectToTrashSync directly since it follows symlinks!
- assert_static(sizeof(Zchar) == sizeof(char));
+ static_assert(sizeof(Zchar) == sizeof(char), "");
const UInt8* itempathUtf8 = reinterpret_cast<const UInt8*>(itempath.c_str());
auto throwFileError = [&](OSStatus oss)
diff --git a/zen/recycler.h b/zen/recycler.h
index e900dfa3..b406408f 100644
--- a/zen/recycler.h
+++ b/zen/recycler.h
@@ -39,7 +39,6 @@ bool recycleOrDelete(const Zstring& itempath); //throw FileError, return "true"
bool recycleBinExists(const Zstring& pathName, const std::function<void ()>& onUpdateGui); //throw FileError
void recycleOrDelete(const std::vector<Zstring>& filepaths, //throw FileError, return "true" if file/dir was actually deleted
- //may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected
const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus); //optional; currentItem may be empty
#endif
}
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index d48eb922..761c6aea 100644
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -40,8 +40,8 @@ protected:
bool isDismissed() const { return dismissed_; }
private:
- ScopeGuardBase (const ScopeGuardBase&); // = delete
- ScopeGuardBase& operator=(const ScopeGuardBase&); //
+ ScopeGuardBase (const ScopeGuardBase&) = delete;
+ ScopeGuardBase& operator=(const ScopeGuardBase&) = delete;
bool dismissed_;
};
@@ -52,7 +52,7 @@ class ScopeGuardImpl : public ScopeGuardBase
{
public:
explicit ScopeGuardImpl(const F& fun) : fun_(fun) {}
- explicit ScopeGuardImpl(F&& fun) : fun_(std::move(fun)) {}
+ explicit ScopeGuardImpl( F&& fun) : fun_(std::move(fun)) {}
ScopeGuardImpl(ScopeGuardImpl&& other) : ScopeGuardBase(std::move(other)), fun_(std::move(other.fun_)) {}
~ScopeGuardImpl()
diff --git a/zen/serialize.h b/zen/serialize.h
index 64df0329..cd427c9c 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -7,6 +7,7 @@
#ifndef SERIALIZE_H_INCLUDED_83940578357
#define SERIALIZE_H_INCLUDED_83940578357
+#include <functional>
#include <cstdint>
#include "string_base.h"
#include "file_io.h"
@@ -54,8 +55,8 @@ private:
//----------------------------------------------------------------------
//functions based on binary container abstraction
-template <class BinContainer> void saveBinStream(const Zstring& filepath, const BinContainer& cont); //throw FileError
-template <class BinContainer> BinContainer loadBinStream(const Zstring& filepath); //throw FileError
+template <class BinContainer> void saveBinStream(const Zstring& filepath, const BinContainer& cont, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); //throw FileError
+template <class BinContainer> BinContainer loadBinStream(const Zstring& filepath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); //throw FileError
/*
@@ -135,20 +136,38 @@ template < class BinInputStream> void readArray (BinInputStream& stre
//-----------------------implementation-------------------------------
template <class BinContainer> inline
-void saveBinStream(const Zstring& filepath, const BinContainer& cont) //throw FileError
+void saveBinStream(const Zstring& filepath, //throw FileError
+ const BinContainer& cont,
+ const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional
{
- assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further)
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further)
FileOutput fileOut(filepath, zen::FileOutput::ACC_OVERWRITE); //throw FileError
if (!cont.empty())
- fileOut.write(&*cont.begin(), cont.size()); //throw FileError
+ {
+ const size_t blockSize = 128 * 1024;
+ auto bytePtr = &*cont.begin();
+ size_t bytesLeft = cont.size();
+
+ while (bytesLeft > blockSize)
+ {
+ fileOut.write(bytePtr, blockSize); //throw FileError
+ bytePtr += blockSize;
+ bytesLeft -= blockSize;
+ if (onUpdateStatus) onUpdateStatus(blockSize);
+ }
+
+ fileOut.write(bytePtr, bytesLeft); //throw FileError
+ if (onUpdateStatus) onUpdateStatus(bytesLeft);
+ }
}
template <class BinContainer> inline
-BinContainer loadBinStream(const Zstring& filepath) //throw FileError
+BinContainer loadBinStream(const Zstring& filepath, //throw FileError
+ const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional
{
- assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further)
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further)
FileInput fileIn(filepath); //throw FileError
@@ -161,6 +180,8 @@ BinContainer loadBinStream(const Zstring& filepath) //throw FileError
const size_t bytesRead = fileIn.read(&*contOut.begin() + contOut.size() - blockSize, blockSize); //throw FileError
if (bytesRead < blockSize)
contOut.resize(contOut.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
+
+ if (onUpdateStatus) onUpdateStatus(bytesRead);
}
while (!fileIn.eof());
@@ -180,7 +201,7 @@ void writeArray(BinOutputStream& stream, const void* data, size_t len)
template <class N, class BinOutputStream> inline
void writeNumber(BinOutputStream& stream, const N& num)
{
- assert_static((IsArithmetic<N>::value || IsSameType<N, bool>::value));
+ static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "");
writeArray(stream, &num, sizeof(N));
}
@@ -198,7 +219,7 @@ void writeContainer(BinOutputStream& stream, const C& cont) //don't even conside
template <class BinInputStream> inline
void readArray(BinInputStream& stream, void* data, size_t len) //throw UnexpectedEndOfStreamError
{
- //expect external write of len bytes:
+ //expect external write of len bytes:
const char* const src = static_cast<const char*>(stream.requestRead(len)); //throw UnexpectedEndOfStreamError
std::copy(src, src + len, static_cast<char*>(data));
}
@@ -207,7 +228,7 @@ void readArray(BinInputStream& stream, void* data, size_t len) //throw Unexpecte
template <class N, class BinInputStream> inline
N readNumber(BinInputStream& stream) //throw UnexpectedEndOfStreamError
{
- assert_static((IsArithmetic<N>::value || IsSameType<N, bool>::value));
+ static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "");
N num = 0;
readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError
return num;
@@ -219,8 +240,8 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError
{
C cont;
auto strLength = readNumber<std::uint32_t>(stream);
- if (strLength > 0)
- {
+ if (strLength > 0)
+ {
try
{
cont.resize(strLength); //throw std::bad_alloc
@@ -229,8 +250,8 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError
{
throw UnexpectedEndOfStreamError();
}
- readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError
- }
+ readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError
+ }
return cont;
}
}
diff --git a/zen/shell_execute.h b/zen/shell_execute.h
index 9f99315a..c2ccd837 100644
--- a/zen/shell_execute.h
+++ b/zen/shell_execute.h
@@ -31,7 +31,7 @@ enum ExecutionType
namespace
{
-void shellExecute2(const Zstring& command, ExecutionType type) //throw FileError
+void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
{
#ifdef ZEN_WIN
//parse commandline
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 38752e67..eb94b4a1 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -9,13 +9,6 @@
#include <memory>
#include <algorithm>
-#if defined _MSC_VER && _MSC_VER <= 1600
-#include <set>
-#include <map>
-#else
-#include <unordered_set>
-#include <unordered_map>
-#endif
//enhancements for <algorithm>
@@ -38,8 +31,8 @@ template <class M, class K, class V>
V& map_add_or_update(M& map, const K& key, const V& value); //efficient add or update without "default-constructible" requirement (Effective STL, item 24)
//binary search returning an iterator
-template <class ForwardIterator, class T, typename Compare>
-ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp);
+template <class ForwardIterator, class T, typename CompLess>
+ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less);
template <class BidirectionalIterator, class T>
BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value);
@@ -53,26 +46,9 @@ template <class InputIterator1, class InputIterator2>
bool equal(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2);
-//hash container: proper name + mitigate MSVC performance bug
-template <class T> class hash_set;
-template <class K, class V> class hash_map;
-
-template<typename T, typename Arg1>
-std::unique_ptr<T> make_unique(Arg1&& arg1); //should eventually make it into the std at some time
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+//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)...)); }
@@ -127,11 +103,11 @@ V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or up
}
-template <class ForwardIterator, class T, typename Compare> inline
-ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp)
+template <class ForwardIterator, class T, typename CompLess> inline
+ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less)
{
- first = std::lower_bound(first, last, value, comp);
- if (first != last && !comp(value, *first))
+ first = std::lower_bound(first, last, value, less);
+ if (first != last && !less(value, *first))
return first;
else
return last;
@@ -187,32 +163,10 @@ bool equal(InputIterator1 first1, InputIterator1 last1,
}
-#if defined _MSC_VER && _MSC_VER <= 1600 //VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11
-template <class T> class hash_set : public std::set<T> {};
-template <class K, class V> class hash_map : public std::map<K, V> {};
-#else
-template <class T> class hash_set : public std::unordered_set<T> {};
-template <class K, class V> class hash_map : public std::unordered_map<K, V> {};
-//C++11:
-//template <class T> using hash_set = std::unordered_set<T>;
-//template <class K, class V> using hash_map = std::unordered_map<K, V>;
+#if defined _MSC_VER && _MSC_VER <= 1600
+//VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11
+static_assert(false, "");
#endif
-
-//as long as variadic templates are not available in MSVC
-template<class T> inline std::unique_ptr<T> make_unique() { return std::unique_ptr<T>(new T); }
-template<class T, class Arg1> inline std::unique_ptr<T> make_unique(Arg1&& arg1) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1))); }
-template<class T, class Arg1, class Arg2> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))); }
-template<class T, class Arg1, class Arg2, class Arg3> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3))); }
-template<class T, class Arg1, class Arg2, class Arg3, class Arg4> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4))); }
-template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4), std::forward<Arg5>(arg5))); }
-template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4), std::forward<Arg5>(arg5), std::forward<Arg6>(arg6))); }
-template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6, class Arg7> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6, Arg7&& arg7) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4), std::forward<Arg5>(arg5), std::forward<Arg6>(arg6), std::forward<Arg7>(arg7))); }
-
-//template<typename T, typename ...Args> inline
-//std::unique_ptr<T> make_unique(Args&& ...args)
-//{
-// return std::unique_ptr<T>(new T( std::forward<Args>(args)... ));
-//}
}
#endif //STL_TOOLS_HEADER_84567184321434
diff --git a/zen/string_base.h b/zen/string_base.h
index a458ef15..0f9ad479 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -21,7 +21,7 @@ namespace zen
Allocator Policy:
-----------------
void* allocate(size_t size) //throw std::bad_alloc
- void deallocate(void* ptr) //must handle deallocate(nullptr)!
+ void deallocate(void* ptr)
size_t calcCapacity(size_t length)
*/
class AllocatorOptimalSpeed //exponential growth + min size
@@ -53,7 +53,7 @@ template <typename Char, //Character Type
Char* create(size_t size)
Char* create(size_t size, size_t minCapacity)
Char* clone(Char* ptr)
- void destroy(Char* ptr) //must handle destroy(nullptr)!
+ void destroy(Char* ptr) //must handle "destroy(nullptr)"!
bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
size_t length(const Char* ptr)
void setLength(Char* ptr, size_t newLength)
@@ -66,29 +66,34 @@ class StorageDeepCopy : public AP
protected:
~StorageDeepCopy() {}
- static Char* create(size_t size) { return create(size, size); }
- static Char* create(size_t size, size_t minCapacity)
+ Char* create(size_t size) { return create(size, size); }
+ Char* create(size_t size, size_t minCapacity)
{
assert(size <= minCapacity);
const size_t newCapacity = AP::calcCapacity(minCapacity);
assert(newCapacity >= minCapacity);
- Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char)));
-
- newDescr->length = size;
- newDescr->capacity = newCapacity;
+ Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc
+ new (newDescr) Descriptor(size, newCapacity);
return reinterpret_cast<Char*>(newDescr + 1); //alignment note: "newDescr + 1" is Descriptor-aligned, which is larger than alignment for Char-array! => no problem!
}
- static Char* clone(Char* ptr)
+ Char* clone(Char* ptr)
{
- Char* newData = create(length(ptr));
+ Char* newData = create(length(ptr)); //throw std::bad_alloc
std::copy(ptr, ptr + length(ptr) + 1, newData);
return newData;
}
- static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); } //should support destroy(nullptr)!
+ void destroy(Char* ptr)
+ {
+ if (!ptr) return; //support "destroy(nullptr)"
+
+ Descriptor* const d = descr(ptr);
+ d->~Descriptor();
+ this->deallocate(d);
+ }
//this needs to be checked before writing to "ptr"
static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; }
@@ -103,6 +108,10 @@ protected:
private:
struct Descriptor
{
+ Descriptor(size_t len, size_t cap) :
+ length (static_cast<std::uint32_t>(len)),
+ capacity(static_cast<std::uint32_t>(cap)) {}
+
std::uint32_t length;
std::uint32_t capacity; //allocated size without null-termination
};
@@ -119,42 +128,44 @@ class StorageRefCountThreadSafe : public AP
protected:
~StorageRefCountThreadSafe() {}
- static Char* create(size_t size) { return create(size, size); }
- static Char* create(size_t size, size_t minCapacity)
+ Char* create(size_t size) { return create(size, size); }
+ Char* create(size_t size, size_t minCapacity)
{
assert(size <= minCapacity);
const size_t newCapacity = AP::calcCapacity(minCapacity);
assert(newCapacity >= minCapacity);
- Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char)));
- new (newDescr) Descriptor(1, size, newCapacity);
+ Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc
+ new (newDescr) Descriptor(size, newCapacity);
return reinterpret_cast<Char*>(newDescr + 1);
}
static Char* clone(Char* ptr)
{
- assert(descr(ptr)->refCount > 0);
++descr(ptr)->refCount;
return ptr;
}
- static void destroy(Char* ptr)
+ void destroy(Char* ptr)
{
- if (!ptr) return; //support destroy(nullptr)
- assert(descr(ptr)->refCount > 0);
- if (--descr(ptr)->refCount == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation!
+ if (!ptr) return; //support "destroy(nullptr)"
+
+ Descriptor* const d = descr(ptr);
+
+ if (--(d->refCount) == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation!
{
- descr(ptr)->~Descriptor();
- AP::deallocate(descr(ptr));
+ d->~Descriptor();
+ this->deallocate(d);
}
}
static bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
{
- assert(descr(ptr)->refCount > 0);
- return descr(ptr)->refCount == 1 && minCapacity <= descr(ptr)->capacity;
+ const Descriptor* const d = descr(ptr);
+ assert(d->refCount > 0);
+ return d->refCount == 1 && minCapacity <= d->capacity;
}
static size_t length(const Char* ptr) { return descr(ptr)->length; }
@@ -168,14 +179,14 @@ protected:
private:
struct Descriptor
{
- Descriptor(int rc, size_t len, size_t cap) :
+ Descriptor(size_t len, size_t cap) :
+ refCount(1),
length (static_cast<std::uint32_t>(len)),
- capacity(static_cast<std::uint32_t>(cap)),
- refCount(rc) { assert_static(ATOMIC_INT_LOCK_FREE == 2); } //2: "the types are always lock-free"
+ 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;
std::uint32_t length;
std::uint32_t capacity; //allocated size without null-termination
- std::atomic<int> refCount; //practically no perf loss: ~0.2%! (FFS comparison)
};
static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
@@ -237,7 +248,7 @@ public:
Zbase& assign(const Char* source, size_t len);
Zbase& append(const Char* source, size_t len);
void resize(size_t newSize, Char fillChar = 0);
- void swap(Zbase& other);
+ void swap(Zbase& other); //make noexcept in C++11
void push_back(Char val) { operator+=(val); } //STL access
Zbase& operator=(const Zbase& source);
@@ -251,10 +262,10 @@ public:
static const size_t npos = static_cast<size_t>(-1);
private:
- Zbase(int); //
- Zbase& operator=(int); //detect usage errors by creating an intentional ambiguity with "Char"
- Zbase& operator+=(int); //
- void push_back(int); //
+ Zbase (int) = delete; //
+ Zbase& operator= (int) = delete; //detect usage errors by creating an intentional ambiguity with "Char"
+ Zbase& operator+=(int) = delete; //
+ void push_back (int) = delete; //
Char* rawStr;
};
@@ -626,8 +637,7 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(const Zbase<Char, SP, AP>& o
template <class Char, template <class, class> class SP, class AP> inline
Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Zbase<Char, SP, AP>&& tmp)
{
- //don't use unifying assignment but save one move-construction in the r-value case instead!
- swap(tmp);
+ swap(tmp); //don't use unifying assignment but save one move-construction in the r-value case instead!
return *this;
}
diff --git a/zen/string_tools.h b/zen/string_tools.h
index a0b02d14..addf2bd5 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -82,7 +82,7 @@ bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; }
template <class Char> inline
bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()!
{
- assert_static((IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value));
+ static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9');
}
@@ -90,7 +90,7 @@ bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()!
template <class S, class T> inline
bool startsWith(const S& str, const T& prefix)
{
- assert_static(IsStringLike<S>::value && IsStringLike<T>::value);
+ static_assert(IsStringLike<S>::value && IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
const size_t pfLength = strLength(prefix);
@@ -106,7 +106,7 @@ bool startsWith(const S& str, const T& prefix)
template <class S, class T> inline
bool endsWith(const S& str, const T& postfix)
{
- assert_static(IsStringLike<S>::value && IsStringLike<T>::value);
+ static_assert(IsStringLike<S>::value && IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
const size_t strLen = strLength(str);
@@ -123,7 +123,7 @@ bool endsWith(const S& str, const T& postfix)
template <class S, class T> inline
bool contains(const S& str, const T& term)
{
- assert_static(IsStringLike<S>::value && IsStringLike<T>::value);
+ static_assert(IsStringLike<S>::value && IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
const size_t strLen = strLength(str);
@@ -144,7 +144,7 @@ bool contains(const S& str, const T& term)
template <class S, class T> inline
S afterLast(const S& str, const T& term)
{
- assert_static(IsStringLike<T>::value);
+ static_assert(IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
const size_t termLen = strLength(term);
@@ -167,7 +167,7 @@ S afterLast(const S& str, const T& term)
template <class S, class T> inline
S beforeLast(const S& str, const T& term)
{
- assert_static(IsStringLike<T>::value);
+ static_assert(IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
const CharType* const strFirst = strBegin(str);
@@ -187,7 +187,7 @@ S beforeLast(const S& str, const T& term)
template <class S, class T> inline
S afterFirst(const S& str, const T& term)
{
- assert_static(IsStringLike<T>::value);
+ static_assert(IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
const size_t termLen = strLength(term);
@@ -209,7 +209,7 @@ S afterFirst(const S& str, const T& term)
template <class S, class T> inline
S beforeFirst(const S& str, const T& term)
{
- assert_static(IsStringLike<T>::value);
+ static_assert(IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
const CharType* const strFirst = strBegin(str);
@@ -223,7 +223,7 @@ S beforeFirst(const S& str, const T& term)
template <class S, class T> inline
std::vector<S> split(const S& str, const T& delimiter)
{
- assert_static(IsStringLike<T>::value);
+ static_assert(IsStringLike<T>::value, "");
typedef typename GetCharType<S>::Type CharType;
std::vector<S> output;
@@ -245,7 +245,7 @@ std::vector<S> split(const S& str, const T& delimiter)
const CharType* const blockEnd = std::search(blockStart, strLast,
delimFirst, delimLast);
- output.push_back(S(blockStart, blockEnd - blockStart));
+ output.emplace_back(blockStart, blockEnd - blockStart);
if (blockEnd == strLast)
break;
blockStart = blockEnd + delimLen;
@@ -271,7 +271,7 @@ typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, const
template <class S, class T, class U> inline
S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
{
- assert_static(IsStringLike<T>::value && IsStringLike<U>::value);
+ static_assert(IsStringLike<T>::value && IsStringLike<U>::value, "");
typedef typename GetCharType<S>::Type CharType;
const size_t oldLen = strLength(oldTerm);
@@ -395,10 +395,10 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const
template <class S, class T, class Num> inline
S printNumber(const T& format, const Num& number) //format a single number using ::sprintf
{
- assert_static(IsStringLike<T>::value);
- assert_static((IsSameType<
- typename GetCharType<S>::Type,
- typename GetCharType<T>::Type>::value));
+ static_assert(IsStringLike<T>::value, "");
+ static_assert(IsSameType<
+ typename GetCharType<S>::Type,
+ typename GetCharType<T>::Type>::value, "");
typedef typename GetCharType<S>::Type CharType;
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 69c1fbc8..ba97397c 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -8,8 +8,6 @@
#define STRING_TRAITS_HEADER_813274321443234
#include "type_tools.h"
-#include "assert_static.h"
-
//uniform access to string-like types, both classes and character arrays
namespace zen
@@ -165,7 +163,11 @@ namespace implementation
template <class C> inline
size_t cStringLength(const C* str) //naive implementation seems somewhat faster than "optimized" strlen/wcslen!
{
- assert_static((IsSameType<C, char>::value || IsSameType<C, wchar_t>::value));
+#if defined _MSC_VER && _MSC_VER > 1800
+ static_assert(false, "strlen/wcslen are vectorized in VS14 CTP3 -> test again!");
+#endif
+
+ static_assert(IsSameType<C, char>::value || IsSameType<C, wchar_t>::value, "");
size_t len = 0;
while (*str++ != 0)
++len;
@@ -184,8 +186,8 @@ inline const char* strBegin(const char* str) { return str; }
inline const wchar_t* strBegin(const wchar_t* str) { return str; }
inline const char* strBegin(const char& ch) { return &ch; }
inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; }
-inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); }
-inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); }
+inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); }
+inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); }
template <class S> inline
diff --git a/zen/sys_error.h b/zen/sys_error.h
index 8afab138..9cfc762f 100644
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -35,7 +35,7 @@ typedef int ErrorCode;
ErrorCode getLastError();
std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError);
-
+std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError, const std::wstring& lastErrorMsg);
//A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"!
class SysError
@@ -75,11 +75,7 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE
{
const ErrorCode currentError = getLastError(); //not necessarily == lastError
- //determine error code if none was specified
- if (lastError == 0)
- lastError = currentError;
-
- std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo<std::wstring>(lastError));
+ std::wstring lastErrorMsg;
#ifdef ZEN_WIN
ZEN_ON_SCOPE_EXIT(::SetLastError(currentError)); //this function must not change active system error variable!
@@ -92,22 +88,37 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE
if (buffer) //"don't trust nobody"
{
ZEN_ON_SCOPE_EXIT(::LocalFree(buffer));
- output += L" ";
- output += buffer;
+ lastErrorMsg = buffer;
}
#elif defined ZEN_LINUX || defined ZEN_MAC
ZEN_ON_SCOPE_EXIT(errno = currentError);
- output += L" ";
- output += utfCvrtTo<std::wstring>(::strerror(lastError));
+ lastErrorMsg = utfCvrtTo<std::wstring>(::strerror(lastError));
#endif
+
+ return formatSystemError(functionName, lastError, lastErrorMsg);
+}
+
+
+inline
+std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError, const std::wstring& lastErrorMsg)
+{
+ std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo<std::wstring>(lastError));
+
+ if (!lastErrorMsg.empty())
+ {
+ output += L" ";
+ output += lastErrorMsg;
+ }
+
if (!endsWith(output, L" ")) //Windows messages seem to end with a blank...
output += L" ";
output += L"(" + functionName + L")";
return output;
}
+
}
#endif //SYS_ERROR_H_3284791347018951324534
diff --git a/zen/thread.h b/zen/thread.h
index 8c72e43f..a834f070 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -88,13 +88,15 @@ private:
#error just some paranoia check...
#endif
-template <class T, class Function> inline
-auto async2(Function fun) -> boost::unique_future<T> //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int!
+template <class Function> inline
+auto async(Function fun) -> boost::unique_future<decltype(fun())>
{
+ typedef decltype(fun()) ResultType;
+
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK //mirror "boost/thread/future.hpp", hopefully they know what they're doing
- boost::packaged_task<T()> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/
+ boost::packaged_task<ResultType()> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/
#else
- boost::packaged_task<T> pt(std::move(fun));
+ boost::packaged_task<ResultType> pt(std::move(fun));
#endif
auto fut = pt.get_future();
boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
@@ -102,10 +104,6 @@ auto async2(Function fun) -> boost::unique_future<T> //support for workaround of
}
-template <class Function> inline
-auto async(Function fun) -> boost::unique_future<decltype(fun())> { return async2<decltype(fun())>(fun); }
-
-
template<class InputIterator, class Duration> inline
bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration)
{
diff --git a/zen/tick_count.h b/zen/tick_count.h
index 482689d7..56179b62 100644
--- a/zen/tick_count.h
+++ b/zen/tick_count.h
@@ -64,9 +64,9 @@ public:
#ifdef ZEN_WIN
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
+ //structure timespec documented with members:
+ // 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);
@@ -113,9 +113,9 @@ int64_t ticksPerSec() //return 0 on error
mach_timebase_info_data_t tbi = {};
if (::mach_timebase_info(&tbi) != KERN_SUCCESS)
return 0;
-//structure mach_timebase_info_data_t documented with members:
-// uint32_t numer;
-// uint32_t denom;
+ //structure mach_timebase_info_data_t documented with members:
+ // uint32_t numer;
+ // uint32_t denom;
return static_cast<int64_t>(1000000000) * tbi.denom / tbi.numer;
#endif
}
@@ -126,10 +126,10 @@ TickVal getTicks() //return !isValid() on error
{
#ifdef ZEN_WIN
LARGE_INTEGER now = {};
- if (!::QueryPerformanceCounter(&now))
+ if (!::QueryPerformanceCounter(&now))
return TickVal();
- //detailed info about QPC: http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx
- //- MSDN: "No need to set the thread affinity"
+ //detailed info about QPC: http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx
+ //- MSDN: "No need to set the thread affinity"
#elif defined ZEN_LINUX
//gettimeofday() seems fine but is deprecated
diff --git a/zen/time.h b/zen/time.h
index e94300a0..e53e4c6e 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -49,8 +49,8 @@ const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M
//----------------------------------------------------------------------------------------------------------------------------------
-template <class String>
-bool parseTime(const String& format, const String& str, TimeComp& comp); //similar to ::strptime(), return true on success
+template <class String, class String2>
+bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success
//----------------------------------------------------------------------------------------------------------------------------------
@@ -272,10 +272,11 @@ String formatTime(const String2& format, const TimeComp& comp)
}
-template <class String>
-bool parseTime(const String& format, const String& str, TimeComp& comp) //return true on success
+template <class String, class String2>
+bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success
{
typedef typename GetCharType<String>::Type CharType;
+ static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, "");
const CharType* iterFmt = strBegin(format);
const CharType* const fmtLast = iterFmt + strLength(format);
@@ -285,7 +286,7 @@ bool parseTime(const String& format, const String& str, TimeComp& comp) //return
auto extractNumber = [&](int& result, size_t digitCount) -> bool
{
- if (strLast - iterStr < digitCount)
+ if (strLast - iterStr < makeSigned(digitCount))
return false;
if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); }))
diff --git a/zen/utf.h b/zen/utf.h
index 281185d3..e742c1e2 100644
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -104,7 +104,7 @@ size_t getUtf16Len(Char16 ch) //ch must be first code unit! returns 0 on error!
template <class CharIterator, class Function> inline
void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint
{
- assert_static(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 2);
+ static_assert(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 2, "");
for ( ; first != last; ++first)
{
@@ -201,7 +201,7 @@ bool decodeTrail(CharIterator& first, CharIterator last, CodePoint& cp) //decode
template <class CharIterator, class Function> inline
void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint
{
- assert_static(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 1);
+ static_assert(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 1, "");
for ( ; first != last; ++first)
{
@@ -337,12 +337,12 @@ size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>)
if (utfPos >= strLen)
return strLen;
- size_t utf16len = getUtf16Len(strFirst[utfPos]);
+ size_t utf16len = getUtf16Len(strFirst[utfPos]);
if (utf16len == 0) ++utf16len; //invalid utf16 character
utfPos += utf16len;
}
- if (utfPos >= strLen)
- return strLen;
+ if (utfPos >= strLen)
+ return strLen;
return utfPos;
}
@@ -416,8 +416,8 @@ CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert ut
template <class WideString, class CharString> inline
WideString utf8ToWide(const CharString& str)
{
- assert_static((IsSameType<typename GetCharType<CharString>::Type, char >::value));
- assert_static((IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value));
+ static_assert(IsSameType<typename GetCharType<CharString>::Type, char >::value, "");
+ static_assert(IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value, "");
return implementation::utf8ToWide<WideString>(str, Int2Type<sizeof(wchar_t)>());
}
@@ -426,8 +426,8 @@ WideString utf8ToWide(const CharString& str)
template <class CharString, class WideString> inline
CharString wideToUtf8(const WideString& str)
{
- assert_static((IsSameType<typename GetCharType<CharString>::Type, char >::value));
- assert_static((IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value));
+ static_assert(IsSameType<typename GetCharType<CharString>::Type, char >::value, "");
+ static_assert(IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value, "");
return implementation::wideToUtf8<CharString>(str, Int2Type<sizeof(wchar_t)>());
}
diff --git a/zen/win_ver.h b/zen/win_ver.h
index 0d3f8d70..1d7ce7f0 100644
--- a/zen/win_ver.h
+++ b/zen/win_ver.h
@@ -7,24 +7,26 @@
#ifndef WINDOWS_VERSION_HEADER_238470348254325
#define WINDOWS_VERSION_HEADER_238470348254325
+#include <cassert>
#include <utility>
#include "win.h" //includes "windows.h"
namespace zen
{
- struct OsVersion
- {
- OsVersion() : major(), minor() {}
- OsVersion(DWORD high, DWORD low) : major(high), minor(low) {}
+struct OsVersion
+{
+ OsVersion() : major(), minor() {}
+ OsVersion(DWORD high, DWORD low) : major(high), minor(low) {}
- DWORD major;
- DWORD minor;
- };
- inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; }
- inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; }
+ DWORD major;
+ DWORD minor;
+};
+inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; }
+inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; }
//version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
+const OsVersion osVersionWin10 (6, 4);
const OsVersion osVersionWin81 (6, 3);
const OsVersion osVersionWin8 (6, 2);
const OsVersion osVersionWin7 (6, 1);
@@ -61,11 +63,18 @@ OsVersion getOsVersion()
{
OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(osvi);
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) //"'GetVersionExW': was declared deprecated"
+#endif
if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread-safe statics right now...
- {
- assert(false);
- return OsVersion();
- }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ {
+ assert(false);
+ return OsVersion();
+ }
return OsVersion(osvi.dwMajorVersion, osvi.dwMinorVersion);
}
@@ -78,16 +87,16 @@ bool isRealOsVersion(const OsVersion& ver)
verInfo.dwMajorVersion = ver.major;
verInfo.dwMinorVersion = ver.minor;
- //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx
+ //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx
DWORDLONG conditionMask = 0;
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_EQUAL);
const bool rv = ::VerifyVersionInfo(&verInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask)
- == TRUE; //silence VC "performance warnings"
- assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION);
+ == TRUE; //silence VC "performance warnings"
+ assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION);
- return rv;
+ return rv;
}
}
diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp
index 485d78bb..9ac4b87f 100644
--- a/zen/xml_io.cpp
+++ b/zen/xml_io.cpp
@@ -5,7 +5,7 @@
// **************************************************************************
#include "xml_io.h"
-#include "file_handling.h"
+#include "file_access.h"
#include "file_io.h"
#include "serialize.h"
@@ -66,7 +66,7 @@ void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filepath) //throw Fi
try
{
if (getFilesize(filepath) == stream.size()) //throw FileError
- if (loadBinStream<std::string>(filepath) == stream) //throw FileError
+ if (loadBinStream<std::string>(filepath, nullptr) == stream) //throw FileError
return;
}
catch (FileError&) {}
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index f16af6a0..a2a26f83 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -6,6 +6,7 @@
#include "zstring.h"
#include <stdexcept>
+#include <unordered_map>
#ifdef ZEN_WIN
#include "dll.h"
@@ -40,7 +41,7 @@ public:
void insert(const void* ptr, size_t size)
{
boost::lock_guard<boost::mutex> dummy(lockActStrings);
- if (!activeStrings.insert(std::make_pair(ptr, size)).second)
+ if (!activeStrings.emplace(ptr, size).second)
reportProblem("Serious Error: New memory points into occupied space: " + rawMemToString(ptr, size));
}
@@ -96,7 +97,7 @@ private:
}
boost::mutex lockActStrings;
- zen::hash_map<const void*, size_t> activeStrings;
+ std::unordered_map<const void*, size_t> activeStrings;
};
//caveat: function scope static initialization is not thread-safe in VS 2010!
@@ -148,15 +149,15 @@ const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<Compa
}
-int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_t sizeLhs, size_t sizeRhs)
+int cmpFileName(const Zstring& lhs, const Zstring& rhs)
{
if (compareStringOrdinal) //this additional test has no noticeable performance impact
{
- const int rv = compareStringOrdinal(lhs, //__in LPCWSTR lpString1,
- static_cast<int>(sizeLhs), //__in int cchCount1,
- rhs, //__in LPCWSTR lpString2,
- static_cast<int>(sizeRhs), //__in int cchCount2,
- true); //__in BOOL bIgnoreCase
+ const int rv = compareStringOrdinal(lhs.c_str(), //__in LPCWSTR lpString1,
+ static_cast<int>(lhs.size()), //__in int cchCount1,
+ rhs.c_str(), //__in LPCWSTR lpString2,
+ static_cast<int>(rhs.size()), //__in int cchCount2,
+ true); //__in BOOL bIgnoreCase
if (rv <= 0)
throw std::runtime_error("Error comparing strings (CompareStringOrdinal).");
else
@@ -167,31 +168,35 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_
//do NOT use "CompareString"; this function is NOT accurate (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!!
//the only reliable way to compare filepaths (with XP) is to call "CharUpper" or "LCMapString":
- auto copyToUpperCase = [](const wchar_t* strIn, wchar_t* strOut, size_t len)
+ const size_t sizeLhs = lhs.size();
+ const size_t sizeRhs = rhs.size();
+
+ const auto minSize = std::min(sizeLhs, sizeRhs);
+
+ if (minSize == 0) //LCMapString does not allow input sizes of 0!
+ return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs);
+
+ auto copyToUpperCase = [&](const wchar_t* strIn, wchar_t* strOut)
{
//faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
- LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
- strIn, //__in LPCTSTR lpSrcStr,
- static_cast<int>(len), //__in int cchSrc,
- strOut, //__out LPTSTR lpDestStr,
- static_cast<int>(len)) == 0) //__in int cchDest
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
+ LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
+ strIn, //__in LPCTSTR lpSrcStr,
+ static_cast<int>(minSize), //__in int cchSrc,
+ strOut, //__out LPTSTR lpDestStr,
+ static_cast<int>(minSize)) == 0) //__in int cchDest
throw std::runtime_error("Error comparing strings (LCMapString).");
};
- const auto minSize = std::min(sizeLhs, sizeRhs);
-
auto eval = [&](wchar_t* bufL, wchar_t* bufR)
{
- if (minSize > 0) //LCMapString does not allow input sizes of 0!
- {
- copyToUpperCase(lhs, bufL, minSize);
- copyToUpperCase(rhs, bufR, minSize);
-
- const int rv = ::wmemcmp(bufL, bufR, minSize);
- if (rv != 0)
- return rv;
- }
+ copyToUpperCase(lhs.c_str(), bufL);
+ copyToUpperCase(rhs.c_str(), bufR);
+
+ const int rv = ::wmemcmp(bufL, bufR, minSize);
+ if (rv != 0)
+ return rv;
+
return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs);
};
@@ -203,35 +208,55 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_
}
else //use freestore
{
- std::vector<wchar_t> bufferL(minSize);
- std::vector<wchar_t> bufferR(minSize);
- return eval(&bufferL[0], &bufferR[0]);
+ std::vector<wchar_t> buffer(2 * minSize);
+ return eval(&buffer[0], &buffer[minSize]);
}
}
}
-void z_impl::makeFilenameUpperCase(wchar_t* str, size_t size)
+Zstring makeUpperCopy(const Zstring& str)
{
- if (size == 0) //LCMapString does not allow input sizes of 0!
- return;
+ const int len = static_cast<int>(str.size());
+
+ if (len == 0) //LCMapString does not allow input sizes of 0!
+ return str;
+
+ Zstring output;
+ output.resize(len);
//use Windows' upper case conversion: faster than ::CharUpper()
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, str, static_cast<int>(size), str, static_cast<int>(size)) == 0)
- throw std::runtime_error("Error converting to upper case! (LCMapString)");
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
+ LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
+ str.c_str(), //__in LPCTSTR lpSrcStr,
+ len, //__in int cchSrc,
+ &*output.begin(), //__out LPTSTR lpDestStr,
+ len) == 0) //__in int cchDest
+ throw std::runtime_error("Error comparing strings (LCMapString).");
+
+ return output;
}
+
#elif defined ZEN_MAC
-int z_impl::compareFilenamesNoCase(const char* lhs, const char* rhs, size_t sizeLhs, size_t sizeRhs)
+int cmpFileName(const Zstring& lhs, const Zstring& rhs)
{
- return ::strcasecmp(lhs, rhs); //locale-dependent!
+ return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent!
}
-void z_impl::makeFilenameUpperCase(char* str, size_t size)
+Zstring makeUpperCopy(const Zstring& str)
{
- std::for_each(str, str + size, [](char& c) { c = static_cast<char>(::toupper(static_cast<unsigned char>(c))); }); //locale-dependent!
+ const size_t len = str.size();
+
+ Zstring output;
+ output.resize(len);
+
+ std::transform(str.begin(), str.end(), output.begin(), [](char c) { return static_cast<char>(::toupper(static_cast<unsigned char>(c))); }); //locale-dependent!
+
//result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness!
//this should work for UTF-8, too: all chars >= 128 are mapped upon themselves!
+
+ return output;
}
#endif
diff --git a/zen/zstring.h b/zen/zstring.h
index 2152954d..94144386 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef ZSTRING_H_INCLUDED
-#define ZSTRING_H_INCLUDED
+#ifndef ZSTRING_H_INCLUDED_73425873425789
+#define ZSTRING_H_INCLUDED_73425873425789
#include "string_base.h"
@@ -66,30 +66,26 @@ typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, AllocatorFreeStoreChec
//Compare filepaths: Windows does NOT distinguish between upper/lower-case, while Linux DOES
-template <template <class, class> class SP, class AP>
-int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs);
+int cmpFileName(const Zstring& lhs, const Zstring& rhs);
struct LessFilename //case-insensitive on Windows, case-sensitive on Linux
{
- template <template <class, class> class SP, class AP>
- bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) < 0; }
+ bool operator()(const Zstring& lhs, const Zstring& rhs) const { return cmpFileName(lhs, rhs) < 0; }
};
struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux
{
- template <template <class, class> class SP, class AP>
- bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) == 0; }
+ bool operator()(const Zstring& lhs, const Zstring& rhs) const { return cmpFileName(lhs, rhs) == 0; }
};
#if defined ZEN_WIN || defined ZEN_MAC
-template <template <class, class> class SP, class AP>
-void makeUpper(zen::Zbase<Zchar, SP, AP>& str);
+Zstring makeUpperCopy(const Zstring& str);
#endif
inline
Zstring appendSeparator(Zstring path) //support rvalue references!
{
- return endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR);
+ return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR);
}
@@ -98,44 +94,13 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
//################################# inline implementation ########################################
-namespace z_impl
-{
-#if defined ZEN_WIN || defined ZEN_MAC
-int compareFilenamesNoCase(const Zchar* lhs, const Zchar* rhs, size_t sizeLhs, size_t sizeRhs);
-void makeFilenameUpperCase(Zchar* str, size_t size);
-#endif
-}
-
-template <template <class, class> class SP, class AP> inline
-int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs)
+#ifdef ZEN_LINUX
+inline
+int cmpFileName(const Zstring& lhs, const Zstring& rhs)
{
-#if defined ZEN_WIN || defined ZEN_MAC
- return z_impl::compareFilenamesNoCase(lhs.data(), rhs.data(), lhs.length(), rhs.length());
-#elif defined ZEN_LINUX
return std::strcmp(lhs.c_str(), rhs.c_str()); //POSIX filepaths don't have embedded 0
- //#elif defined ZEN_MAC
- // return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent!
-#endif
-}
-
-
-#if defined ZEN_WIN || defined ZEN_MAC
-template <template <class, class> class SP, class AP> inline
-void makeUpper(zen::Zbase<Zchar, SP, AP>& str)
-{
- z_impl::makeFilenameUpperCase(str.begin(), str.length());
}
#endif
-
-namespace std
-{
-template<> inline
-void swap(Zstring& rhs, Zstring& lhs)
-{
- rhs.swap(lhs);
-}
-}
-
-#endif //ZSTRING_H_INCLUDED
+#endif //ZSTRING_H_INCLUDED_73425873425789
bgstack15