summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2015-10-02 14:56:07 +0200
committerDaniel Wilhelm <daniel@wili.li>2015-10-02 14:56:07 +0200
commitde73d25e0b27f4bee2de116d19cab32800785d64 (patch)
tree21de1736d12a92223ad04c02a5b0826d77e5e71c /zen
parent7.1 (diff)
downloadFreeFileSync-de73d25e0b27f4bee2de116d19cab32800785d64.tar.gz
FreeFileSync-de73d25e0b27f4bee2de116d19cab32800785d64.tar.bz2
FreeFileSync-de73d25e0b27f4bee2de116d19cab32800785d64.zip
7.2
Diffstat (limited to 'zen')
-rw-r--r--zen/async_task.h4
-rw-r--r--zen/basic_math.h2
-rw-r--r--zen/dir_watcher.cpp157
-rw-r--r--zen/file_access.cpp489
-rw-r--r--zen/file_access.h28
-rw-r--r--zen/file_error.h18
-rw-r--r--zen/file_io.cpp99
-rw-r--r--zen/file_io.h2
-rw-r--r--zen/file_traverser.cpp14
-rw-r--r--zen/long_path_prefix.h2
-rw-r--r--zen/perf.h1
-rw-r--r--zen/recycler.cpp16
-rw-r--r--zen/recycler.h2
-rw-r--r--zen/scope_guard.h2
-rw-r--r--zen/shell_execute.h10
-rw-r--r--zen/stl_tools.h5
-rw-r--r--zen/string_base.h25
-rw-r--r--zen/string_tools.h79
-rw-r--r--zen/string_traits.h41
-rw-r--r--zen/symlink_target.h22
-rw-r--r--zen/sys_error.h3
-rw-r--r--zen/thread.h63
-rw-r--r--zen/type_tools.h3
-rw-r--r--zen/win.h4
-rw-r--r--zen/xml_io.cpp8
-rw-r--r--zen/zstring.cpp16
-rw-r--r--zen/zstring.h42
27 files changed, 615 insertions, 542 deletions
diff --git a/zen/async_task.h b/zen/async_task.h
index b8d72fbe..5c6f7f6e 100644
--- a/zen/async_task.h
+++ b/zen/async_task.h
@@ -27,7 +27,7 @@ public:
// -> doAsync: the usual thread-safety requirements apply!
// -> evalOnGui: no thread-safety concerns, but must only reference variables with greater-equal lifetime than the AsyncTask instance!
{
- tasks.push_back(zen::async([=]() -> std::function<void()>
+ tasks.push_back(zen::runAsync([=]() -> std::function<void()>
{
auto result = doAsync();
return [=]{ evalOnGui(result); };
@@ -37,7 +37,7 @@ public:
template <class Fun, class Fun2>
void add2(Fun doAsync, Fun2 evalOnGui) //for evalOnGui taking no parameters
{
- tasks.push_back(zen::async([=]() -> std::function<void()> { doAsync(); return [=]{ evalOnGui(); }; }));
+ tasks.push_back(zen::runAsync([doAsync, evalOnGui]() -> std::function<void()> { doAsync(); return [evalOnGui]{ evalOnGui(); }; }));
}
void evalResults() //call from gui thread repreatedly
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 227d1cd4..14fcae9c 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -92,7 +92,7 @@ const double ln2 = 0.693147180559945309417;
template <class T> inline
T abs(T value)
{
- static_assert(std::is_signed<T>::value, "");
+ //static_assert(std::is_signed<T>::value, "");
if (value < 0)
return -value; //operator "?:" caveat: may be different type than "value"
else
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index a97ea80d..e93e2b06 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -156,7 +156,7 @@ public:
FILE_FLAG_OVERLAPPED, //_In_ DWORD dwFlagsAndAttributes,
nullptr); //_In_opt_ HANDLE hTemplateFile
if (hDir == INVALID_HANDLE_VALUE)
- throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)), L"CreateFile", getLastError());
+ throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(directory)), L"CreateFile", getLastError());
//end of constructor, no need to start managing "hDir"
}
@@ -176,86 +176,79 @@ public:
void operator()() //thread entry
{
- try
- {
- std::vector<char> buffer(64 * 1024); //needs to be aligned on a DWORD boundary; maximum buffer size restricted by some networks protocols (according to docu)
+ std::vector<char> buffer(64 * 1024); //needs to be aligned on a DWORD boundary; maximum buffer size restricted by some networks protocols (according to docu)
- for (;;)
+ for (;;)
+ {
+ boost::this_thread::interruption_point();
+
+ //actual work
+ OVERLAPPED overlapped = {};
+ overlapped.hEvent = ::CreateEvent(nullptr, //__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes,
+ true, //__in BOOL bManualReset,
+ false, //__in BOOL bInitialState,
+ nullptr); //__in_opt LPCTSTR lpName
+ if (overlapped.hEvent == nullptr)
{
- boost::this_thread::interruption_point();
-
- //actual work
- OVERLAPPED overlapped = {};
- overlapped.hEvent = ::CreateEvent(nullptr, //__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes,
- true, //__in BOOL bManualReset,
- false, //__in BOOL bInitialState,
- nullptr); //__in_opt LPCTSTR lpName
- if (overlapped.hEvent == nullptr)
- {
- 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));
-
- DWORD bytesReturned = 0; //should not be needed for async calls, still pass it to help broken drivers
-
- //asynchronous variant: runs on this thread's APC queue!
- if (!::ReadDirectoryChangesW(hDir, // __in HANDLE hDirectory,
- &buffer[0], // __out LPVOID lpBuffer,
- static_cast<DWORD>(buffer.size()), // __in DWORD nBufferLength,
- true, // __in BOOL bWatchSubtree,
- FILE_NOTIFY_CHANGE_FILE_NAME |
- FILE_NOTIFY_CHANGE_DIR_NAME |
- FILE_NOTIFY_CHANGE_SIZE |
- FILE_NOTIFY_CHANGE_LAST_WRITE, // __in DWORD dwNotifyFilter,
- &bytesReturned, // __out_opt LPDWORD lpBytesReturned,
- &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped,
- nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
- {
- const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls!
- return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", ec), ec);
- }
+ const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls!
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(dirpathPf)), formatSystemError(L"CreateEvent", ec), ec);
+ }
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent));
+
+ DWORD bytesReturned = 0; //should not be needed for async calls, still pass it to help broken drivers
+
+ //asynchronous variant: runs on this thread's APC queue!
+ if (!::ReadDirectoryChangesW(hDir, // __in HANDLE hDirectory,
+ &buffer[0], // __out LPVOID lpBuffer,
+ static_cast<DWORD>(buffer.size()), // __in DWORD nBufferLength,
+ true, // __in BOOL bWatchSubtree,
+ FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_SIZE |
+ FILE_NOTIFY_CHANGE_LAST_WRITE, // __in DWORD dwNotifyFilter,
+ &bytesReturned, // __out_opt LPDWORD lpBytesReturned,
+ &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped,
+ nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
+ {
+ const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls!
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(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"!
- zen::ScopeGuard guardAio = zen::makeGuard([&]
- {
- //Canceling Pending I/O Operations: http://msdn.microsoft.com/en-us/library/aa363789(v=vs.85).aspx
+ //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"!
+ zen::ScopeGuard guardAio = zen::makeGuard([&]
+ {
+ //Canceling Pending I/O Operations: http://msdn.microsoft.com/en-us/library/aa363789(v=vs.85).aspx
#ifdef ZEN_WIN_VISTA_AND_LATER
- if (::CancelIoEx(hDir, &overlapped) /*!= FALSE*/ || ::GetLastError() != ERROR_NOT_FOUND)
+ if (::CancelIoEx(hDir, &overlapped) /*!= FALSE*/ || ::GetLastError() != ERROR_NOT_FOUND)
#else
- if (::CancelIo(hDir) /*!= FALSE*/ || ::GetLastError() != ERROR_NOT_FOUND)
+ if (::CancelIo(hDir) /*!= FALSE*/ || ::GetLastError() != ERROR_NOT_FOUND)
#endif
- {
- DWORD bytesWritten = 0;
- ::GetOverlappedResult(hDir, &overlapped, &bytesWritten, true); //wait until cancellation is complete
- }
- });
-
- //wait for results
- DWORD bytesWritten = 0;
- while (!::GetOverlappedResult(hDir, //__in HANDLE hFile,
- &overlapped, //__in LPOVERLAPPED lpOverlapped,
- &bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred,
- false)) //__in BOOL bWait
{
- 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,
- true); // __in BOOL bAlertable
-
- boost::this_thread::interruption_point();
+ DWORD bytesWritten = 0;
+ ::GetOverlappedResult(hDir, &overlapped, &bytesWritten, true); //must wait until cancellation is complete!
}
- guardAio.dismiss();
+ });
+
+ //wait for results
+ DWORD bytesWritten = 0;
+ while (!::GetOverlappedResult(hDir, //__in HANDLE hFile,
+ &overlapped, //__in LPOVERLAPPED lpOverlapped,
+ &bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred,
+ false)) //__in BOOL bWait
+ {
+ 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", fmtPath(dirpathPf)), formatSystemError(L"GetOverlappedResult", ec), ec);
+
+ //execute asynchronous procedure calls (APC) queued on this thread
+ ::SleepEx(50, // __in DWORD dwMilliseconds,
+ true); // __in BOOL bAlertable
- shared_->addChanges(&buffer[0], bytesWritten, dirpathPf); //throw ()
+ boost::this_thread::interruption_point();
}
- }
- catch (boost::thread_interrupted&)
- {
- throw; //this is the only exception expected!
+ guardAio.dismiss();
+
+ shared_->addChanges(&buffer[0], bytesWritten, dirpathPf); //throw ()
}
}
@@ -362,13 +355,13 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
//wait until device removal is confirmed, to prevent locking hDir again by some new watch!
if (pimpl_->volRemoval->requestReceived())
{
- const boost::system_time maxwait = boost::get_system_time() + boost::posix_time::seconds(15);
+ const boost::chrono::steady_clock::time_point endTime = boost::chrono::steady_clock::now() + boost::chrono::seconds(15);
//HandleVolumeRemoval::finished() not guaranteed! note: Windows gives unresponsive applications ca. 10 seconds until unmounting the usb stick in worst case
- while (!pimpl_->volRemoval->finished() && boost::get_system_time() < maxwait)
+ while (!pimpl_->volRemoval->finished() && boost::chrono::steady_clock::now() < endTime)
{
processGuiMessages(); //DBT_DEVICEREMOVECOMPLETE message is sent here!
- boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(50));
+ boost::this_thread::sleep_for(boost::chrono::milliseconds(50)); //throw boost::thread_interrupted
}
output.emplace_back(ACTION_DELETE, baseDirPath); //report removal as change to main directory
@@ -411,7 +404,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
//init
pimpl_->notifDescr = ::inotify_init();
if (pimpl_->notifDescr == -1)
- throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"inotify_init", getLastError());
+ throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init", getLastError());
zen::ScopeGuard guardDescr = zen::makeGuard([&] { ::close(pimpl_->notifDescr); });
@@ -423,7 +416,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1;
}
if (!initSuccess)
- throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"fcntl", getLastError());
+ throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl", getLastError());
//add watches
for (const Zstring& subDirPath : fullDirList)
@@ -443,10 +436,10 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
{
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(subDirPath)),
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)),
formatSystemError(L"inotify_add_watch", ec, L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource."));
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subDirPath)), formatSystemError(L"inotify_add_watch", ec));
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)), formatSystemError(L"inotify_add_watch", ec));
}
pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath));
@@ -479,7 +472,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found
return std::vector<Entry>();
- throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"read", getLastError());
+ throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"read", getLastError());
}
std::vector<Entry> output;
@@ -575,7 +568,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) :
{
CFStringRef dirpathCf = osx::createCFString(baseDirPath.c_str()); //returns nullptr on error
if (!dirpathCf)
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"Function call failed: createCFString"); //no error code documented!
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"Function call failed: createCFString"); //no error code documented!
ZEN_ON_SCOPE_EXIT(::CFRelease(dirpathCf));
CFArrayRef dirpathCfArray = ::CFArrayCreate(nullptr, //CFAllocatorRef allocator,
@@ -583,7 +576,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) :
1, //CFIndex numValues,
nullptr); //const CFArrayCallBacks* callBacks
if (!dirpathCfArray)
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"Function call failed: CFArrayCreate"); //no error code documented!
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"Function call failed: CFArrayCreate"); //no error code documented!
ZEN_ON_SCOPE_EXIT(::CFRelease(dirpathCfArray));
FSEventStreamContext context = {};
@@ -609,7 +602,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) :
zen::ScopeGuard guardRunloop = zen::makeGuard([&] { ::FSEventStreamInvalidate(pimpl_->eventStream); });
if (!::FSEventStreamStart(pimpl_->eventStream))
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"Function call failed: FSEventStreamStart"); //no error code documented!
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"Function call failed: FSEventStreamStart"); //no error code documented!
guardCreate .dismiss();
guardRunloop.dismiss();
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 96aac081..09a1eb07 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -45,45 +45,45 @@
using namespace zen;
-bool zen::fileExists(const Zstring& filepath)
+bool zen::fileExists(const Zstring& filePath)
{
//symbolic links (broken or not) are also treated as existing files!
#ifdef ZEN_WIN
- const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str());
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filePath).c_str());
if (attr != INVALID_FILE_ATTRIBUTES)
return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
- if (::stat(filepath.c_str(), &fileInfo) == 0) //follow symlinks!
+ if (::stat(filePath.c_str(), &fileInfo) == 0) //follow symlinks!
return S_ISREG(fileInfo.st_mode);
#endif
return false;
}
-bool zen::dirExists(const Zstring& dirpath)
+bool zen::dirExists(const Zstring& dirPath)
{
//symbolic links (broken or not) are also treated as existing directories!
#ifdef ZEN_WIN
- const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirpath).c_str());
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirPath).c_str());
if (attr != INVALID_FILE_ATTRIBUTES)
return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat dirInfo = {};
- if (::stat(dirpath.c_str(), &dirInfo) == 0) //follow symlinks!
+ if (::stat(dirPath.c_str(), &dirInfo) == 0) //follow symlinks!
return S_ISDIR(dirInfo.st_mode);
#endif
return false;
}
-bool zen::symlinkExists(const Zstring& linkname)
+bool zen::symlinkExists(const Zstring& linkPath)
{
#ifdef ZEN_WIN
WIN32_FIND_DATA linkInfo = {};
- const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &linkInfo);
+ const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkPath).c_str(), &linkInfo);
if (searchHandle != INVALID_HANDLE_VALUE)
{
::FindClose(searchHandle);
@@ -92,17 +92,17 @@ bool zen::symlinkExists(const Zstring& linkname)
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat linkInfo = {};
- if (::lstat(linkname.c_str(), &linkInfo) == 0)
+ if (::lstat(linkPath.c_str(), &linkInfo) == 0)
return S_ISLNK(linkInfo.st_mode);
#endif
return false;
}
-bool zen::somethingExists(const Zstring& objname)
+bool zen::somethingExists(const Zstring& itemPath)
{
#ifdef ZEN_WIN
- const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(itemPath).c_str());
if (attr != INVALID_FILE_ATTRIBUTES)
return true;
const DWORD lastError = ::GetLastError();
@@ -115,7 +115,7 @@ bool zen::somethingExists(const Zstring& objname)
lastError != ERROR_BAD_NET_NAME) //
{
WIN32_FIND_DATA fileInfo = {};
- const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(objname).c_str(), &fileInfo);
+ const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(itemPath).c_str(), &fileInfo);
if (searchHandle != INVALID_HANDLE_VALUE)
{
::FindClose(searchHandle);
@@ -125,7 +125,7 @@ bool zen::somethingExists(const Zstring& objname)
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
- if (::lstat(objname.c_str(), &fileInfo) == 0)
+ if (::lstat(itemPath.c_str(), &fileInfo) == 0)
return true;
#endif
return false;
@@ -172,26 +172,26 @@ bool isFatDrive(const Zstring& filePath) //throw()
}
-std::uint64_t zen::getFilesize(const Zstring& filepath) //throw FileError
+std::uint64_t zen::getFilesize(const Zstring& filePath) //throw FileError
{
#ifdef ZEN_WIN
{
WIN32_FIND_DATA fileInfo = {};
- const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filepath).c_str(), &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());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"FindFirstFile", getLastError());
::FindClose(searchHandle);
if (!isSymlink(fileInfo))
return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
}
// WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {};
- // if (!::GetFileAttributesEx(applyLongPathPrefix(filepath).c_str(), //__in LPCTSTR lpFileName,
+ // 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,
+ 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,
@@ -199,20 +199,20 @@ std::uint64_t zen::getFilesize(const Zstring& filepath) //throw FileError
FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes,
nullptr); //_In_opt_ HANDLE hTemplateFile
if (hFile == INVALID_HANDLE_VALUE)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"CreateFile", getLastError());
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
//why not use ::GetFileSizeEx() instead???
BY_HANDLE_FILE_INFORMATION fileInfoHnd = {};
if (!::GetFileInformationByHandle(hFile, &fileInfoHnd))
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileInformationByHandle", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle", getLastError());
return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh);
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
- if (::stat(filepath.c_str(), &fileInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"stat", getLastError());
+ if (::stat(filePath.c_str(), &fileInfo) != 0)
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"stat", getLastError());
return fileInfo.st_size;
#endif
@@ -227,7 +227,7 @@ std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, retu
&bytesFree, //__out_opt PULARGE_INTEGER lpFreeBytesAvailable,
nullptr, //__out_opt PULARGE_INTEGER lpTotalNumberOfBytes,
nullptr)) //__out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes
- throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtFileName(path)), L"GetDiskFreeSpaceEx", getLastError());
+ throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"GetDiskFreeSpaceEx", getLastError());
//return 0 if info is not available: "The GetDiskFreeSpaceEx function returns zero for lpFreeBytesAvailable for all CD requests"
return get64BitUInt(bytesFree.LowPart, bytesFree.HighPart);
@@ -235,47 +235,47 @@ std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, retu
#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::statfs info = {};
if (::statfs(path.c_str(), &info) != 0)
- throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtFileName(path)), L"statfs", getLastError());
+ throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"statfs", getLastError());
return static_cast<std::uint64_t>(info.f_bsize) * info.f_bavail;
#endif
}
-bool zen::removeFile(const Zstring& filepath) //throw FileError
+bool zen::removeFile(const Zstring& filePath) //throw FileError
{
#ifdef ZEN_WIN
const wchar_t functionName[] = L"DeleteFile";
- if (!::DeleteFile(applyLongPathPrefix(filepath).c_str()))
+ if (!::DeleteFile(applyLongPathPrefix(filePath).c_str()))
#elif defined ZEN_LINUX || defined ZEN_MAC
const wchar_t functionName[] = L"unlink";
- if (::unlink(filepath.c_str()) != 0)
+ if (::unlink(filePath.c_str()) != 0)
#endif
{
ErrorCode lastError = getLastError();
#ifdef ZEN_WIN
if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only
{
- ::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes
+ ::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes
- if (::DeleteFile(applyLongPathPrefix(filepath).c_str())) //now try again...
+ if (::DeleteFile(applyLongPathPrefix(filePath).c_str())) //now try again...
return true;
lastError = ::GetLastError();
}
#endif
- if (!somethingExists(filepath)) //warning: changes global error code!!
+ if (!somethingExists(filePath)) //warning: changes global error code!!
return false; //neither file nor any other object (e.g. broken symlink) with that name existing - caveat: what if "access is denied"!?!??!?!?
//begin of "regular" error reporting
- const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filepath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtPath(filePath));
std::wstring errorDescr = formatSystemError(functionName, lastError);
#ifdef ZEN_WIN_VISTA_AND_LATER
if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message!
lastError == ERROR_LOCK_VIOLATION)
{
- const std::wstring procList = vista::getLockingProcesses(filepath); //noexcept
+ const std::wstring procList = vista::getLockingProcesses(filePath); //noexcept
if (!procList.empty())
errorDescr = _("The file is locked by another process:") + L"\n" + procList;
}
@@ -286,6 +286,91 @@ bool zen::removeFile(const Zstring& filepath) //throw FileError
}
+void zen::removeDirectorySimple(const Zstring& dirPath) //throw FileError
+{
+#ifdef ZEN_WIN
+ //(try to) normalize file attributes: actually NEEDED for symbolic links also!
+ ::SetFileAttributes(applyLongPathPrefix(dirPath).c_str(), FILE_ATTRIBUTE_NORMAL);
+
+ const wchar_t functionName[] = L"RemoveDirectory";
+ if (!::RemoveDirectory(applyLongPathPrefix(dirPath).c_str()))
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"rmdir";
+ if (::rmdir(dirPath.c_str()) != 0)
+#endif
+ {
+ const ErrorCode ec = getLastError();
+
+ if (!somethingExists(dirPath)) //warning: changes global error code!!
+ return;
+
+#if defined ZEN_LINUX || defined ZEN_MAC
+ if (symlinkExists(dirPath))
+ {
+ if (::unlink(dirPath.c_str()) != 0)
+ throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), L"unlink", getLastError());
+ return;
+ }
+#endif
+
+ throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), functionName, ec);
+ }
+ //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have
+ //successfully been *marked* for deletion, but some application still has a handle open!
+ //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145
+ //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html
+}
+
+
+namespace
+{
+void removeDirectoryImpl(const Zstring& dirPath) //throw FileError
+{
+ assert(dirExists(dirPath)); //[!] no symlinks in this context!!!
+ //attention: check if dirPath is a symlink! Do NOT traverse into it deleting contained files!!!
+
+ std::vector<Zstring> fileList;
+ std::vector<Zstring> dirLinkList;
+ std::vector<Zstring> dirList;
+ //get all files and directories from current directory (WITHOUT subdirectories!)
+ traverseFolder(dirPath,
+ [&](const FileInfo& fi) { fileList.push_back(fi.fullPath); },
+ [&](const DirInfo& di) { dirList .push_back(di.fullPath); },
+ [&](const SymlinkInfo& si)
+ {
+#ifdef ZEN_WIN
+ if (dirExists(si.fullPath)) //dir symlink
+ dirLinkList.push_back(si.fullPath);
+ else //file symlink, broken symlink
+#endif
+ fileList.push_back(si.fullPath);
+ },
+ [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
+
+ for (const Zstring& filePath : fileList)
+ removeFile(filePath); //throw FileError
+
+ for (const Zstring& dirLinkPath : dirLinkList)
+ removeDirectorySimple(dirLinkPath); //throw FileError
+
+ //delete directories recursively
+ for (const Zstring& subDirPath : dirList)
+ removeDirectoryImpl(subDirPath); //throw FileError; call recursively to correctly handle symbolic links
+
+ removeDirectorySimple(dirPath); //throw FileError
+}
+}
+
+
+void zen::removeDirectoryRecursively(const Zstring& dirPath) //throw FileError
+{
+ if (symlinkExists(dirPath))
+ removeDirectorySimple(dirPath); //throw FileError
+ else if (somethingExists(dirPath))
+ removeDirectoryImpl(dirPath); //throw FileError
+}
+
+
namespace
{
/* Usage overview: (avoid circular pattern!)
@@ -334,7 +419,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro
}
}
//begin of "regular" error reporting
- const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(pathSource)), L"%y", L"\n" + fmtFileName(pathTarget));
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget));
std::wstring errorDescr = formatSystemError(L"MoveFileEx", lastError);
#ifdef ZEN_WIN_VISTA_AND_LATER //(try to) enhance error message
@@ -360,7 +445,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro
if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0)
{
const int lastError = errno; //copy before directly or indirectly making other system calls!
- const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(pathSource)), L"%y", L"\n" + fmtFileName(pathTarget));
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget));
const std::wstring errorDescr = formatSystemError(L"rename", lastError);
if (lastError == EXDEV)
@@ -379,17 +464,17 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro
::GetShortPathName()
::GetLongPathName() */
template <typename Function>
-Zstring getFilenameFmt(const Zstring& filepath, Function fun) //throw(); returns empty string on error
+Zstring getFilenameFmt(const Zstring& filePath, Function fun) //throw(); returns empty string on error
{
- const Zstring filepathFmt = applyLongPathPrefix(filepath);
+ const Zstring filePathFmt = applyLongPathPrefix(filePath);
- const DWORD bufferSize = fun(filepathFmt.c_str(), nullptr, 0);
+ const DWORD bufferSize = fun(filePathFmt.c_str(), nullptr, 0);
if (bufferSize == 0)
return Zstring();
std::vector<wchar_t> buffer(bufferSize);
- const DWORD charsWritten = fun(filepathFmt.c_str(), //__in LPCTSTR lpszShortPath,
+ const DWORD charsWritten = fun(filePathFmt.c_str(), //__in LPCTSTR lpszShortPath,
&buffer[0], //__out LPTSTR lpszLongPath,
bufferSize); //__in DWORD cchBuffer
if (charsWritten == 0 || charsWritten >= bufferSize)
@@ -399,18 +484,19 @@ Zstring getFilenameFmt(const Zstring& filepath, Function fun) //throw(); returns
}
-Zstring findUnused8Dot3Name(const Zstring& filepath) //find a unique 8.3 short name
+Zstring findUnused8Dot3Name(const Zstring& filePath) //find a unique 8.3 short name
{
- const Zstring pathPrefix = contains(filepath, FILE_NAME_SEPARATOR) ?
- (beforeLast(filepath, FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR) : Zstring();
+ const Zstring pathPrefix = contains(filePath, FILE_NAME_SEPARATOR) ?
+ (beforeLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) + FILE_NAME_SEPARATOR) : Zstring();
- Zstring extension = afterLast(afterLast(filepath, FILE_NAME_SEPARATOR), Zchar('.')); //extension needn't contain reasonable data
+ //extension needn't contain reasonable data
+ Zstring extension = getFileExtension(filePath);
if (extension.empty())
extension = Zstr("FFS");
else if (extension.length() > 3)
extension.resize(3);
- for (int index = 0; index < 100000000; ++index) //filepath must be representable by <= 8 characters
+ for (int index = 0; index < 100000000; ++index) //filePath must be representable by <= 8 characters
{
const Zstring output = pathPrefix + numberTo<Zstring>(index) + Zchar('.') + extension;
if (!somethingExists(output)) //ensure uniqueness
@@ -421,24 +507,24 @@ Zstring findUnused8Dot3Name(const Zstring& filepath) //find a unique 8.3 short n
}
-bool have8dot3NameClash(const Zstring& filepath)
+bool have8dot3NameClash(const Zstring& filePath)
{
- if (!contains(filepath, FILE_NAME_SEPARATOR))
+ if (!contains(filePath, FILE_NAME_SEPARATOR))
return false;
- if (somethingExists(filepath)) //name OR directory!
+ if (somethingExists(filePath)) //name OR directory!
{
- const Zstring origName = afterLast(filepath, FILE_NAME_SEPARATOR); //returns the whole string if ch not found
- const Zstring shortName = afterLast(getFilenameFmt(filepath, ::GetShortPathName), FILE_NAME_SEPARATOR); //throw() returns empty string on error
- const Zstring longName = afterLast(getFilenameFmt(filepath, ::GetLongPathName ), FILE_NAME_SEPARATOR); //
+ const Zstring origName = afterLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ const Zstring shortName = afterLast(getFilenameFmt(filePath, ::GetShortPathName), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); //throw() returns empty string on error
+ const Zstring longName = afterLast(getFilenameFmt(filePath, ::GetLongPathName ), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); //
if (!shortName.empty() &&
!longName .empty() &&
EqualFilePath()(origName, shortName) &&
!EqualFilePath()(shortName, longName))
{
- //for filepath short and long file name are equal and another unrelated file happens to have the same short name
- //e.g. filepath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1"
+ //for filePath short and long file name are equal and another unrelated file happens to have the same short name
+ //e.g. filePath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1"
return true;
}
}
@@ -448,14 +534,17 @@ bool have8dot3NameClash(const Zstring& filepath)
class Fix8Dot3NameClash //throw FileError
{
public:
- Fix8Dot3NameClash(const Zstring& filepath)
+ Fix8Dot3NameClash(const Zstring& filePath)
{
- const Zstring longName = afterLast(getFilenameFmt(filepath, ::GetLongPathName), FILE_NAME_SEPARATOR); //throw() returns empty string on error
+ const Zstring longName = afterLast(getFilenameFmt(filePath, ::GetLongPathName), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); //throw() returns empty string on error
- unrelatedFile = beforeLast(filepath, FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + longName;
+ unrelatedFile = beforeLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ if (!unrelatedFile.empty())
+ unrelatedFile += FILE_NAME_SEPARATOR;
+ unrelatedFile += longName;
//find another name in short format: this ensures the actual short name WILL be renamed as well!
- unrelatedFileParked = findUnused8Dot3Name(filepath);
+ unrelatedFileParked = findUnused8Dot3Name(filePath);
//move already existing short name out of the way for now
renameFile_sub(unrelatedFile, unrelatedFileParked); //throw FileError, ErrorDifferentVolume
@@ -492,7 +581,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr
//try to handle issues with already existing short 8.3 file names on Windows
if (have8dot3NameClash(pathTarget))
{
- Fix8Dot3NameClash dummy(pathTarget); //throw FileError; move clashing filepath to the side
+ Fix8Dot3NameClash dummy(pathTarget); //throw FileError; move clashing file path to the side
//now try again...
renameFile_sub(pathSource, pathTarget); //throw FileError
return;
@@ -505,83 +594,9 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr
namespace
{
-void removeDirectoryImpl(const Zstring& directory, //throw FileError
- const std::function<void (const Zstring& filepath)>& onBeforeFileDeletion,
- const std::function<void (const Zstring& dirpath )>& onBeforeDirDeletion)
-{
- assert(somethingExists(directory)); //[!]
#ifdef ZEN_WIN
- const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix
-
- //(try to) normalize file attributes: actually NEEDED for symbolic links also!
- ::SetFileAttributes(directoryFmt.c_str(), FILE_ATTRIBUTE_NORMAL);
-#endif
-
- //attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
- if (symlinkExists(directory)) //remove symlink directly
- {
- if (onBeforeDirDeletion)
- onBeforeDirDeletion(directory); //once per symlink
-#ifdef ZEN_WIN
- const wchar_t functionName[] = L"RemoveDirectory";
- if (!::RemoveDirectory(directoryFmt.c_str()))
-#elif defined ZEN_LINUX || defined ZEN_MAC
- const wchar_t functionName[] = L"unlink";
- if (::unlink(directory.c_str()) != 0)
-#endif
- throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), functionName, getLastError());
- }
- else
- {
- std::vector<Zstring> fileList;
- std::vector<Zstring> dirList;
- //get all files and directories from current directory (WITHOUT subdirectories!)
- traverseFolder(directory,
- [&](const FileInfo& fi) { fileList.push_back(fi.fullPath); },
- [&](const DirInfo& di) { dirList .push_back(di.fullPath); },
- [&](const SymlinkInfo& si)
- {
- if (dirExists(si.fullPath)) //dir symlink
- dirList.push_back(si.fullPath);
- else //file symlink, broken symlink
- fileList.push_back(si.fullPath);
- },
- [&](const std::wstring& errorMsg) { throw FileError(errorMsg); });
-
- //delete directories recursively
- for (const Zstring& dirpath : dirList)
- removeDirectoryImpl(dirpath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError; call recursively to correctly handle symbolic links
-
- //delete files
- for (const Zstring& filepath : fileList)
- {
- if (onBeforeFileDeletion)
- onBeforeFileDeletion(filepath); //call once per file
- removeFile(filepath); //throw FileError
- }
-
- //parent directory is deleted last
- if (onBeforeDirDeletion)
- onBeforeDirDeletion(directory); //and once per folder
-#ifdef ZEN_WIN
- const wchar_t functionName[] = L"RemoveDirectory";
- if (!::RemoveDirectory(directoryFmt.c_str()))
-#elif defined ZEN_LINUX || defined ZEN_MAC
- const wchar_t functionName[] = L"rmdir";
- if (::rmdir(directory.c_str()) != 0)
-#endif
- throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), functionName, getLastError());
- //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have
- //successfully been *marked* for deletion, but some application still has a handle open!
- //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145
- //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html
- }
-}
-
-
-#ifdef ZEN_WIN
-void setFileTimeRaw(const Zstring& filepath,
+void setFileTimeRaw(const Zstring& filePath,
const FILETIME* creationTime, //optional
const FILETIME& lastWriteTime,
ProcSymlink procSl) //throw FileError
@@ -613,21 +628,21 @@ void setFileTimeRaw(const Zstring& filepath,
DWORD attribs = INVALID_FILE_ATTRIBUTES;
ZEN_ON_SCOPE_EXIT(
if (attribs != INVALID_FILE_ATTRIBUTES)
- ::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), attribs);
+ ::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), attribs);
);
auto removeReadonly = [&]() -> bool //throw FileError; may need to remove the readonly-attribute (e.g. on FAT usb drives)
{
if (attribs == INVALID_FILE_ATTRIBUTES)
{
- const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str());
+ const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filePath).c_str());
if (tmpAttr == INVALID_FILE_ATTRIBUTES)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileAttributes", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileAttributes", getLastError());
if (tmpAttr & FILE_ATTRIBUTE_READONLY)
{
- if (!::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), FILE_ATTRIBUTE_NORMAL))
- throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filepath)), L"SetFileAttributes", getLastError());
+ if (!::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), FILE_ATTRIBUTE_NORMAL))
+ throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileAttributes", getLastError());
attribs = tmpAttr; //reapplied on scope exit
return true;
@@ -638,7 +653,7 @@ void setFileTimeRaw(const Zstring& filepath,
auto openFile = [&](bool conservativeApproach)
{
- return ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName,
+ return ::CreateFile(applyLongPathPrefix(filePath).c_str(), //_In_ LPCTSTR lpFileName,
(conservativeApproach ?
//some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently!
//http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430
@@ -677,7 +692,7 @@ void setFileTimeRaw(const Zstring& filepath,
continue;
//3. after these herculean stunts we give up...
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", lastError);
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"CreateFile", lastError);
}
}
break;
@@ -707,7 +722,7 @@ void setFileTimeRaw(const Zstring& filepath,
FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
&basicInfo, //__in LPVOID lpFileInformation,
sizeof(basicInfo))) //__in DWORD dwBufferSize
- throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filepath)), L"SetFileInformationByHandle", getLastError());
+ throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileInformationByHandle", getLastError());
};
auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER
@@ -745,32 +760,32 @@ void setFileTimeRaw(const Zstring& filepath,
}
}
- std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath));
+ std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath));
//add more meaningful message: FAT accepts only a subset of the NTFS date range
if (lastError == ERROR_INVALID_PARAMETER &&
- 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
+ auto fmtDate = [](const FILETIME& ft)
{
SYSTEMTIME st = {};
if (!::FileTimeToSystemTime(&ft, //__in const FILETIME *lpFileTime,
&st)) //__out LPSYSTEMTIME lpSystemTime
- return Zstring();
+ return std::wstring();
- Zstring dateTime;
+ std::wstring dateTime;
{
const int bufferSize = ::GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0);
if (bufferSize > 0)
{
std::vector<wchar_t> buffer(bufferSize);
if (::GetDateFormat(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
- 0, //_In_ DWORD dwFlags,
- &st, //_In_opt_ const SYSTEMTIME *lpDate,
- nullptr, //_In_opt_ LPCTSTR lpFormat,
- &buffer[0], //_Out_opt_ LPTSTR lpDateStr,
- bufferSize) > 0) //_In_ int cchDate
+ 0, //_In_ DWORD dwFlags,
+ &st, //_In_opt_ const SYSTEMTIME *lpDate,
+ nullptr, //_In_opt_ LPCTSTR lpFormat,
+ &buffer[0], //_Out_opt_ LPTSTR lpDateStr,
+ bufferSize) > 0) //_In_ int cchDate
dateTime = &buffer[0]; //GetDateFormat() returns char count *including* 0-termination!
}
}
@@ -801,7 +816,7 @@ void setFileTimeRaw(const Zstring& filepath,
FILETIME creationTimeDbg = {};
FILETIME lastWriteTimeDbg = {};
- HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName,
+ HANDLE hFile = ::CreateFile(applyLongPathPrefix(filePath).c_str(), //_In_ LPCTSTR lpFileName,
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, //_In_ DWORD dwDesiredAccess,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
@@ -854,17 +869,17 @@ void setFileTimeRaw(const Zstring& filePath, const struct ::timespec& modTime, P
if (errno == EACCES) //bullshit, access denied even with 0777 permissions! => utimes should work!
throw ErrorLinuxFallbackToUtimes(L"");
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filePath)), L"open", getLastError());
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"open", getLastError());
}
ZEN_ON_SCOPE_EXIT(::close(fdFile));
if (::futimens(fdFile, newTimes) != 0)
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filePath)), L"futimens", getLastError());
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"futimens", getLastError());
}
else
{
if (::utimensat(AT_FDCWD, filePath.c_str(), newTimes, AT_SYMLINK_NOFOLLOW) != 0)
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filePath)), L"utimensat", getLastError());
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimensat", getLastError());
}
}
@@ -878,7 +893,7 @@ struct AttrBufFileTimes
} __attribute__((aligned(4), packed));
-void setFileTimeRaw(const Zstring& filepath,
+void setFileTimeRaw(const Zstring& filePath,
const struct ::timespec* createTime, //optional
const struct ::timespec& writeTime,
ProcSymlink procSl) //throw FileError
@@ -900,18 +915,18 @@ void setFileTimeRaw(const Zstring& filepath,
newTimes.writeTime.tv_sec = writeTime.tv_sec;
newTimes.writeTime.tv_nsec = writeTime.tv_nsec;
- const int rv = ::setattrlist(filepath.c_str(), //const char* path,
+ const int rv = ::setattrlist(filePath.c_str(), //const char* path,
&attribs, //struct ::attrlist* attrList,
createTime ? &newTimes.createTime : &newTimes.writeTime, //void* attrBuf,
(createTime ? sizeof(newTimes.createTime) : 0) + sizeof(newTimes.writeTime), //size_t attrBufSize,
procSl == ProcSymlink::DIRECT ? FSOPT_NOFOLLOW : 0); //unsigned long options
if (rv != 0)
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"setattrlist", getLastError());
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"setattrlist", getLastError());
}
/*
void getFileTimeRaw(int fd, //throw FileError
- const Zstring& filepath, //for error reporting only
+ const Zstring& filePath, //for error reporting only
struct ::timespec& createTime, //out
struct ::timespec& writeTime) //
{
@@ -928,7 +943,7 @@ void getFileTimeRaw(int fd, //throw FileError
sizeof(fileTimes), //size_t attrBufSize,
0); //unsigned long options
if (rv != 0)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"getattrlist", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"getattrlist", getLastError());
createTime.tv_sec = fileTimes.createTime.tv_sec;
createTime.tv_nsec = fileTimes.createTime.tv_nsec;
@@ -940,17 +955,6 @@ void getFileTimeRaw(int fd, //throw FileError
}
-void zen::removeDirectory(const Zstring& directory, //throw FileError
- const std::function<void (const Zstring& filepath)>& onBeforeFileDeletion,
- const std::function<void (const Zstring& dirpath)>& onBeforeDirDeletion)
-{
- //no error situation if directory is not existing! manual deletion relies on it!
- if (!somethingExists(directory))
- return; //neither directory nor any other object (e.g. broken symlink) with that name existing
- removeDirectoryImpl(directory, onBeforeFileDeletion, onBeforeDirDeletion);
-}
-
-
void zen::setFileTime(const Zstring& filePath, std::int64_t modTime, ProcSymlink procSl) //throw FileError
{
#ifdef ZEN_WIN
@@ -972,12 +976,12 @@ void zen::setFileTime(const Zstring& filePath, std::int64_t modTime, ProcSymlink
if (procSl == ProcSymlink::FOLLOW)
{
if (::utimes(filePath.c_str(), writeTime) != 0)
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filePath)), L"utimes", getLastError());
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimes", getLastError());
}
else
{
if (::lutimes(filePath.c_str(), writeTime) != 0)
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filePath)), L"lutimes", getLastError());
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"lutimes", getLastError());
}
}
@@ -998,7 +1002,7 @@ bool zen::supportsPermissions(const Zstring& dirpath) //throw FileError
if (!::GetVolumePathName(dirpath.c_str(), //__in LPCTSTR lpszFileName,
&buffer[0], //__out LPTSTR lpszVolumePathName,
bufferSize)) //__in DWORD cchBufferLength
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirpath)), L"GetVolumePathName", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(dirpath)), L"GetVolumePathName", getLastError());
const Zstring volumePath = appendSeparator(&buffer[0]);
@@ -1011,7 +1015,7 @@ bool zen::supportsPermissions(const Zstring& dirpath) //throw FileError
&fsFlags, //__out_opt LPDWORD lpFileSystemFlags,
nullptr, //__out LPTSTR lpFileSystemNameBuffer,
0)) //__in DWORD nFileSystemNameSize
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirpath)), L"GetVolumeInformation", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(dirpath)), L"GetVolumeInformation", getLastError());
return (fsFlags & FILE_PERSISTENT_ACLS) != 0;
@@ -1037,7 +1041,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem
return;
- throwFileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtFileName(source)), L"getfilecon", getLastError());
+ throwFileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtPath(source)), L"getfilecon", getLastError());
}
ZEN_ON_SCOPE_EXIT(::freecon(contextSource));
@@ -1065,7 +1069,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
::setfilecon(target.c_str(), contextSource) :
::lsetfilecon(target.c_str(), contextSource);
if (rv3 < 0)
- throwFileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtFileName(target)), L"setfilecon", getLastError());
+ throwFileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtPath(target)), L"setfilecon", getLastError());
}
#endif //HAVE_SELINUX
@@ -1097,7 +1101,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
}
catch (const FileError& e)//add some more context description (e.g. user is not an admin)
{
- throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), e.toString());
+ throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourceResolved)), e.toString());
}
@@ -1116,7 +1120,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
if (bytesNeeded > buffer.size())
buffer.resize(bytesNeeded);
else
- throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), L"GetFileSecurity", getLastError());
+ throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourceResolved)), L"GetFileSecurity", getLastError());
}
SECURITY_DESCRIPTOR& secDescr = reinterpret_cast<SECURITY_DESCRIPTOR&>(buffer[0]);
@@ -1140,7 +1144,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInformation,
&secDescr)) //__in PSECURITY_DESCRIPTOR pSecurityDescriptor
- throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetResolved)), L"SetFileSecurity", getLastError());
+ throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetResolved)), L"SetFileSecurity", getLastError());
/*
PSECURITY_DESCRIPTOR buffer = nullptr;
@@ -1232,25 +1236,25 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
if (procSl == ProcSymlink::FOLLOW)
{
if (::stat(sourcePath.c_str(), &fileInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourcePath)), L"stat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat", getLastError());
if (::chown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
- throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetPath)), L"chown", getLastError());
+ throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown", getLastError());
if (::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
- throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetPath)), L"chmod", getLastError());
+ throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod", getLastError());
}
else
{
if (::lstat(sourcePath.c_str(), &fileInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourcePath)), L"lstat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat", getLastError());
if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
- throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetPath)), L"lchown", getLastError());
+ throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown", getLastError());
if (!symlinkExists(targetPath) && //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
- throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetPath)), L"chmod", getLastError());
+ throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod", getLastError());
}
#elif defined ZEN_MAC
@@ -1259,7 +1263,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
flags |= COPYFILE_NOFOLLOW;
if (::copyfile(sourcePath.c_str(), targetPath.c_str(), 0, flags) != 0)
- throwFileError(replaceCpy(replaceCpy(_("Cannot copy permissions from %x to %y."), L"%x", L"\n" + fmtFileName(sourcePath)), L"%y", L"\n" + fmtFileName(targetPath)), L"copyfile", getLastError());
+ throwFileError(replaceCpy(replaceCpy(_("Cannot copy permissions from %x to %y."), L"%x", L"\n" + fmtPath(sourcePath)), L"%y", L"\n" + fmtPath(targetPath)), L"copyfile", getLastError());
//owner is *not* copied with ::copyfile():
@@ -1267,24 +1271,24 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
if (procSl == ProcSymlink::FOLLOW)
{
if (::stat(sourcePath.c_str(), &fileInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourcePath)), L"stat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat", getLastError());
if (::chown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
- throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetPath)), L"chown", getLastError());
+ throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown", getLastError());
}
else
{
if (::lstat(sourcePath.c_str(), &fileInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourcePath)), L"lstat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat", getLastError());
if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
- throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetPath)), L"lchown", getLastError());
+ throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown", getLastError());
}
#endif
}
-void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTargetExisting
+void makeDirectoryRecursivelyImpl(const Zstring& directory) //FileError
{
assert(!endsWith(directory, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input!
@@ -1292,18 +1296,15 @@ void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTarget
{
copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
}
+ catch (const ErrorTargetExisting&) {} //*something* existing: folder or FILE!
catch (const ErrorTargetPathMissing&)
{
//we need to create parent directories first
- const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR);
+ const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
if (!dirParent.empty())
{
//recurse...
- try
- {
- makeDirectoryRecursively(dirParent); //throw FileError, (ErrorTargetExisting)
- }
- catch (const ErrorTargetExisting&) {} //parent directory created externally in the meantime? => NOT AN ERROR; not a directory? fail in next step!
+ makeDirectoryRecursivelyImpl(dirParent); //throw FileError
//now try again...
copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing)
@@ -1315,32 +1316,24 @@ void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTarget
}
-void zen::makeNewDirectory(const Zstring& directory) //throw FileError, ErrorTargetExisting
+void zen::makeDirectoryRecursively(const Zstring& dirpath) //throw FileError
{
//remove trailing separator (even for C:\ root directories)
- const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ?
- beforeLast(directory, FILE_NAME_SEPARATOR) :
- directory;
-
- try
- {
- makeDirectoryRecursively(dirFormatted); //FileError, ErrorTargetExisting
- }
- catch (const ErrorTargetExisting&) //*something* existing: folder or FILE!
- {
- //avoid any file system race-condition by *not* checking existence again here!!!
- throw;
- }
+ const Zstring dirFormatted = endsWith(dirpath, FILE_NAME_SEPARATOR) ?
+ beforeLast(dirpath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) :
+ dirpath;
+ makeDirectoryRecursivelyImpl(dirFormatted); //FileError
}
+//source path is optional (may be empty)
void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
bool copyFilePermissions)
{
#ifdef ZEN_WIN
//special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS!
Zstring dirTmp = removeLongPathPrefix(endsWith(targetPath, FILE_NAME_SEPARATOR) ?
- beforeLast(targetPath, FILE_NAME_SEPARATOR) :
+ beforeLast(targetPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) :
targetPath);
if (dirTmp.size() == 2 &&
isAlpha(dirTmp[0]) && dirTmp[1] == L':')
@@ -1349,7 +1342,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
const ErrorCode lastError = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting!
- const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp));
+ const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirTmp));
const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError);
if (lastError == ERROR_ALREADY_EXISTS)
@@ -1381,7 +1374,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
if (lastError != ERROR_SUCCESS)
{
- const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(targetPath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(targetPath));
const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError);
if (lastError == ERROR_ALREADY_EXISTS)
@@ -1393,7 +1386,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
}
#elif defined ZEN_LINUX || defined ZEN_MAC
- mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //= default for newly created directory
+ mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //0777, default for newly created directories
struct ::stat dirInfo = {};
if (!sourcePath.empty())
@@ -1407,7 +1400,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
if (::mkdir(targetPath.c_str(), mode) != 0)
{
const int lastError = errno; //copy before directly or indirectly making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(targetPath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(targetPath));
const std::wstring errorDescr = formatSystemError(L"mkdir", lastError);
if (lastError == EEXIST)
@@ -1487,7 +1480,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
/*int rv =*/ ::copyfile(sourcePath.c_str(), targetPath.c_str(), 0, COPYFILE_XATTR);
#endif
- zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(targetPath); } catch (FileError&) {} }); //ensure cleanup:
+ zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectorySimple(targetPath); } catch (FileError&) {} }); //ensure cleanup:
//enforce copying file permissions: it's advertized on GUI...
if (copyFilePermissions)
@@ -1513,7 +1506,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW");
if (!createSymbolicLink)
- throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)),
+ throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)),
replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\""));
const wchar_t functionName[] = L"CreateSymbolicLinkW";
@@ -1524,7 +1517,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
const wchar_t functionName[] = L"symlink";
if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
#endif
- throwFileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)), functionName, getLastError());
+ throwFileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName, getLastError());
//allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist!
zen::ScopeGuard guardNewLink = zen::makeGuard([&]
@@ -1533,7 +1526,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
{
#ifdef ZEN_WIN
if (isDirLink)
- removeDirectory(targetLink); //throw FileError
+ removeDirectorySimple(targetLink); //throw FileError
else
#endif
removeFile(targetLink); //throw FileError
@@ -1547,24 +1540,24 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
if (!::GetFileAttributesEx(applyLongPathPrefix(sourceLink).c_str(), //__in LPCTSTR lpFileName,
GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
&sourceAttr)) //__out LPVOID lpFileInformation
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"GetFileAttributesEx", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"GetFileAttributesEx", getLastError());
setFileTimeRaw(targetLink, &sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError
#elif defined ZEN_LINUX
struct ::stat sourceInfo = {};
if (::lstat(sourceLink.c_str(), &sourceInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"lstat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat", getLastError());
setFileTime(targetLink, sourceInfo.st_mtime, ProcSymlink::DIRECT); //throw FileError
#elif defined ZEN_MAC
struct ::stat sourceInfo = {};
if (::lstat(sourceLink.c_str(), &sourceInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"lstat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat", getLastError());
if (::copyfile(sourceLink.c_str(), targetLink.c_str(), 0, COPYFILE_XATTR | COPYFILE_NOFOLLOW) != 0)
- throwFileError(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)), L"copyfile", getLastError());
+ throwFileError(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), L"copyfile", getLastError());
setFileTimeRaw(targetLink, &sourceInfo.st_birthtimespec, sourceInfo.st_mtimespec, ProcSymlink::DIRECT); //throw FileError
#endif
@@ -1731,7 +1724,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
{
const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile));
+ const std::wstring errorMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile));
std::wstring errorDescr = formatSystemError(L"CreateFile", lastError);
//if file is locked throw "ErrorFileLocked" instead!
@@ -1753,12 +1746,12 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
//----------------------------------------------------------------------
BY_HANDLE_FILE_INFORMATION fileInfoSource = {};
if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource))
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"GetFileInformationByHandle", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"GetFileInformationByHandle", getLastError());
//encrypted files cannot be read with BackupRead which would fail silently!
const bool sourceIsEncrypted = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0;
if (sourceIsEncrypted)
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead: Source file is encrypted.");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: Source file is encrypted.");
//----------------------------------------------------------------------
const DWORD validAttribs = FILE_ATTRIBUTE_NORMAL | //"This attribute is valid only if used alone."
@@ -1785,7 +1778,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
if (hFileTarget == INVALID_HANDLE_VALUE)
{
const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile));
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile));
const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError);
if (lastError == ERROR_FILE_EXISTS || //confirmed to be used
@@ -1802,7 +1795,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
//----------------------------------------------------------------------
BY_HANDLE_FILE_INFORMATION fileInfoTarget = {};
if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget))
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"GetFileInformationByHandle", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"GetFileInformationByHandle", getLastError());
//return up-to-date file attributes
InSyncAttributes newAttrib = {};
@@ -1853,7 +1846,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
0, //_In_ DWORD nOutBufferSize,
&bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
- throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError());
+ throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError());
}
//----------------------------------------------------------------------
@@ -1880,10 +1873,10 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
false, //__in BOOL bAbort,
false, //__in BOOL bProcessSecurity,
&contextRead)) //__out LPVOID *lpContext
- throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead", getLastError()); //better use fine-granular error messages "reading/writing"!
+ throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead", getLastError()); //better use fine-granular error messages "reading/writing"!
if (bytesRead > BUFFER_SIZE)
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this
if (bytesRead < BUFFER_SIZE)
eof = true;
@@ -1896,10 +1889,10 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
false, //__in BOOL bAbort,
false, //__in BOOL bProcessSecurity,
&contextWrite)) //__out LPVOID *lpContext
- throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"BackupWrite", getLastError());
+ throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite", getLastError());
if (bytesWritten != bytesRead)
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"BackupWrite: incomplete write."); //user should never see this
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite: incomplete write."); //user should never see this
//total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)!
if (onUpdateCopyStatus) onUpdateCopyStatus(bytesRead); //throw X!
@@ -1912,14 +1905,14 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
//::BackupRead() silently fails reading encrypted files -> double check!
if (!someBytesRead && get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh) != 0U)
//note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)!
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead: unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()"
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()"
//time needs to be set at the end: BackupWrite() changes modification time
if (!::SetFileTime(hFileTarget,
&fileInfoSource.ftCreationTime,
nullptr,
&fileInfoSource.ftLastWriteTime))
- throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)), L"SetFileTime", getLastError());
+ throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(targetFile)), L"SetFileTime", getLastError());
guardTarget.dismiss();
return newAttrib;
@@ -1992,10 +1985,10 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
{
//#################### return source file attributes ################################
if (!::GetFileInformationByHandle(hSourceFile, &cbd.fileInfoSrc))
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.sourceFile_)), L"GetFileInformationByHandle", ::GetLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(cbd.sourceFile_)), L"GetFileInformationByHandle", ::GetLastError());
if (!::GetFileInformationByHandle(hDestinationFile, &cbd.fileInfoTrg))
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.targetFile_)), L"GetFileInformationByHandle", ::GetLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(cbd.targetFile_)), L"GetFileInformationByHandle", ::GetLastError());
//#################### switch to sparse file copy if req. #######################
#ifdef ZEN_WIN_VISTA_AND_LATER
@@ -2046,7 +2039,9 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
return PROGRESS_CONTINUE;
}
-
+#if defined _MSC_VER && _MSC_VER > 1800
+ #error get rid!
+#endif
const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destination is not supported with Windows 2000
//caveat: function scope static initialization is not thread-safe in VS 2010! -> still not sufficient if multiple threads access during static init!!!
@@ -2109,7 +2104,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE
throw ErrorFallbackToCopyAsBackupStream(L"bogus file not found");
//assemble error message...
- const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile));
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtPath(sourceFile)), L"%y", L"\n" + fmtPath(targetFile));
std::wstring errorDescr = formatSystemError(L"CopyFileEx", lastError);
//if file is locked throw "ErrorFileLocked" instead!
@@ -2121,7 +2116,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE
if (!procList.empty())
errorDescr = _("The file is locked by another process:") + L"\n" + procList;
#endif
- throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), errorDescr);
+ throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), errorDescr);
}
//if target is existing this functions is expected to throw ErrorTargetExisting!!!
@@ -2143,7 +2138,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE
errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabytes.";
//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.
+ //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target file path is of a restricted type.
}
catch (FileError&) {}
@@ -2195,8 +2190,8 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile,
//try to handle issues with already existing short 8.3 file names on Windows
if (have8dot3NameClash(targetFile))
{
- Fix8Dot3NameClash dummy(targetFile); //throw FileError; move clashing filepath to the side
- return copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError; the short filepath name clash is solved, this should work now
+ Fix8Dot3NameClash dummy(targetFile); //throw FileError; move clashing file path to the side
+ return copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError; the short file path name clash is solved, this should work now
}
throw;
}
@@ -2212,7 +2207,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
struct ::stat sourceInfo = {};
if (::fstat(fileIn.getHandle(), &sourceInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"fstat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"fstat", getLastError());
const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL,
sourceInfo.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); //analog to "cp" which copies "mode" (considering umask) by default
@@ -2220,7 +2215,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
if (fdTarget == -1)
{
const int ec = errno; //copy before making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile));
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile));
const std::wstring errorDescr = formatSystemError(L"open", ec);
if (ec == EEXIST)
@@ -2243,7 +2238,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
struct ::stat targetInfo = {};
if (::fstat(fileOut.getHandle(), &targetInfo) != 0)
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"fstat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"fstat", getLastError());
newAttrib.fileSize = sourceInfo.st_size;
#ifdef ZEN_MAC
@@ -2261,7 +2256,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
//docs: http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/copyfile.3.html
//source: http://www.opensource.apple.com/source/copyfile/copyfile-103.92.1/copyfile.c
if (::fcopyfile(fileIn.getHandle(), fileOut.getHandle(), 0, COPYFILE_XATTR) != 0)
- throwFileError(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)), L"copyfile", getLastError());
+ throwFileError(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtPath(sourceFile)), L"%y", L"\n" + fmtPath(targetFile)), L"copyfile", getLastError());
#endif
fileOut.close(); //throw FileError -> optional, but good place to catch errors when closing stream!
diff --git a/zen/file_access.h b/zen/file_access.h
index 0dfb650e..4b1c31dd 100644
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -14,10 +14,10 @@
namespace zen
{
-bool fileExists (const Zstring& filepath); //noexcept; check whether file or file-symlink exists
-bool dirExists (const Zstring& dirpath ); //noexcept; check whether directory or dir-symlink exists
-bool symlinkExists (const Zstring& linkname); //noexcept; check whether a symbolic link exists
-bool somethingExists(const Zstring& objname ); //noexcept; check whether any object with this name exists
+bool fileExists (const Zstring& filePath); //noexcept; check whether file or file-symlink exists
+bool dirExists (const Zstring& dirPath ); //noexcept; check whether directory or dir-symlink exists
+bool symlinkExists (const Zstring& linkPath); //noexcept; check whether a symbolic link exists
+bool somethingExists(const Zstring& itemPath); //noexcept; check whether any object with this name exists
enum class ProcSymlink
{
@@ -25,27 +25,29 @@ enum class ProcSymlink
FOLLOW
};
-void setFileTime(const Zstring& filepath, std::int64_t modificationTime, ProcSymlink procSl); //throw FileError
+void setFileTime(const Zstring& filePath, std::int64_t modificationTime, ProcSymlink procSl); //throw FileError
//symlink handling: always evaluate target
-std::uint64_t getFilesize(const Zstring& filepath); //throw FileError
+std::uint64_t getFilesize(const Zstring& filePath); //throw FileError
std::uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available
-bool removeFile(const Zstring& filepath); //throw FileError; return "false" if file is not existing
-void removeDirectory(const Zstring& directory, //throw FileError
- const std::function<void (const Zstring& filepath)>& onBeforeFileDeletion = nullptr, //optional;
- const std::function<void (const Zstring& dirpath )>& onBeforeDirDeletion = nullptr); //one call for each *existing* object!
+bool removeFile(const Zstring& filePath); //throw FileError; return "false" if file is not existing
+
+void removeDirectorySimple(const Zstring& dirPath); //throw FileError
+
+void removeDirectoryRecursively(const Zstring& dirPath); //throw FileError
//rename file or directory: no copying!!!
void renameFile(const Zstring& itemPathOld, const Zstring& itemPathNew); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
bool supportsPermissions(const Zstring& dirpath); //throw FileError, dereferences symlinks
-//if parent directory not existing: create recursively:
-void makeNewDirectory(const Zstring& directory); //throw FileError, ErrorTargetExisting
+//- no error if already existing
+//- create recursively if parent directory is not existing
+void makeDirectoryRecursively(const Zstring& dirpath); //throw FileError
//fail if already existing or parent directory not existing:
-//directory should not end with path separator
+//source path is optional (may be empty)
void copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError
diff --git a/zen/file_error.h b/zen/file_error.h
index 4ba05107..d8c6224c 100644
--- a/zen/file_error.h
+++ b/zen/file_error.h
@@ -47,22 +47,14 @@ void throwFileError(const std::wstring& msg, const std::wstring& functionName, c
//----------- facilitate usage of std::wstring for error messages --------------------
-//allow implicit UTF8 conversion: since std::wstring models a GUI string, convenience is more important than performance
inline
-std::wstring operator+(const std::wstring& lhs, const Zstring& rhs) { return std::wstring(lhs) += utfCvrtTo<std::wstring>(rhs); }
-
-//we musn't put our overloads in namespace std, but namespace zen (+ using directive) is sufficient
-
-
-inline
-std::wstring fmtFileName(const Zstring& filepath)
+std::wstring fmtPath(const std::wstring& displayPath)
{
- std::wstring output;
- output += L'\"';
- output += utfCvrtTo<std::wstring>(filepath);
- output += L'\"';
- return output;
+ return L'\"' + displayPath + L'\"';
}
+
+inline std::wstring fmtPath(const Zstring& displayPath) { return fmtPath(utfCvrtTo<std::wstring>(displayPath)); }
+inline std::wstring fmtPath(const wchar_t* displayPath) { return fmtPath(std::wstring(displayPath)); }
}
#endif //FILEERROR_H_INCLUDED_839567308565656789
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 5d7fa8c5..1ea8b1b1 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -9,7 +9,7 @@
#ifdef ZEN_WIN
#include "long_path_prefix.h"
-#include "privilege.h"
+ #include "privilege.h"
#ifdef ZEN_WIN_VISTA_AND_LATER
#include "vista_file_op.h"
#endif
@@ -49,17 +49,28 @@ void checkForUnsupportedType(const Zstring& filepath) //throw FileError
const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & S_IFMT);
return name ? numFmt + L", " + name : numFmt;
};
- throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtFileName(filepath)) + L" " + getTypeName(fileInfo.st_mode));
+ throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtPath(filepath)) + L" " + getTypeName(fileInfo.st_mode));
}
}
#endif
+
+inline
+FileHandle getInvalidHandle()
+{
+#ifdef ZEN_WIN
+ return INVALID_HANDLE_VALUE;
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ return -1;
+#endif
+}
}
FileInput::FileInput(FileHandle handle, const Zstring& filepath) : FileBase(filepath), fileHandle(handle) {}
-FileInput::FileInput(const Zstring& filepath) : FileBase(filepath) //throw FileError, ErrorFileLocked
+FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLocked
+ FileBase(filepath), fileHandle(getInvalidHandle())
{
#ifdef ZEN_WIN
try { activatePrivilege(SE_BACKUP_NAME); }
@@ -97,7 +108,7 @@ FileInput::FileInput(const Zstring& filepath) : FileBase(filepath) //throw FileE
for FFS most comparisons are probably between different disks => let's use FILE_FLAG_SEQUENTIAL_SCAN
*/
- | FILE_FLAG_BACKUP_SEMANTICS,
+ | FILE_FLAG_BACKUP_SEMANTICS,
nullptr); //_In_opt_ HANDLE hTemplateFile
};
fileHandle = createHandle(FILE_SHARE_READ | FILE_SHARE_DELETE);
@@ -111,7 +122,7 @@ FileInput::FileInput(const Zstring& filepath) : FileBase(filepath) //throw FileE
if (fileHandle == INVALID_HANDLE_VALUE)
{
const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filepath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filepath));
std::wstring errorDescr = formatSystemError(L"CreateFile", ec);
if (ec == ERROR_SHARING_VIOLATION || //-> enhance error message!
@@ -134,27 +145,40 @@ FileInput::FileInput(const Zstring& filepath) : FileBase(filepath) //throw FileE
//don't use O_DIRECT: http://yarchive.net/comp/linux/o_direct.html
fileHandle = ::open(filepath.c_str(), O_RDONLY);
if (fileHandle == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle
- throwFileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filepath)), L"open", getLastError());
+ throwFileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filepath)), L"open", getLastError());
+#endif
+ //------------------------------------------------------------------------------------------------------
-#ifdef ZEN_LINUX
+ ScopeGuard constructorGuard = zen::makeGuard([&] //destructor call would lead to member double clean-up!!!
+ {
+#ifdef ZEN_WIN
+ ::CloseHandle(fileHandle);
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ ::close(fileHandle);
+#endif
+ });
+
+#ifdef ZEN_LINUX //handle still un-owned => need constructor guard
//optimize read-ahead on input file:
if (::posix_fadvise(fileHandle, 0, 0, POSIX_FADV_SEQUENTIAL) != 0)
- throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filepath)), L"posix_fadvise", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(filepath)), L"posix_fadvise", getLastError());
#elif defined ZEN_MAC
//"dtruss" doesn't show use of "fcntl() F_RDAHEAD/F_RDADVISE" for "cp")
#endif
-#endif
+
+ constructorGuard.dismiss();
}
FileInput::~FileInput()
{
+ if (fileHandle != getInvalidHandle())
#ifdef ZEN_WIN
- ::CloseHandle(fileHandle);
+ ::CloseHandle(fileHandle);
#elif defined ZEN_LINUX || defined ZEN_MAC
- ::close(fileHandle);
+ ::close(fileHandle);
#endif
}
@@ -173,7 +197,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError; retu
static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead,
&bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead,
nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
- throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilePath())), L"ReadFile", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"ReadFile", getLastError());
#elif defined ZEN_LINUX || defined ZEN_MAC
ssize_t bytesRead = 0;
@@ -184,12 +208,13 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError; retu
while (bytesRead < 0 && errno == EINTR); //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-8.23.tar.xz
if (bytesRead < 0)
- throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilePath())), L"read", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"read", getLastError());
#endif
if (bytesRead == 0) //"zero indicates end of file"
return bytesReadTotal;
- else if (static_cast<size_t>(bytesRead) > bytesToRead) //better safe than sorry
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilePath())), L"ReadFile: buffer overflow."); //user should never see this
+
+ if (static_cast<size_t>(bytesRead) > bytesToRead) //better safe than sorry
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"ReadFile: buffer overflow."); //user should never see this
//if ::read is interrupted (EINTR) right in the middle, it will return successfully with "bytesRead < bytesToRead" => loop!
buffer = static_cast<char*>(buffer) + bytesRead; //suppress warning about pointer arithmetics on void*
@@ -204,7 +229,8 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError; retu
FileOutput::FileOutput(FileHandle handle, const Zstring& filepath) : FileBase(filepath), fileHandle(handle) {}
-FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileBase(filepath) //throw FileError, ErrorTargetExisting
+FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetExisting
+ FileBase(filepath), fileHandle(getInvalidHandle())
{
#ifdef ZEN_WIN
try { activatePrivilege(SE_BACKUP_NAME); }
@@ -229,7 +255,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileBase(fi
dwCreationDisposition, //_In_ DWORD dwCreationDisposition,
dwFlagsAndAttributes |
FILE_FLAG_SEQUENTIAL_SCAN //_In_ DWORD dwFlagsAndAttributes,
- | FILE_FLAG_BACKUP_SEMANTICS,
+ | FILE_FLAG_BACKUP_SEMANTICS,
nullptr); //_In_opt_ HANDLE hTemplateFile
};
@@ -252,7 +278,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileBase(fi
//begin of "regular" error reporting
if (fileHandle == INVALID_HANDLE_VALUE)
{
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filepath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filepath));
std::wstring errorDescr = formatSystemError(L"CreateFile", ec);
#ifdef ZEN_WIN_VISTA_AND_LATER //(try to) enhance error message
@@ -281,7 +307,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileBase(fi
if (fileHandle == -1)
{
const int ec = errno; //copy before making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filepath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filepath));
const std::wstring errorDescr = formatSystemError(L"open", ec);
if (ec == EEXIST)
@@ -291,23 +317,20 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileBase(fi
throw FileError(errorMsg, errorDescr);
}
#endif
-}
+ //------------------------------------------------------------------------------------------------------
-namespace
-{
-inline
-FileHandle getInvalidHandle()
-{
-#ifdef ZEN_WIN
- return INVALID_HANDLE_VALUE;
-#elif defined ZEN_LINUX || defined ZEN_MAC
- return -1;
-#endif
-}
+ //ScopeGuard constructorGuard = zen::makeGuard
+
+ //guard handle when adding code!!!
+
+ //constructorGuard.dismiss();
}
+FileOutput::FileOutput(FileOutput&& tmp) : FileBase(tmp.getFilePath()), fileHandle(tmp.fileHandle) { tmp.fileHandle = getInvalidHandle(); }
+
+
FileOutput::~FileOutput()
{
if (fileHandle != getInvalidHandle())
@@ -322,17 +345,17 @@ FileOutput::~FileOutput()
void FileOutput::close() //throw FileError
{
if (fileHandle == getInvalidHandle())
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"Contract error: close() called more than once.");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"Contract error: close() called more than once.");
ZEN_ON_SCOPE_EXIT(fileHandle = getInvalidHandle());
//no need to clean-up on failure here (just like there is no clean on FileOutput::write failure!) => FileOutput is not transactional!
#ifdef ZEN_WIN
if (!::CloseHandle(fileHandle))
- throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"CloseHandle", getLastError());
+ throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"CloseHandle", getLastError());
#elif defined ZEN_LINUX || defined ZEN_MAC
if (::close(fileHandle) != 0)
- throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"close", getLastError());
+ throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"close", getLastError());
#endif
}
@@ -346,10 +369,10 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite,
&bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten,
nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
- throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"WriteFile", getLastError());
+ throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"WriteFile", getLastError());
if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes!
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"WriteFile: incomplete write."); //user should never see this
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"WriteFile: incomplete write."); //user should never see this
#elif defined ZEN_LINUX || defined ZEN_MAC
while (bytesToWrite > 0)
@@ -366,10 +389,10 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers
errno = ENOSPC;
- throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"write", getLastError());
+ throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write", getLastError());
}
if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"write: buffer overflow."); //user should never see this
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write: buffer overflow."); //user should never see this
//if ::write() is interrupted (EINTR) right in the middle, it will return successfully with "bytesWritten < bytesToWrite"!
buffer = static_cast<const char*>(buffer) + bytesWritten; //suppress warning about pointer arithmetics on void*
diff --git a/zen/file_io.h b/zen/file_io.h
index 648bafe8..52be7f95 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -81,6 +81,8 @@ public:
FileHandle getHandle() { return fileHandle; }
size_t optimalBlockSize() const { return 128 * 1024; }
+ FileOutput(FileOutput&& tmp);
+
private:
FileHandle fileHandle;
};
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 66dcd198..3ef94032 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -47,7 +47,7 @@ void zen::traverseFolder(const Zstring& dirPath,
if (dirExists(dirPath)) //yes, a race-condition, still the best we can do
return;
}
- throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPath)), L"FindFirstFile", lastError);
+ throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"FindFirstFile", lastError);
}
ZEN_ON_SCOPE_EXIT(::FindClose(hDir));
@@ -62,13 +62,13 @@ void zen::traverseFolder(const Zstring& dirPath,
if (lastError == ERROR_NO_MORE_FILES) //not an error situation
return;
//else we have a problem... report it:
- throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"FindNextFile", lastError);
+ throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile", lastError);
}
//skip "." and ".."
const Zchar* const shortName = findData.cFileName;
- if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"FindNextFile: Data corruption, found item without name.");
+ if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item is missing a name.");
if (shortName[0] == L'.' &&
(shortName[1] == 0 || (shortName[1] == L'.' && shortName[2] == 0)))
continue;
@@ -106,14 +106,14 @@ void zen::traverseFolder(const Zstring& dirPath,
DIR* dirObj = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/"
if (!dirObj)
- throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPath)), L"opendir", getLastError());
+ throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir", getLastError());
ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash
for (;;)
{
struct ::dirent* dirEntry = nullptr;
if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
- throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readdir_r", getLastError());
+ throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r", getLastError());
//don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx
if (!dirEntry) //no more items
@@ -122,7 +122,7 @@ void zen::traverseFolder(const Zstring& dirPath,
//don't return "." and ".."
const char* shortName = dirEntry->d_name;
- if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readdir_r: Data corruption, found item without name.");
+ if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item is missing a name.");
if (shortName[0] == '.' &&
(shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0)))
continue;
@@ -152,7 +152,7 @@ void zen::traverseFolder(const Zstring& dirPath,
try
{
if (::lstat(itempath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
- throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(itempath)), L"lstat", getLastError());
+ throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itempath)), L"lstat", getLastError());
}
catch (const FileError& e)
{
diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h
index dfdf60ba..f7ceb5e7 100644
--- a/zen/long_path_prefix.h
+++ b/zen/long_path_prefix.h
@@ -70,7 +70,7 @@ Zstring applyLongPathPrefixImpl(const Zstring& path)
if (!startsWith(path, LONG_PATH_PREFIX))
{
if (startsWith(path, L"\\\\")) //UNC-name, e.g. \\zenju-pc\Users
- return LONG_PATH_PREFIX_UNC + afterFirst(path, L'\\'); //convert to \\?\UNC\zenju-pc\Users
+ return LONG_PATH_PREFIX_UNC + afterFirst(path, L'\\', zen::IF_MISSING_RETURN_NONE); //convert to \\?\UNC\zenju-pc\Users
else
return LONG_PATH_PREFIX + path; //prepend \\?\ prefix
}
diff --git a/zen/perf.h b/zen/perf.h
index e52bf662..6015d97b 100644
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -9,6 +9,7 @@
#include "deprecate.h"
#include "tick_count.h"
+#include "scope_guard.h"
#ifdef ZEN_WIN
#include <sstream>
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index a4f6c128..6cd34a17 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -27,7 +27,7 @@ using namespace zen;
#ifdef ZEN_WIN
-void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::function<void (const Zstring& currentItem)>& onRecycleItem)
+void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::function<void (const std::wstring& displayPath)>& onRecycleItem)
{
if (itempaths.empty()) return;
//warning: moving long file paths to recycler does not work!
@@ -70,7 +70,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func
//"You should use fully-qualified path names with this function. Using it with relative path names is not thread safe."
if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted)
{
- std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1!
+ std::wstring itempathFmt = fmtPath(itempaths[0]); //probably not the correct file name for file lists larger than 1!
if (itempaths.size() > 1)
itempathFmt += L", ..."; //give at least some hint that there are multiple files, and the error need not be related to the first one
throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", itempathFmt));
@@ -97,7 +97,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError
if (!::g_file_trash(file, nullptr, &error))
{
- const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempath));
+ const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itempath));
if (!error)
throw FileError(errorMsg, L"g_file_trash: unknown error."); //user should never see this
@@ -112,7 +112,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError
if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode))
removeFile(itempath); //throw FileError
else if (S_ISDIR(fileInfo.st_mode))
- removeDirectory(itempath); //throw FileError
+ removeDirectoryRecursively(itempath); //throw FileError
return true;
}
@@ -128,7 +128,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError
auto throwFileError = [&](OSStatus oss)
{
- const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempath));
+ const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itempath));
std::wstring errorDescr = L"OSStatus Code " + numberTo<std::wstring>(oss);
if (const char* description = ::GetMacOSStatusCommentString(oss)) //found no documentation for proper use of GetMacOSStatusCommentString
@@ -160,7 +160,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError
if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode))
removeFile(itempath); //throw FileError
else if (S_ISDIR(fileInfo.st_mode))
- removeDirectory(itempath); //throw FileError
+ removeDirectoryRecursively(itempath); //throw FileError
return true;
}
@@ -179,7 +179,7 @@ bool zen::recycleBinExists(const Zstring& dirpath, const std::function<void ()>&
#else
//excessive runtime if recycle bin exists, is full and drive is slow:
- auto ft = async([dirpath]()
+ auto ft = runAsync([dirpath]()
{
SHQUERYRBINFO recInfo = {};
recInfo.cbSize = sizeof(recInfo);
@@ -187,7 +187,7 @@ bool zen::recycleBinExists(const Zstring& dirpath, const std::function<void ()>&
&recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo
});
- while (!ft.timed_wait(boost::posix_time::milliseconds(50)))
+ while (ft.wait_for(boost::chrono::milliseconds(50)) != boost::future_status::ready)
if (onUpdateGui)
onUpdateGui(); //may throw!
diff --git a/zen/recycler.h b/zen/recycler.h
index 3f48452e..61a721cd 100644
--- a/zen/recycler.h
+++ b/zen/recycler.h
@@ -39,7 +39,7 @@ bool recycleOrDelete(const Zstring& itempath); //throw FileError, return "true"
bool recycleBinExists(const Zstring& dirpath, const std::function<void ()>& onUpdateGui); //throw FileError
void recycleOrDelete(const std::vector<Zstring>& filepaths, //throw FileError, return "true" if file/dir was actually deleted
- const std::function<void (const Zstring& currentItem)>& onRecycleItem); //optional; currentItem may be empty
+ const std::function<void (const std::wstring& displayPath)>& onRecycleItem); //optional; currentItem may be empty
#endif
}
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 706b20dc..8477c7ee 100644
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -17,7 +17,7 @@ namespace zen
{
//Scope Guard
/*
- zen::ScopeGuard lockAio = zen::makeGuard([&] { ::CancelIo(hDir); });
+ zen::ScopeGuard lockAio = zen::makeGuard([&] { ::CloseHandle(hDir); });
...
lockAio.dismiss();
*/
diff --git a/zen/shell_execute.h b/zen/shell_execute.h
index 628e957a..b36bc5ea 100644
--- a/zen/shell_execute.h
+++ b/zen/shell_execute.h
@@ -60,14 +60,14 @@ bool shellExecuteImpl(Function fillExecInfo, ExecutionType type)
}
-void shellExecute(const void* /*PCIDLIST_ABSOLUTE*/ shellItemPidl, const Zstring& displayPath, ExecutionType type) //throw FileError
+void shellExecute(const void* /*PCIDLIST_ABSOLUTE*/ shellItemPidl, const std::wstring& displayPath, ExecutionType type) //throw FileError
{
if (!shellExecuteImpl([&](SHELLEXECUTEINFO& execInfo)
{
execInfo.fMask |= SEE_MASK_IDLIST;
execInfo.lpIDList = const_cast<void*>(shellItemPidl); //lpIDList is documented as PCIDLIST_ABSOLUTE!
}, type)) //throw FileError
- throwFileError(_("Incorrect command line:") + L"\n" + fmtFileName(displayPath), L"ShellExecuteEx", ::GetLastError());
+ throwFileError(_("Incorrect command line:") + L"\n" + fmtPath(displayPath), L"ShellExecuteEx", ::GetLastError());
}
#endif
@@ -102,7 +102,7 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
execInfo.lpFile = filepath.c_str();
execInfo.lpParameters = arguments.c_str();
}, type))
- throwFileError(_("Incorrect command line:") + L"\nFile: " + fmtFileName(filepath) + L"\nArg: " + arguments, L"ShellExecuteEx", ::GetLastError());
+ throwFileError(_("Incorrect command line:") + L"\nFile: " + fmtPath(filepath) + L"\nArg: " + copyStringTo<std::wstring>(arguments), L"ShellExecuteEx", ::GetLastError());
#elif defined ZEN_LINUX || defined ZEN_MAC
/*
@@ -117,10 +117,10 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
//Posix::system - execute a shell command
int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect...
if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)"
- throw FileError(_("Incorrect command line:") + L"\n" + command);
+ throw FileError(_("Incorrect command line:") + L"\n" + utfCvrtTo<std::wstring>(command));
}
else
- async([=] { int rv = ::system(command.c_str()); (void)rv; });
+ runAsync([=] { int rv = ::system(command.c_str()); (void)rv; });
#endif
}
}
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index d2d4ee1a..bd76e264 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -9,6 +9,7 @@
#include <memory>
#include <algorithm>
+#include <zen/type_tools.h>
//enhancements for <algorithm>
@@ -66,6 +67,7 @@ std::unique_ptr<T> make_unique(Args&& ... args) { return std::unique_ptr<T>(new
template <class V, class Predicate> inline
void vector_remove_if(V& vec, Predicate p)
{
+ static_assert(IsSameType<typename std::iterator_traits<typename V::iterator>::iterator_category, std::random_access_iterator_tag>::value, "poor man's check for vector");
vec.erase(std::remove_if(vec.begin(), vec.end(), p), vec.end());
}
@@ -87,6 +89,9 @@ void set_append(V& s, const W& s2)
template <class S, class Predicate> inline
void set_remove_if(S& set, Predicate p)
{
+ //function compiles and fails (if we're lucky) not before runtime for std::vector!!!
+ static_assert(!IsSameType<typename std::iterator_traits<typename S::iterator>::iterator_category, std::random_access_iterator_tag>::value, "poor man's check for non-vector");
+
for (auto iter = set.begin(); iter != set.end();)
if (p(*iter))
set.erase(iter++);
diff --git a/zen/string_base.h b/zen/string_base.h
index be3b532e..f4ca5f2e 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -28,8 +28,8 @@ class AllocatorOptimalSpeed //exponential growth + min size
{
public:
//::operator new/ ::operator delete show same performance characterisics like malloc()/free()!
- static void* allocate(size_t size) { return ::operator new(size); } //throw std::bad_alloc
- static void deallocate(void* ptr) { ::operator delete(ptr); }
+ static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
+ static void deallocate(void* ptr) { ::free(ptr); }
static size_t calcCapacity(size_t length) { return std::max<size_t>(16, std::max(length + length / 2, length)); }
//- size_t might overflow! => better catch here than return a too small size covering up the real error: a way too large length!
//- any growth rate should not exceed golden ratio: 1.618033989
@@ -39,8 +39,8 @@ public:
class AllocatorOptimalMemory //no wasted memory, but more reallocations required when manipulating string
{
public:
- static void* allocate(size_t size) { return ::operator new(size); } //throw std::bad_alloc
- static void deallocate(void* ptr) { ::operator delete(ptr); }
+ static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
+ static void deallocate(void* ptr) { ::free(ptr); }
static size_t calcCapacity(size_t length) { return length; }
};
@@ -148,9 +148,21 @@ protected:
return ptr;
}
+#ifdef NDEBUG
void destroy(Char* ptr)
+#else
+ void destroy(Char*& ptr)
+#endif
{
- if (!ptr) return; //support "destroy(nullptr)"
+ assert(ptr != reinterpret_cast<Char*>(0x1)); //detect double-deletion
+
+ if (!ptr) //support "destroy(nullptr)"
+ {
+#ifndef NDEBUG
+ ptr = reinterpret_cast<Char*>(0x1);
+#endif
+ return;
+ }
Descriptor* const d = descr(ptr);
@@ -158,6 +170,9 @@ protected:
{
d->~Descriptor();
this->deallocate(d);
+#ifndef NDEBUG
+ ptr = reinterpret_cast<Char*>(0x1);
+#endif
}
}
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 03094c96..c04adf96 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -30,10 +30,16 @@ template <class S, class T> bool startsWith(const S& str, const T& prefix); //
template <class S, class T> bool endsWith (const S& str, const T& postfix); //both S and T can be strings or char/wchar_t arrays or simple char/wchar_t
template <class S, class T> bool contains (const S& str, const T& term); //
-template <class S, class T> S afterLast (const S& str, const T& term); //returns the whole string if term not found
-template <class S, class T> S beforeLast (const S& str, const T& term); //returns empty string if term not found
-template <class S, class T> S afterFirst (const S& str, const T& term); //returns empty string if term not found
-template <class S, class T> S beforeFirst(const S& str, const T& term); //returns the whole string if term not found
+enum FailureReturnVal
+{
+ IF_MISSING_RETURN_ALL,
+ IF_MISSING_RETURN_NONE
+};
+
+template <class S, class T> S afterLast (const S& str, const T& term, FailureReturnVal rv);
+template <class S, class T> S beforeLast (const S& str, const T& term, FailureReturnVal rv);
+template <class S, class T> S afterFirst (const S& str, const T& term, FailureReturnVal rv);
+template <class S, class T> S beforeFirst(const S& str, const T& term, FailureReturnVal rv);
template <class S, class T> std::vector<S> split(const S& str, const T& delimiter);
template <class S> void trim ( S& str, bool fromLeft = true, bool fromRight = true);
@@ -96,6 +102,7 @@ template <> inline bool isAlpha(wchar_t ch) { return std::iswalpha(ch) != 0; }
template <class S, class T> inline
bool startsWith(const S& str, const T& prefix)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t pfLen = strLength(prefix);
if (strLength(str) < pfLen)
return false;
@@ -109,6 +116,7 @@ bool startsWith(const S& str, const T& prefix)
template <class S, class T> inline
bool endsWith(const S& str, const T& postfix)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t strLen = strLength(str);
const size_t pfLen = strLength(postfix);
if (strLen < pfLen)
@@ -123,6 +131,7 @@ bool endsWith(const S& str, const T& postfix)
template <class S, class T> inline
bool contains(const S& str, const T& term)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t strLen = strLength(str);
const size_t termLen = strLength(term);
if (strLen < termLen)
@@ -137,77 +146,83 @@ bool contains(const S& str, const T& term)
}
-//returns the whole string if term not found
template <class S, class T> inline
-S afterLast(const S& str, const T& term)
+S afterLast(const S& str, const T& term, FailureReturnVal rv)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t termLen = strLength(term);
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- const auto* iter = search_last(strFirst, strLast,
- termFirst, termFirst + termLen);
- if (iter == strLast)
- return str;
+ const auto* it = search_last(strFirst, strLast,
+ termFirst, termFirst + termLen);
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
- iter += termLen;
- return S(iter, strLast - iter);
+ it += termLen;
+ return S(it, strLast - it);
}
-//returns empty string if term not found
template <class S, class T> inline
-S beforeLast(const S& str, const T& term)
+S beforeLast(const S& str, const T& term, FailureReturnVal rv)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- const auto* iter = search_last(strFirst, strLast,
- termFirst, termFirst + strLength(term));
- if (iter == strLast)
- return S();
+ const auto* it = search_last(strFirst, strLast,
+ termFirst, termFirst + strLength(term));
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
- return S(strFirst, iter - strFirst);
+ return S(strFirst, it - strFirst);
}
-//returns empty string if term not found
template <class S, class T> inline
-S afterFirst(const S& str, const T& term)
+S afterFirst(const S& str, const T& term, FailureReturnVal rv)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t termLen = strLength(term);
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- const auto* iter = std::search(strFirst, strLast,
- termFirst, termFirst + termLen);
- if (iter == strLast)
- return S();
- iter += termLen;
+ const auto* it = std::search(strFirst, strLast,
+ termFirst, termFirst + termLen);
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
- return S(iter, strLast - iter);
+ it += termLen;
+ return S(it, strLast - it);
}
-//returns the whole string if term not found
template <class S, class T> inline
-S beforeFirst(const S& str, const T& term)
+S beforeFirst(const S& str, const T& term, FailureReturnVal rv)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const auto* const strFirst = strBegin(str);
+ const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- return S(strFirst, std::search(strFirst, strFirst + strLength(str),
- termFirst, termFirst + strLength(term)) - strFirst);
+ auto it = std::search(strFirst, strLast,
+ termFirst, termFirst + strLength(term));
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
+
+ return S(strFirst, it - strFirst);
}
template <class S, class T> inline
std::vector<S> split(const S& str, const T& delimiter)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
std::vector<S> output;
const size_t delimLen = strLength(delimiter);
@@ -253,6 +268,8 @@ 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)
{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
const size_t oldLen = strLength(oldTerm);
if (oldLen == 0)
{
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 12a7f87c..5f91bdc4 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -38,14 +38,15 @@ class StringRef
{
public:
template <class Iterator>
- StringRef(Iterator first, Iterator last) : length_(last - first), data_(first != last ? &*first : nullptr) {}
+ StringRef(Iterator first, Iterator last) : len_(last - first), str_(first != last ? &*first : nullptr) {}
+ //StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range!
- size_t length() const { return length_; }
- const Char* data() const { return data_; } //1. no null-termination! 2. may be nullptr!
+ const Char* data() const { return str_; } //1. no null-termination! 2. may be nullptr!
+ size_t length() const { return len_; }
private:
- size_t length_;
- const Char* data_;
+ size_t len_;
+ const Char* str_;
};
@@ -98,6 +99,10 @@ struct GetCharTypeImpl<S, true> :
template <> struct GetCharTypeImpl<char, false> : ResultType<char > {};
template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {};
+template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {};
+template <> struct GetCharTypeImpl<StringRef<wchar_t>, false> : ResultType<wchar_t> {};
+
+
ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
ZEN_INIT_DETECT_MEMBER(c_str); //we don't know the exact declaration of the member attribute and it may be in a base class!
ZEN_INIT_DETECT_MEMBER(length); //
@@ -127,28 +132,6 @@ public:
IsSameType<CharType, wchar_t>::value
};
};
-
-
-template <> class StringTraits<StringRef<char>>
-{
-public:
- enum
- {
- isStringClass = false,
- isStringLike = true
- };
- typedef char CharType;
-};
-template <> class StringTraits<StringRef<wchar_t>>
-{
-public:
- enum
- {
- isStringClass = false,
- isStringLike = true
- };
- typedef wchar_t CharType;
-};
}
template <class T>
@@ -174,7 +157,7 @@ size_t cStringLength(const C* str) //naive implementation seems somewhat faster
return len;
}
-template <class S, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline
+template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline
const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string"
{
return str.c_str();
@@ -188,7 +171,7 @@ 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, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline
+template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline
size_t strLength(const S& str) //SFINAE: T must be a "string"
{
return str.length();
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index c895d9a0..c8c8c4be 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -100,7 +100,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, //_In_ DWORD dwFlagsAndAttributes,
nullptr); //_In_opt_ HANDLE hTemplateFile
if (hLink == INVALID_HANDLE_VALUE)
- throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), L"CreateFile", getLastError());
+ throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"CreateFile", getLastError());
ZEN_ON_SCOPE_EXIT(::CloseHandle(hLink));
//respect alignment issues...
@@ -116,7 +116,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
bufferSize, //__in DWORD nOutBufferSize,
&bytesReturned, //__out_opt LPDWORD lpBytesReturned,
nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
- throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), L"DeviceIoControl, FSCTL_GET_REPARSE_POINT", getLastError());
+ throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"DeviceIoControl, FSCTL_GET_REPARSE_POINT", getLastError());
REPARSE_DATA_BUFFER& reparseData = *reinterpret_cast<REPARSE_DATA_BUFFER*>(&buffer[0]); //REPARSE_DATA_BUFFER needs to be artificially enlarged!
@@ -132,7 +132,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
reparseData.MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR));
}
else
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), L"Not a symbolic link or junction.");
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"Not a symbolic link or junction.");
//absolute symlinks and junctions use NT namespace naming convention while relative ones do not:
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#NT_Namespaces
@@ -144,9 +144,9 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE);
if (bytesWritten < 0)
- throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), L"readlink", getLastError());
- if (bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation!
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)));
+ throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink", getLastError());
+ if (bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation, not an error for readlink!
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink: buffer truncated.");
return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination!
#endif
@@ -161,7 +161,7 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
if (!getFinalPathNameByHandle)
- throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
const HANDLE hFile = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName,
@@ -173,12 +173,12 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
nullptr); //_In_opt_ HANDLE hTemplateFile
if (hFile == INVALID_HANDLE_VALUE)
- throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"CreateFile", getLastError());
+ throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"CreateFile", getLastError());
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
const DWORD bufferSize = getFinalPathNameByHandle(hFile, nullptr, 0, 0);
if (bufferSize == 0)
- throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"GetFinalPathNameByHandle", getLastError());
+ throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"GetFinalPathNameByHandle", getLastError());
std::vector<wchar_t> targetPath(bufferSize);
const DWORD charsWritten = getFinalPathNameByHandle(hFile, //__in HANDLE hFile,
@@ -187,7 +187,7 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
0); //__in DWORD dwFlags
if (charsWritten == 0 || charsWritten >= bufferSize)
{
- const std::wstring errorMsg = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath));
if (charsWritten == 0)
throwFileError(errorMsg, L"GetFinalPathNameByHandle", getLastError());
throw FileError(errorMsg);
@@ -198,7 +198,7 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
#elif defined ZEN_LINUX || defined ZEN_MAC
char* targetPath = ::realpath(linkPath.c_str(), nullptr);
if (!targetPath)
- throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"realpath", getLastError());
+ throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"realpath", getLastError());
ZEN_ON_SCOPE_EXIT(::free(targetPath));
return targetPath;
#endif
diff --git a/zen/sys_error.h b/zen/sys_error.h
index 7fb12d31..16e59266 100644
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -46,6 +46,7 @@ private:
std::wstring msg_;
};
+#define DEFINE_NEW_SYS_ERROR(X) struct X : public SysError { X(const std::wstring& msg) : SysError(msg) {} };
@@ -92,7 +93,7 @@ std::wstring formatSystemErrorRaw(ErrorCode ec) //return empty string on error
errorMsg = utfCvrtTo<std::wstring>(::strerror(ec));
#endif
- trim(errorMsg); //Windows messages seem to end with a blank...
+ trim(errorMsg); //Windows messages seem to end with a blank...
return errorMsg;
}
diff --git a/zen/thread.h b/zen/thread.h
index c9b4c76f..6d647de8 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -39,23 +39,23 @@ namespace zen
{
/*
std::async replacement without crappy semantics:
- 1. guaranteed to run asynchronous
+ 1. guaranteed to run asynchronously
2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor
Example:
Zstring dirpath = ...
- auto ft = zen::async([=](){ return zen::dirExists(dirpath); });
- if (ft.timed_wait(boost::posix_time::milliseconds(200)) && ft.get())
+ auto ft = zen::runAsync([=](){ return zen::dirExists(dirpath); });
+ if (ft.wait_for(boost::chrono::milliseconds(200)) == boost::future_status::ready && ft.get())
//dir exising
*/
template <class Function>
-auto async(Function fun) -> boost::unique_future<decltype(fun())>;
+auto runAsync(Function fun) -> boost::unique_future<decltype(fun())>;
//wait for all with a time limit: return true if *all* results are available!
template<class InputIterator, class Duration>
bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration);
-//wait until first job is successful or all failed
+//wait until first job is successful or all failed: substitute until std::when_any is available
template <class T>
class GetFirstResult
{
@@ -73,11 +73,36 @@ public:
private:
class AsyncResult;
- std::shared_ptr<AsyncResult> asyncResult;
+ std::shared_ptr<AsyncResult> asyncResult_;
size_t jobsTotal_;
};
+//value associated with mutex and guaranteed protected access:
+template <class T>
+class Protected
+{
+public:
+ Protected() : value_() {}
+ Protected(const T& value) : value_(value) {}
+
+ template <class Function>
+ void access(Function fun)
+ {
+ boost::lock_guard<boost::mutex> dummy(lockValue);
+ fun(value_);
+ }
+
+private:
+ Protected (const Protected&) = delete;
+ Protected& operator=(const Protected&) = delete;
+
+ boost::mutex lockValue;
+ T value_;
+};
+
+
+
@@ -90,12 +115,12 @@ private:
#endif
template <class Function> inline
-auto async(Function fun) -> boost::unique_future<decltype(fun())>
+auto runAsync(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<ResultType()> 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));
#else
boost::packaged_task<ResultType> pt(std::move(fun));
#endif
@@ -106,11 +131,11 @@ auto async(Function fun) -> boost::unique_future<decltype(fun())>
template<class InputIterator, class Duration> inline
-bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration)
+bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& duration)
{
- const boost::system_time endTime = boost::get_system_time() + wait_duration;
+ const boost::chrono::steady_clock::time_point endTime = boost::chrono::steady_clock::now() + duration;
for (; first != last; ++first)
- if (!first->timed_wait_until(endTime))
+ if (first->wait_until(endTime) != boost::future_status::ready)
return false; //time elapsed
return true;
}
@@ -144,15 +169,13 @@ public:
bool waitForResult(size_t jobsTotal, const Duration& duration)
{
boost::unique_lock<boost::mutex> dummy(lockResult);
- return conditionJobDone.timed_wait(dummy, duration, [&] { return this->jobDone(jobsTotal); });
- //use timed_wait predicate if exitting before condition is reached: http://www.boost.org/doc/libs/1_49_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref.condition_variable.timed_wait_rel
+ return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); }); //throw boost::thread_interrupted
}
std::unique_ptr<T> getResult(size_t jobsTotal)
{
boost::unique_lock<boost::mutex> dummy(lockResult);
- while (!jobDone(jobsTotal))
- conditionJobDone.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point!
+ conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); }); //throw boost::thread_interrupted
#ifndef NDEBUG
assert(!returnedResult);
@@ -177,15 +200,15 @@ private:
template <class T> inline
-GetFirstResult<T>::GetFirstResult() : asyncResult(std::make_shared<AsyncResult>()), jobsTotal_(0) {}
+GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()), jobsTotal_(0) {}
template <class T>
template <class Fun> inline
void GetFirstResult<T>::addJob(Fun f) //f must return a std::unique_ptr<T> containing a value on success
{
- auto asyncResult2 = asyncResult; //capture member variable, not "this"!
- boost::thread t([asyncResult2, f] { asyncResult2->reportFinished(f()); });
+ auto asyncResult = this->asyncResult_; //capture member variable, not "this"!
+ boost::thread t([asyncResult, f] { asyncResult->reportFinished(f()); });
++jobsTotal_;
t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
}
@@ -193,11 +216,11 @@ void GetFirstResult<T>::addJob(Fun f) //f must return a std::unique_ptr<T> conta
template <class T>
template <class Duration> inline
-bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult->waitForResult(jobsTotal_, duration); }
+bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
template <class T> inline
-std::unique_ptr<T> GetFirstResult<T>::get() const { return asyncResult->getResult(jobsTotal_); }
+std::unique_ptr<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
}
#endif //BOOST_THREAD_WRAP_H_78963234
diff --git a/zen/type_tools.h b/zen/type_tools.h
index 95e49769..ac365f05 100644
--- a/zen/type_tools.h
+++ b/zen/type_tools.h
@@ -46,6 +46,9 @@ struct RemoveRef : ResultType<T> {};
template <class T>
struct RemoveRef<T&> : ResultType<T> {};
+
+template <class T>
+struct RemoveRef<T&&> : ResultType<T> {};
//------------------------------------------------------
template <class T>
struct RemoveConst : ResultType<T> {};
diff --git a/zen/win.h b/zen/win.h
index 121e6a9c..13ecf4e4 100644
--- a/zen/win.h
+++ b/zen/win.h
@@ -7,6 +7,10 @@
#ifndef YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H
#define YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H
+#ifndef _WINSOCKAPI_ //prevent inclusion of winsock.h in windows.h: obsoleted by and conflicting with winsock2.h
+ #define _WINSOCKAPI_
+#endif
+
//------------------------------------------------------
#ifdef __WXMSW__ //we have wxWidgets
#include <wx/msw/wrapwin.h> //includes "windows.h"
diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp
index a1bf05d0..5b2cfdca 100644
--- a/zen/xml_io.cpp
+++ b/zen/xml_io.cpp
@@ -28,7 +28,7 @@ XmlDoc zen::loadXmlDocument(const Zstring& filepath) //throw FileError
if (!startsWith(memStreamOut.ref(), xmlBegin) &&
!startsWith(memStreamOut.ref(), BYTE_ORDER_MARK_UTF8 + xmlBegin)) //allow BOM!
- throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filepath)));
+ throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filepath)));
}
copyStream(fileStreamIn, memStreamOut, fileStreamIn.optimalBlockSize(), nullptr); //throw FileError
@@ -41,7 +41,7 @@ XmlDoc zen::loadXmlDocument(const Zstring& filepath) //throw FileError
{
throw FileError(
replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."),
- L"%x", fmtFileName(filepath)),
+ L"%x", fmtPath(filepath)),
L"%y", numberTo<std::wstring>(e.row + 1)),
L"%z", numberTo<std::wstring>(e.col + 1)));
}
@@ -70,10 +70,10 @@ void zen::checkForMappingErrors(const XmlIn& xmlInput, const Zstring& filepath)
{
if (xmlInput.errorsOccured())
{
- std::wstring msg = _("Cannot read the following XML elements:") + L"\n";
+ std::wstring msg = _("The following XML elements could not be read:") + L"\n";
for (const std::wstring& elem : xmlInput.getErrorsAs<std::wstring>())
msg += L"\n" + elem;
- throw FileError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filepath)) + L"\n\n" + msg);
+ throw FileError(replaceCpy(_("Configuration file %x is incomplete. The missing elements will be set to their default values."), L"%x", fmtPath(filepath)) + L"\n\n" + msg);
}
}
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 68934e19..8dcd4736 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -55,6 +55,10 @@ typedef int (WINAPI* CompareStringOrdinalFunc)(LPCWSTR lpString1, int cchCount1,
const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal");
//watch for dependencies in global namespace!!!
//caveat: function scope static initialization is not thread-safe in VS 2010!
+#if defined _MSC_VER && _MSC_VER > 1800
+ #error not true anymore
+#endif
+
}
@@ -148,18 +152,6 @@ Zstring makeUpperCopy(const Zstring& str)
#elif defined ZEN_MAC
-int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen)
-{
- assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
- assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
-
- const int rv = ::strncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent!
- if (rv != 0)
- return rv;
- return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
-}
-
-
Zstring makeUpperCopy(const Zstring& str)
{
const size_t len = str.size();
diff --git a/zen/zstring.h b/zen/zstring.h
index 9822e504..462ea39c 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -14,7 +14,6 @@
#endif
-
#ifdef ZEN_WIN //Windows encodes Unicode as UTF-16 wchar_t
typedef wchar_t Zchar;
#define Zstr(x) L ## x
@@ -27,13 +26,19 @@
#endif
//"The reason for all the fuss above" - Loki/SmartPtr
-//a high-performance string for interfacing with native OS APIs and multithreaded contexts
+//a high-performance string for interfacing with native OS APIs in multithreaded contexts
typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, zen::AllocatorOptimalSpeed> Zstring;
//Compare filepaths: Windows does NOT distinguish between upper/lower-case, while Linux DOES
-int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen);
+int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen);
+
+#if defined ZEN_LINUX || defined ZEN_MAC
+ int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
+#endif
+
+
struct LessFilePath //case-insensitive on Windows, case-sensitive on Linux
@@ -64,11 +69,8 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
inline
Zstring getFileExtension(const Zstring& filePath)
{
- const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR); //returns the whole string if term not found
-
- return contains(shortName, Zchar('.')) ?
- afterLast(filePath, Zchar('.')) :
- Zstring();
+ const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
+ return afterLast(shortName, Zchar('.'), zen::IF_MISSING_RETURN_NONE);
}
@@ -102,14 +104,34 @@ bool pathEndsWith(const S& str, const T& postfix)
//################################# inline implementation ########################################
-#ifdef ZEN_LINUX
+#if defined ZEN_LINUX || defined ZEN_MAC
inline
-int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen)
+int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
{
assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
+#if defined ZEN_LINUX
const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen));
+#elif defined ZEN_MAC
+ const int rv = ::strncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent!
+#endif
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+inline
+int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen)
+{
+ assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
+ assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
+
+#if defined ZEN_LINUX
+ const int rv = std::wcsncmp(lhs, rhs, std::min(lhsLen, rhsLen));
+#elif defined ZEN_MAC
+ const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent!
+#endif
if (rv != 0)
return rv;
return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
bgstack15