summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:24:59 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:24:59 +0200
commita1c91f4695e208d5a8f80dc37b1818169b7829ff (patch)
tree52f5134376d17c99b6c9e53133a2eb5cf171377c /zen
parent5.16 (diff)
downloadFreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.tar.gz
FreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.tar.bz2
FreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.zip
5.17
Diffstat (limited to 'zen')
-rw-r--r--zen/FindFilePlus/FindFilePlus.vcxproj2
-rw-r--r--zen/IFileOperation/FileOperation_Vista.vcxproj14
-rw-r--r--zen/IFileOperation/file_op.cpp171
-rw-r--r--zen/assert_static.h2
-rw-r--r--zen/base64.h5
-rw-r--r--zen/basic_math.h4
-rw-r--r--zen/com_error.h85
-rw-r--r--zen/com_ptr.h2
-rw-r--r--zen/debug_log.h4
-rw-r--r--zen/dir_watcher.cpp98
-rw-r--r--zen/dll.h12
-rw-r--r--zen/dst_hack.cpp21
-rw-r--r--zen/error_log.h10
-rw-r--r--zen/file_error.h21
-rw-r--r--zen/file_handling.cpp472
-rw-r--r--zen/file_handling.h30
-rw-r--r--zen/file_id.cpp8
-rw-r--r--zen/file_id_def.h8
-rw-r--r--zen/file_io.cpp137
-rw-r--r--zen/file_io.h16
-rw-r--r--zen/file_traverser.cpp105
-rw-r--r--zen/file_traverser.h29
-rw-r--r--zen/format_unit.cpp18
-rw-r--r--zen/int64.h8
-rw-r--r--zen/notify_removal.cpp43
-rw-r--r--zen/notify_removal.h1
-rw-r--r--zen/osx_error.h27
-rw-r--r--zen/osx_string.h2
-rw-r--r--zen/osx_throw_exception.h18
-rw-r--r--zen/perf.h6
-rw-r--r--zen/privilege.cpp17
-rw-r--r--zen/process_priority.cpp33
-rw-r--r--zen/recycler.cpp45
-rw-r--r--zen/recycler.h2
-rw-r--r--zen/scope_guard.h2
-rw-r--r--zen/serialize.h2
-rw-r--r--zen/stl_tools.h8
-rw-r--r--zen/string_tools.h60
-rw-r--r--zen/string_traits.h2
-rw-r--r--zen/symlink_target.h60
-rw-r--r--zen/sys_error.h (renamed from zen/last_error.h)61
-rw-r--r--zen/tick_count.h40
-rw-r--r--zen/time.h8
-rw-r--r--zen/type_tools.h2
-rw-r--r--zen/type_traits.h7
-rw-r--r--zen/utf.h2
-rw-r--r--zen/zstring.cpp12
-rw-r--r--zen/zstring.h20
48 files changed, 887 insertions, 875 deletions
diff --git a/zen/FindFilePlus/FindFilePlus.vcxproj b/zen/FindFilePlus/FindFilePlus.vcxproj
index b94174ac..d7dfe219 100644
--- a/zen/FindFilePlus/FindFilePlus.vcxproj
+++ b/zen/FindFilePlus/FindFilePlus.vcxproj
@@ -66,10 +66,8 @@
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
- <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
- <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
diff --git a/zen/IFileOperation/FileOperation_Vista.vcxproj b/zen/IFileOperation/FileOperation_Vista.vcxproj
index 4694a3a5..e5165514 100644
--- a/zen/IFileOperation/FileOperation_Vista.vcxproj
+++ b/zen/IFileOperation/FileOperation_Vista.vcxproj
@@ -67,10 +67,8 @@
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
- <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
- <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
@@ -88,7 +86,7 @@
</BuildLog>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -100,6 +98,7 @@
<DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
<SmallerTypeCheck>true</SmallerTypeCheck>
+ <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -124,7 +123,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -136,6 +135,7 @@
<DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
<SmallerTypeCheck>true</SmallerTypeCheck>
+ <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -158,7 +158,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
@@ -169,6 +169,7 @@
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
+ <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -196,7 +197,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
@@ -207,6 +208,7 @@
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
+ <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp
index 8816b502..218c6f99 100644
--- a/zen/IFileOperation/file_op.cpp
+++ b/zen/IFileOperation/file_op.cpp
@@ -31,13 +31,7 @@ using namespace zen;
namespace
{
-struct Win32Error
-{
- Win32Error(DWORD errorCode) : errorCode_(errorCode) {}
- DWORD errorCode_;
-};
-
-std::vector<std::wstring> getLockingProcesses(const wchar_t* filename); //throw Win32Error
+std::vector<std::wstring> getLockingProcesses(const wchar_t* filename); //throw SysError
class RecyclerProgressCallback : public IFileOperationProgressSink
@@ -167,13 +161,13 @@ private:
};
-void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
+void moveToRecycleBin(const wchar_t* fileNames[], //throw SysError
size_t fileCount,
fileop::RecyclerCallback callback,
void* sink)
{
ComPtr<IFileOperation> fileOp;
- ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw ComError
+ ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw SysError
nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(fileOp.init())));
@@ -196,7 +190,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
ComPtr<RecyclerProgressCallback> opProgress;
*opProgress.init() = new (std::nothrow) RecyclerProgressCallback(callback, sink);
if (!opProgress)
- throw ComError(L"Error creating RecyclerProgressCallback.", E_OUTOFMEMORY);
+ throw SysError(formatComError(L"Error creating RecyclerProgressCallback.", E_OUTOFMEMORY));
DWORD callbackID = 0;
ZEN_COM_CHECK(fileOp->Advise(opProgress.get(), &callbackID));
@@ -214,10 +208,10 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
{
continueExecution = callback(fileNames[i], sink); //should not throw!
}
- catch (...) { throw ComError(L"Unexpected exception in callback.", E_UNEXPECTED); }
+ catch (...) { throw SysError(formatComError(L"Unexpected exception in callback.", E_UNEXPECTED)); }
if (!continueExecution)
- throw ComError(L"Operation cancelled.", HRESULT_FROM_WIN32(ERROR_CANCELLED));
+ throw SysError(formatComError(L"Operation cancelled.", HRESULT_FROM_WIN32(ERROR_CANCELLED)));
}
//create file/folder item object
@@ -235,7 +229,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
if (!somethingExists(fileNames[i]))
continue;
}
- throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + fileNames[i] + L"\'.", hr);
+ throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + fileNames[i] + L"\'.", hr));
}
ZEN_COM_CHECK(fileOp->DeleteItem(psiFile.get(), nullptr));
@@ -251,24 +245,22 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
{
ZEN_COM_CHECK(fileOp->PerformOperations());
}
- catch (const ComError&)
+ catch (const SysError&)
{
//first let's check if we have more detailed error information available
if (const std::pair<std::wstring, HRESULT>* lastError = opProgress->getLastError())
{
- try //create an even better error message if we detect a locking issue:
+ std::vector<std::wstring> processes; //create an even better error message if we detect a locking issue:
+ try { processes = getLockingProcesses(lastError->first.c_str()); /*throw SysError*/ }
+ catch (const SysError&) {}
+
+ if (!processes.empty())
{
- std::vector<std::wstring> processes = getLockingProcesses(lastError->first.c_str()); //throw Win32Error
- if (!processes.empty())
- {
- std::wstring msg = L"The file \'" + lastError->first + L"\' is locked by another process:";
- std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { msg += L'\n'; msg += proc; });
- throw ComError(msg); //message is already descriptive enough, no need to add the HRESULT code
- }
+ std::wstring errorMsg = L"The file \'" + lastError->first + L"\' is locked by another process:";
+ std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { errorMsg += L'\n'; errorMsg += proc; });
+ throw SysError(errorMsg); //message is descriptive enough, no need to evaluate HRESULT!
}
- catch (const Win32Error&) {}
-
- throw ComError(std::wstring(L"Error during \"PerformOperations\" for file:\n") + L"\'" + lastError->first + L"\'.", lastError->second);
+ throw SysError(formatComError(std::wstring(L"Error during \"PerformOperations\" for file:\n") + L"\'" + lastError->first + L"\'.", lastError->second));
}
throw;
}
@@ -278,15 +270,15 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
if (pfAnyOperationsAborted == TRUE)
- throw ComError(L"Operation did not complete successfully.");
+ throw SysError(L"Operation did not complete successfully.");
}
-void copyFile(const wchar_t* sourceFile, //throw ComError
+void copyFile(const wchar_t* sourceFile, //throw SysError
const wchar_t* targetFile)
{
ComPtr<IFileOperation> fileOp;
- ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw ComError
+ ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw SysError
nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(fileOp.init())));
@@ -295,7 +287,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
// from being shown to the user during the
// operation. This includes error, confirmation
// and progress dialogs.
- ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw ComError
+ ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw SysError
FOF_SILENT |
FOFX_EARLYFAILURE |
FOF_NOERRORUI));
@@ -306,12 +298,12 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
nullptr,
IID_PPV_ARGS(psiSourceFile.init()));
if (FAILED(hr))
- throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + sourceFile + L"\'.", hr);
+ throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + sourceFile + L"\'.", hr));
}
const size_t pos = std::wstring(targetFile).find_last_of(L'\\');
if (pos == std::wstring::npos)
- throw ComError(L"Target filename does not contain a path separator.");
+ throw SysError(L"Target filename does not contain a path separator.");
const std::wstring targetFolder(targetFile, pos);
const std::wstring targetFileNameShort = targetFile + pos + 1;
@@ -323,7 +315,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
nullptr,
IID_PPV_ARGS(psiTargetFolder.init()));
if (FAILED(hr))
- throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for folder:\n") + L"\'" + targetFolder + L"\'.", hr);
+ throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for folder:\n") + L"\'" + targetFolder + L"\'.", hr));
}
//schedule file copy operation
@@ -337,14 +329,14 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
if (pfAnyOperationsAborted == TRUE)
- throw ComError(L"Operation did not complete successfully.");
+ throw SysError(L"Operation did not complete successfully.");
}
-void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw ComError
+void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw SysError
{
ComPtr<IShellFolder> desktopFolder;
- ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw ComError
+ ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError
PIDLIST_RELATIVE pidlFolder = nullptr;
ZEN_COM_CHECK(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd,
@@ -358,53 +350,64 @@ void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw ComError
ComPtr<IPersist> persistFolder;
ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl,
nullptr, // [in] IBindCtx *pbc,
- IID_PPV_ARGS(persistFolder.init()))); //throw ComError
+ IID_PPV_ARGS(persistFolder.init()))); //throw SysError
- ZEN_COM_CHECK(persistFolder->GetClassID(&pathCLSID)); //throw ComError
+ ZEN_COM_CHECK(persistFolder->GetClassID(&pathCLSID)); //throw SysError
}
-std::vector<std::wstring> getLockingProcesses(const wchar_t* filename) //throw Win32Error
+std::vector<std::wstring> getLockingProcesses(const wchar_t* filename) //throw SysError
{
- wchar_t sessionKey[CCH_RM_SESSION_KEY + 1] = {}; //fixes two bugs: http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx
DWORD sessionHandle = 0;
- DWORD rv1 = ::RmStartSession(&sessionHandle, //__out DWORD *pSessionHandle,
- 0, //__reserved DWORD dwSessionFlags,
- sessionKey); //__out WCHAR strSessionKey[ ]
- if (rv1 != ERROR_SUCCESS)
- throw Win32Error(rv1);
+ {
+ wchar_t sessionKey[CCH_RM_SESSION_KEY + 1] = {}; //fixes two bugs: http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx
+ DWORD rv1 = ::RmStartSession(&sessionHandle, //__out DWORD *pSessionHandle,
+ 0, //__reserved DWORD dwSessionFlags,
+ sessionKey); //__out WCHAR strSessionKey[ ]
+ if (rv1 != ERROR_SUCCESS)
+ throw SysError(formatSystemError(L"RmStartSession", rv1));
+ }
ZEN_ON_SCOPE_EXIT(::RmEndSession(sessionHandle));
- DWORD rv2 = ::RmRegisterResources(sessionHandle, //__in DWORD dwSessionHandle,
- 1, //__in UINT nFiles,
- &filename, //__in_opt LPCWSTR rgsFilenames[ ],
- 0, //__in UINT nApplications,
- nullptr, //__in_opt RM_UNIQUE_PROCESS rgApplications[ ],
- 0, //__in UINT nServices,
- nullptr); //__in_opt LPCWSTR rgsServiceNames[ ]
- if (rv2 != ERROR_SUCCESS)
- throw Win32Error(rv2);
-
- UINT procInfoSize = 0;
- UINT procInfoSizeNeeded = 0;
- DWORD rebootReasons = 0;
- ::RmGetList(sessionHandle, &procInfoSizeNeeded, &procInfoSize, nullptr, &rebootReasons); //get procInfoSizeNeeded
- //fails with "access denied" for C:\pagefile.sys!
-
- if (procInfoSizeNeeded == 0)
- return std::vector<std::wstring>();
-
- procInfoSize = procInfoSizeNeeded;
- std::vector<RM_PROCESS_INFO> procInfo(procInfoSize);
-
- DWORD rv3 = ::RmGetList(sessionHandle, //__in DWORD dwSessionHandle,
- &procInfoSizeNeeded, //__out UINT *pnProcInfoNeeded,
- &procInfoSize, //__inout UINT *pnProcInfo,
- &procInfo[0], //__inout_opt RM_PROCESS_INFO rgAffectedApps[ ],
- &rebootReasons); //__out LPDWORD lpdwRebootReasons
- if (rv3 != ERROR_SUCCESS)
- throw Win32Error(rv3);
- procInfo.resize(procInfoSize);
+ {
+ DWORD rv2 = ::RmRegisterResources(sessionHandle, //__in DWORD dwSessionHandle,
+ 1, //__in UINT nFiles,
+ &filename, //__in_opt LPCWSTR rgsFilenames[ ],
+ 0, //__in UINT nApplications,
+ nullptr, //__in_opt RM_UNIQUE_PROCESS rgApplications[ ],
+ 0, //__in UINT nServices,
+ nullptr); //__in_opt LPCWSTR rgsServiceNames[ ]
+ if (rv2 != ERROR_SUCCESS)
+ throw SysError(formatSystemError(L"RmRegisterResources", rv2));
+ }
+
+ std::vector<RM_PROCESS_INFO> procInfo;
+ {
+ UINT procInfoSize = 0;
+ UINT procInfoSizeNeeded = 0;
+ DWORD rebootReasons = 0;
+ DWORD rv3 = ::RmGetList(sessionHandle, &procInfoSizeNeeded, &procInfoSize, nullptr, &rebootReasons); //get procInfoSizeNeeded
+ if (rv3 == ERROR_SUCCESS)
+ return std::vector<std::wstring>();
+ if (rv3 != ERROR_MORE_DATA)
+ throw SysError(formatSystemError(L"RmGetList", rv3));
+ //C:\pagefile.sys fails with ERROR_SHARING_VIOLATION!
+
+ if (procInfoSizeNeeded == 0)
+ return std::vector<std::wstring>();
+
+ procInfoSize = procInfoSizeNeeded;
+ procInfo.resize(procInfoSizeNeeded);
+
+ rv3 = ::RmGetList(sessionHandle, //__in DWORD dwSessionHandle,
+ &procInfoSizeNeeded, //__out UINT *pnProcInfoNeeded,
+ &procInfoSize, //__inout UINT *pnProcInfo,
+ &procInfo[0], //__inout_opt RM_PROCESS_INFO rgAffectedApps[ ],
+ &rebootReasons); //__out LPDWORD lpdwRebootReasons
+ if (rv3 != ERROR_SUCCESS)
+ throw SysError(formatSystemError(L"RmGetList", rv3));
+ procInfo.resize(procInfoSize);
+ }
std::vector<std::wstring> output;
for (auto iter = procInfo.begin(); iter != procInfo.end(); ++iter)
@@ -437,7 +440,7 @@ std::vector<std::wstring> getLockingProcesses(const wchar_t* filename) //throw W
&buffer[0], //__out LPTSTR lpExeName,
&bufferSize)) //__inout PDWORD lpdwSize
if (bufferSize < buffer.size())
- processName += std::wstring(L" - ") + L"\'" + &buffer[0] + L"\'";
+ processName += std::wstring(L", ") + L"\'" + &buffer[0] + L"\'";
}
}
output.push_back(processName);
@@ -457,10 +460,10 @@ bool fileop::moveToRecycleBin(const wchar_t* fileNames[],
{
try
{
- ::moveToRecycleBin(fileNames, fileCount, callback, sink); //throw ComError
+ ::moveToRecycleBin(fileNames, fileCount, callback, sink); //throw SysError
return true;
}
- catch (const ComError& e)
+ catch (const SysError& e)
{
lastErrorMessage.reset(new std::wstring(e.toString()));
return false;
@@ -473,10 +476,10 @@ bool fileop::copyFile(const wchar_t* sourceFile,
{
try
{
- ::copyFile(sourceFile, targetFile); //throw ComError
+ ::copyFile(sourceFile, targetFile); //throw SysError
return true;
}
- catch (const ComError& e)
+ catch (const SysError& e)
{
lastErrorMessage.reset(new std::wstring(e.toString()));
return false;
@@ -489,11 +492,11 @@ bool fileop::checkRecycler(const wchar_t* dirname, bool& isRecycler)
try
{
CLSID clsid = {};
- getFolderClsid(dirname, clsid); //throw ComError
+ getFolderClsid(dirname, clsid); //throw SysError
isRecycler = ::IsEqualCLSID(clsid, CLSID_RecycleBin) == TRUE; //silence perf warning
return true;
}
- catch (const ComError& e)
+ catch (const SysError& e)
{
lastErrorMessage.reset(new std::wstring(e.toString()));
return false;
@@ -511,7 +514,7 @@ bool fileop::getLockingProcesses(const wchar_t* filename, const wchar_t*& procLi
{
try
{
- std::vector<std::wstring> processes = ::getLockingProcesses(filename); //throw Win32Error
+ std::vector<std::wstring> processes = ::getLockingProcesses(filename); //throw SysError
std::wstring buffer;
std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { buffer += proc; buffer += L'\n'; });
@@ -524,9 +527,9 @@ bool fileop::getLockingProcesses(const wchar_t* filename, const wchar_t*& procLi
return true;
}
- catch (const Win32Error& e)
+ catch (const SysError& e)
{
- lastErrorMessage.reset(new std::wstring(formatWin32Msg(e.errorCode_)));
+ lastErrorMessage.reset(new std::wstring(e.toString()));
return false;
}
}
diff --git a/zen/assert_static.h b/zen/assert_static.h
index c5b7f8c1..469e0299 100644
--- a/zen/assert_static.h
+++ b/zen/assert_static.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
diff --git a/zen/base64.h b/zen/base64.h
index 5f18ea36..ae896e70 100644
--- a/zen/base64.h
+++ b/zen/base64.h
@@ -1,8 +1,9 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
-// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
+
#ifndef BASE64_HEADER_08473021856321840873021487213453214
#define BASE64_HEADER_08473021856321840873021487213453214
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 89d9d6c0..f01421c7 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -1,6 +1,6 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
-// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
diff --git a/zen/com_error.h b/zen/com_error.h
index 0e0448a7..5eb7611d 100644
--- a/zen/com_error.h
+++ b/zen/com_error.h
@@ -4,31 +4,19 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef COM_ERROR_HEADER
-#define COM_ERROR_HEADER
+#ifndef COM_ERROR_HEADER_88425703425254
+#define COM_ERROR_HEADER_88425703425254
-#include <string>
#include <cstdio>
-#include "win.h" //includes "windows.h"
+#include "sys_error.h"
namespace zen
{
-std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr);
-std::wstring formatWin32Msg(DWORD dwMessageId); //return empty string on error
-
-class ComError
-{
-public:
- explicit ComError(const std::wstring& msg, HRESULT hr = S_OK) : msg_(hr == S_OK ? msg : generateErrorMsg(msg, hr)) {}
- const std::wstring& toString() const { return msg_; }
-
-private:
- std::wstring msg_;
-};
+std::wstring formatComError(const std::wstring& msg, HRESULT hr);
//Convenience Macros checking for COM errors:
-#define ZEN_COM_CHECK(func) ZEN_COM_CHECK_IMPL(func, #func) //throw ComError
+#define ZEN_COM_CHECK(func) ZEN_COM_CHECK_IMPL(func, #func) //throw SysError
/*
Example: ZEN_COM_CHECK(backupComp->InitializeForBackup());
@@ -36,17 +24,17 @@ Equivalent to:
{
HRESULT hrInternal = backupComp->InitializeForBackup();
if (FAILED(hrInternal))
- throw ComError(L"Error calling \"backupComp->InitializeForBackup()\".", hrInternal);
+ throw SysError(formatComError(L"Error calling \"backupComp->InitializeForBackup()\".", hrInternal));
}
*/
-#define ZEN_COM_ASSERT(obj) ZEN_COM_ASSERT_IMPL(obj, #obj) //throw ComError
+#define ZEN_COM_ASSERT(obj) ZEN_COM_ASSERT_IMPL(obj, #obj) //throw SysError
/*
Example: ZEN_COM_ASSERT(obj);
Equivalent to:
if (!obj)
- throw ComError(L"Assertion failed: \"obj\".", E_FAIL);
+ throw SysError(formatComError(L"Assertion failed: \"obj\".", E_FAIL));
*/
@@ -54,32 +42,35 @@ Equivalent to:
-
-
-
-
-
-
-
//################# implementation #####################
-std::wstring formatWin32Msg(DWORD dwMessageId) //return empty string on error
+namespace impl
+{
+inline
+std::wstring formatWin32Message(DWORD dwMessageId) //return empty string on error
{
- std::wstring output;
LPWSTR buffer = nullptr;
if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_MAX_WIDTH_MASK |
FORMAT_MESSAGE_IGNORE_INSERTS | //important: without this flag ::FormatMessage() will fail if message contains placeholders
FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, dwMessageId, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0)
- {
if (buffer) //just to be sure
{
- output = buffer;
- ::LocalFree(buffer);
+ ZEN_ON_SCOPE_EXIT(::LocalFree(buffer));
+ return buffer;
}
- }
- return output;
+ return std::wstring();
+}
+
+
+inline
+std::wstring numberToHexString(long number)
+{
+ wchar_t buffer[100] = {};
+ const int charsWritten = ::swprintf(buffer, 100, L"0x%08x", static_cast<int>(number));
+ return charsWritten > 0 ? std::wstring(buffer, charsWritten) : std::wstring();
}
+
namespace
{
std::wstring formatFacility(HRESULT hr)
@@ -199,28 +190,24 @@ std::wstring formatFacility(HRESULT hr)
}
}
}
-
-inline
-std::wstring numberToHexString(long number)
-{
- wchar_t result[100];
- ::swprintf(result, 100, L"0x%08x", static_cast<int>(number));
- return std::wstring(result);
}
+std::wstring formatComError(const std::wstring& msg, long long hr); //not implemented! intentional overload ambiguity to catch usage errors with HRESULT!
+
+
inline
-std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr)
+std::wstring formatComError(const std::wstring& msg, HRESULT hr)
{
- std::wstring output(input);
+ std::wstring output(msg);
output += L"\n";
//don't use _com_error(hr).ErrorMessage(): internally this is nothing more than a call to ::FormatMessage()
- std::wstring win32Msg = formatWin32Msg(hr);
+ std::wstring win32Msg = impl::formatWin32Message(hr);
if (!win32Msg.empty()) //empty string on error
- output += win32Msg + L"\n" + L"HRESULT: " + numberToHexString(hr);
+ output += win32Msg + L"\n" + L"HRESULT: " + impl::numberToHexString(hr);
else
- output += L"HRESULT: " + numberToHexString(hr) + L", " + L"Facility: " + formatFacility(hr);
+ output += L"HRESULT: " + impl::numberToHexString(hr) + L", " + L"Facility: " + impl::formatFacility(hr);
//don't bluntly interpret as Win32 error code HRESULT_CODE(hr), too often misleading!
//http://blogs.msdn.com/b/oldnewthing/archive/2006/11/03/942851.aspx
@@ -232,9 +219,9 @@ std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr)
{ \
HRESULT hrInternal = func; \
if (FAILED(hrInternal)) \
- throw zen::ComError(std::wstring(L"Error calling \"") + L ## txt + L"\".", hrInternal); \
+ throw zen::SysError(formatComError(std::wstring(L"Error calling \"") + L ## txt + L"\".", hrInternal)); \
}
-#define ZEN_COM_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::ComError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".", E_FAIL);
+#define ZEN_COM_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(formatComError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".", E_FAIL));
}
-#endif //COM_ERROR_HEADER
+#endif //COM_ERROR_HEADER_88425703425254
diff --git a/zen/com_ptr.h b/zen/com_ptr.h
index 9944ea56..6fb9f2cd 100644
--- a/zen/com_ptr.h
+++ b/zen/com_ptr.h
@@ -36,7 +36,7 @@ class ComPtr
public:
ComPtr() : ptr(nullptr) {} //
ComPtr(const ComPtr& other) : ptr(other.ptr) { if (ptr) ptr->AddRef(); } //noexcept in C++11
- ComPtr( ComPtr&& other) : ptr(other.release()) {} //
+ ComPtr( ComPtr&& tmp ) : ptr(tmp.release()) {} //
~ComPtr() { if (ptr) ptr->Release(); } //has exception spec of compiler-generated destructor by default
ComPtr& operator=(const ComPtr& other) { ComPtr(other).swap(*this); return *this; } //noexcept in C++11
diff --git a/zen/debug_log.h b/zen/debug_log.h
index 6ee44ff5..ad27b66e 100644
--- a/zen/debug_log.h
+++ b/zen/debug_log.h
@@ -23,9 +23,9 @@
namespace zen
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const char ZEN_FILE_NAME_SEPARATOR = '\\';
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
const char ZEN_FILE_NAME_SEPARATOR = '/';
#endif
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 6402dce7..a800fb80 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -10,24 +10,24 @@
#include "thread.h" //includes <boost/thread.hpp>
#include "scope_guard.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "notify_removal.h"
#include "win.h" //includes "windows.h"
#include "long_path_prefix.h"
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
#include <sys/inotify.h>
#include <fcntl.h>
#include "file_traverser.h"
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
//#include <CoreFoundation/FSEvents.h>
#endif
using namespace zen;
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
namespace
{
class SharedData
@@ -101,14 +101,14 @@ public:
boost::lock_guard<boost::mutex> dummy(lockAccess);
//first check whether errors occurred in thread
- if (!errorMsg.first.empty())
+ if (errorInfo)
{
- const std::wstring msg = errorMsg.first.c_str();
- const DWORD lastError = errorMsg.second;
+ const std::wstring msg = copyStringTo<std::wstring>(errorInfo->msg);
+ const std::wstring descr = copyStringTo<std::wstring>(errorInfo->descr);
- if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(msg);
- throw FileError(msg);
+ if (errorCodeForNotExisting(errorInfo->errorCode))
+ throw ErrorNotExisting(msg, descr);
+ throw FileError(msg, descr);
}
output.swap(changedFiles);
@@ -117,10 +117,12 @@ public:
//context of worker thread
- void reportError(const std::wstring& msg, DWORD errorCode) //throw()
+ void reportError(const std::wstring& msg, const std::wstring& description, DWORD errorCode) //throw()
{
boost::lock_guard<boost::mutex> dummy(lockAccess);
- errorMsg = std::make_pair(copyStringTo<BasicWString>(msg), errorCode);
+
+ ErrorInfo newInfo = { copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode };
+ errorInfo = make_unique<ErrorInfo>(newInfo);
}
private:
@@ -128,7 +130,14 @@ private:
boost::mutex lockAccess;
std::vector<DirWatcher::Entry> changedFiles;
- std::pair<BasicWString, DWORD> errorMsg; //non-empty if errors occurred in thread
+
+ struct ErrorInfo
+ {
+ BasicWString msg;
+ BasicWString descr;
+ DWORD errorCode;
+ };
+ std::unique_ptr<ErrorInfo> errorInfo; //non-empty if errors occurred in thread
};
@@ -151,11 +160,13 @@ public:
nullptr);
if (hDir == INVALID_HANDLE_VALUE)
{
- const DWORD lastError = ::GetLastError();
- const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError);
+ const DWORD lastError = ::GetLastError(); //copy before making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory));
+ const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError);
+
if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(errorMsg);
- throw FileError(errorMsg);
+ throw ErrorNotExisting(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
}
//end of constructor, no need to start managing "hDir"
@@ -184,7 +195,10 @@ public:
false, //__in BOOL bInitialState,
nullptr); //__in_opt LPCTSTR lpName
if (overlapped.hEvent == nullptr)
- return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)) + L" (CreateEvent)" L"\n\n" + getLastErrorFormatted(), ::GetLastError());
+ {
+ const DWORD lastError = ::GetLastError(); //copy before making other system calls!
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"CreateEvent", lastError), lastError);
+ }
ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent));
@@ -202,7 +216,10 @@ public:
&bytesReturned, // __out_opt LPDWORD lpBytesReturned,
&overlapped, // __inout_opt LPOVERLAPPED lpOverlapped,
nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
- return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)) + L" (ReadDirectoryChangesW)" L"\n\n" + getLastErrorFormatted(), ::GetLastError());
+ {
+ const DWORD lastError = ::GetLastError(); //copy before making other system calls!
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"ReadDirectoryChangesW", lastError), lastError);
+ }
//async I/O is a resource that needs to be guarded since it will write to local variable "buffer"!
zen::ScopeGuard guardAio = zen::makeGuard([&]
@@ -223,8 +240,9 @@ public:
&bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred,
false)) //__in BOOL bWait
{
- if (::GetLastError() != ERROR_IO_INCOMPLETE)
- return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)) + L" (GetOverlappedResult)" L"\n\n" + getLastErrorFormatted(), ::GetLastError());
+ const DWORD lastError = ::GetLastError(); //copy before making other system calls!
+ if (lastError != ERROR_IO_INCOMPLETE)
+ return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"GetOverlappedResult", lastError), lastError);
//execute asynchronous procedure calls (APC) queued on this thread
::SleepEx(50, // __in DWORD dwMilliseconds,
@@ -362,7 +380,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
}
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
struct DirWatcher::Pimpl
{
Zstring dirname;
@@ -376,21 +394,19 @@ namespace
class DirsOnlyTraverser : public zen::TraverseCallback
{
public:
- DirsOnlyTraverser(std::vector<Zstring>& dirs,
- const std::shared_ptr<TraverseCallback>& otherMe) : otherMe_(otherMe), dirs_(dirs) {}
+ DirsOnlyTraverser(std::vector<Zstring>& dirs) : dirs_(dirs) {}
virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) {}
virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; }
- virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName)
+ virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName)
{
dirs_.push_back(fullName);
- return otherMe_;
+ return this;
}
virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); }
virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); }
private:
- const std::shared_ptr<TraverseCallback>& otherMe_; //lifetime management, two options: 1. use std::weak_ptr 2. ref to shared_ptr
std::vector<Zstring>& dirs_;
};
}
@@ -408,16 +424,14 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
std::vector<Zstring> fullDirList;
fullDirList.push_back(dirname);
- std::shared_ptr<TraverseCallback> traverser;
- traverser = std::make_shared<DirsOnlyTraverser>(fullDirList, traverser); //throw FileError
-
- zen::traverseFolder(dirname, *traverser); //don't traverse into symlinks (analog to windows build)
+ DirsOnlyTraverser traverser(fullDirList); //throw FileError
+ zen::traverseFolder(dirname, traverser); //don't traverse into symlinks (analog to windows build)
//init
pimpl_->dirname = directory;
pimpl_->notifDescr = ::inotify_init();
if (pimpl_->notifDescr == -1)
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"inotify_init", getLastError()));
zen::ScopeGuard guardDescr = zen::makeGuard([&] { ::close(pimpl_->notifDescr); });
@@ -429,7 +443,7 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1;
}
if (!initSuccess)
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"fcntl", getLastError()));
//add watches
std::for_each(fullDirList.begin(), fullDirList.end(),
@@ -448,10 +462,13 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
IN_MOVE_SELF);
if (wd == -1)
{
- const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)) + L"\n\n" + getLastErrorFormatted();
- if (errorCodeForNotExisting(errno))
- throw ErrorNotExisting(errorMsg);
- throw FileError(errorMsg);
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir));
+ const std::wstring errorDescr = formatSystemError(L"inotify_add_watch", lastError);
+
+ if (errorCodeForNotExisting(lastError))
+ throw ErrorNotExisting(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
}
pimpl_->watchDescrs.insert(std::make_pair(wd, appendSeparator(subdir)));
@@ -484,7 +501,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>();
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(pimpl_->dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(pimpl_->dirname)), formatSystemError(L"read", getLastError()));
}
std::vector<Entry> output;
@@ -522,18 +539,17 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
return output;
}
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
warn_static("finish")
struct DirWatcher::Pimpl
{
-
};
DirWatcher::DirWatcher(const Zstring& directory) //throw FileError
{
-
+ throw FileError(L"Dir Watcher is not yet implemented!");
}
diff --git a/zen/dll.h b/zen/dll.h
index 2c39ac44..e1f3f0ad 100644
--- a/zen/dll.h
+++ b/zen/dll.h
@@ -8,12 +8,12 @@
#define DLLLOADER_H_INCLUDED
#include <memory>
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include <string>
#include "scope_guard.h"
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#include <dlfcn.h>
#endif
@@ -40,11 +40,11 @@ class DllFun
public:
DllFun() : fun(nullptr) {}
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
DllFun(const wchar_t* libraryName, const char* functionName) :
hLibRef(::LoadLibrary(libraryName), ::FreeLibrary),
fun(hLibRef ? reinterpret_cast<Func>(::GetProcAddress(static_cast<HMODULE>(hLibRef.get()), functionName)) : nullptr) {}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
DllFun(const char* libraryName, const char* functionName) :
hLibRef(::dlopen(libraryName, RTLD_LAZY), ::dlclose),
fun(hLibRef ? reinterpret_cast<Func>(::dlsym(hLibRef.get(), functionName)) : nullptr) {}
@@ -57,7 +57,7 @@ private:
};
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//if the dll is already part of the process space, e.g. "kernel32.dll" or "shell32.dll", we can use a faster variant:
//NOTE: since the lifetime of the referenced library is *not* controlled, this is safe to use only for permanently loaded libraries like these!
template <class Func>
@@ -100,7 +100,7 @@ std::string getResourceStream(const std::wstring& libraryName, size_t resourceId
//--------------- implementation---------------------------------------------------
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
inline
std::string getResourceStream(const wchar_t* libraryName, size_t resourceId)
{
diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp
index a70ef13b..887b9b63 100644
--- a/zen/dst_hack.cpp
+++ b/zen/dst_hack.cpp
@@ -166,10 +166,10 @@ FILETIME utcToLocal(const FILETIME& utcTime) //throw std::runtime_error
&utcTime, //__in const FILETIME *lpFileTime,
&localTime)) //__out LPFILETIME lpLocalFileTime
{
- const std::wstring errorMessage = _("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" +
- L"High: " + numberTo<std::wstring>(utcTime.dwHighDateTime) + L" " +
- L"Low: " + numberTo<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted();
- throw std::runtime_error(utfCvrtTo<std::string>(errorMessage));
+ const std::wstring errorMsg = _("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" +
+ L"High: " + numberTo<std::wstring>(utcTime.dwHighDateTime) + L" " +
+ L"Low: " + numberTo<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"FileTimeToLocalFileTime", getLastError());
+ throw std::runtime_error(utfCvrtTo<std::string>(errorMsg));
}
return localTime;
}
@@ -184,10 +184,10 @@ FILETIME localToUtc(const FILETIME& localTime) //throw std::runtime_error
&localTime, //__in const FILETIME *lpLocalFileTime,
&utcTime)) //__out LPFILETIME lpFileTime
{
- const std::wstring errorMessage = _("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" +
- L"High: " + numberTo<std::wstring>(localTime.dwHighDateTime) + L" " +
- L"Low: " + numberTo<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted();
- throw std::runtime_error(utfCvrtTo<std::string>(errorMessage));
+ const std::wstring errorMsg = _("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" +
+ L"High: " + numberTo<std::wstring>(localTime.dwHighDateTime) + L" " +
+ L"Low: " + numberTo<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"LocalFileTimeToFileTime", getLastError());
+ throw std::runtime_error(utfCvrtTo<std::string>(errorMsg));
}
return utcTime;
}
@@ -286,9 +286,8 @@ std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift()
if (std::bitset < UTC_LOCAL_OFFSET_BITS - 1 > (absValue).to_ulong() != static_cast<unsigned long>(absValue) || //time shifts that big shouldn't be possible!
timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity!
{
- const std::wstring errorMessage = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " +
- L"(" + numberTo<std::wstring>(timeShiftSec) + L") " + L"\n\n" + getLastErrorFormatted();
- throw std::runtime_error(utfCvrtTo<std::string>(errorMessage));
+ const std::wstring errorMsg = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + L"(" + numberTo<std::wstring>(timeShiftSec) + L")";
+ throw std::runtime_error(utfCvrtTo<std::string>(errorMsg));
}
std::bitset<UTC_LOCAL_OFFSET_BITS> output(absValue);
diff --git a/zen/error_log.h b/zen/error_log.h
index 490bb1f4..05049d27 100644
--- a/zen/error_log.h
+++ b/zen/error_log.h
@@ -105,8 +105,8 @@ String formatMessageImpl(const LogEntry& entry) //internal linkage
String formattedText = L"[" + formatTime<String>(FORMAT_TIME, localTime(entry.time)) + L"] " + copyStringTo<String>(getTypeName()) + L": ";
const size_t prefixLen = formattedText.size();
- for (auto iter = entry.message.begin(); iter != entry.message.end(); )
- if (*iter == L'\n')
+ for (auto it = entry.message.begin(); it != entry.message.end(); )
+ if (*it == L'\n')
{
formattedText += L'\n';
@@ -116,12 +116,12 @@ String formatMessageImpl(const LogEntry& entry) //internal linkage
do //skip duplicate newlines
{
- ++iter;
+ ++it;
}
- while (iter != entry.message.end() && *iter == L'\n');
+ while (it != entry.message.end() && *it == L'\n');
}
else
- formattedText += *iter++;
+ formattedText += *it++;
return formattedText;
}
diff --git a/zen/file_error.h b/zen/file_error.h
index f107a736..71619834 100644
--- a/zen/file_error.h
+++ b/zen/file_error.h
@@ -4,29 +4,31 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef FILEERROR_H_INCLUDED
-#define FILEERROR_H_INCLUDED
+#ifndef FILEERROR_H_INCLUDED_839567308565656789
+#define FILEERROR_H_INCLUDED_839567308565656789
#include <string>
#include "zstring.h"
#include "utf.h"
-#include "last_error.h" //we'll need this later anyway!
+#include "sys_error.h" //we'll need this later anyway!
namespace zen
{
-class FileError //Exception base class used to notify file/directory copy/delete errors
+//A high-level exception class giving detailed context information for end users
+class FileError
{
public:
- explicit FileError(const std::wstring& message) : errorMessage(message) {}
+ explicit FileError(const std::wstring& msg) : msg_(msg) {}
+ FileError(const std::wstring& msg, const std::wstring& details) : msg_(msg + L"\n\n" + details) {}
virtual ~FileError() {}
- const std::wstring& toString() const { return errorMessage; }
+ const std::wstring& toString() const { return msg_; }
private:
- std::wstring errorMessage;
+ std::wstring msg_;
};
-#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& message) : FileError(message) {} };
+#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} };
DEFINE_NEW_FILE_ERROR(ErrorNotExisting);
DEFINE_NEW_FILE_ERROR(ErrorTargetExisting);
@@ -35,7 +37,6 @@ DEFINE_NEW_FILE_ERROR(ErrorFileLocked);
DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume);
-
//----------- 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
@@ -56,4 +57,4 @@ std::wstring fmtFileName(const Zstring& filename)
}
}
-#endif // FILEERROR_H_INCLUDED
+#endif //FILEERROR_H_INCLUDED_839567308565656789
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index 8dc3e72d..398e88e8 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -16,7 +16,7 @@
#include "file_id_def.h"
#include <boost/thread/tss.hpp>
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include <Aclapi.h>
#include "privilege.h"
#include "dll.h"
@@ -26,19 +26,19 @@
#include "win_ver.h"
#include "IFileOperation/file_op.h"
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
#include <sys/vfs.h> //statfs
#include <fcntl.h> //AT_SYMLINK_NOFOLLOW, UTIME_OMIT
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
#include <sys/mount.h> //statfs
//#include <utime.h>
#endif
-#if defined FFS_LINUX || defined FFS_MAC
+#if defined ZEN_LINUX || defined ZEN_MAC
#include <sys/stat.h>
#include <sys/time.h> //lutimes
#endif
@@ -49,11 +49,11 @@ using namespace zen;
bool zen::fileExists(const Zstring& filename)
{
//symbolic links (broken or not) are also treated as existing files!
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
return ::lstat(filename.c_str(), &fileInfo) == 0 &&
(S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)); //in Linux a symbolic link is neither file nor directory
@@ -64,11 +64,11 @@ bool zen::fileExists(const Zstring& filename)
bool zen::dirExists(const Zstring& dirname)
{
//symbolic links (broken or not) are also treated as existing directories!
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat dirInfo = {};
return ::lstat(dirname.c_str(), &dirInfo) == 0 &&
(S_ISLNK(dirInfo.st_mode) || S_ISDIR(dirInfo.st_mode)); //in Linux a symbolic link is neither file nor directory
@@ -78,7 +78,7 @@ bool zen::dirExists(const Zstring& dirname)
bool zen::symlinkExists(const Zstring& linkname)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
WIN32_FIND_DATA fileInfo = {};
{
const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &fileInfo);
@@ -88,7 +88,7 @@ bool zen::symlinkExists(const Zstring& linkname)
}
return isSymlink(fileInfo);
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
return ::lstat(linkname.c_str(), &fileInfo) == 0 &&
S_ISLNK(fileInfo.st_mode); //symbolic link
@@ -98,11 +98,11 @@ bool zen::symlinkExists(const Zstring& linkname)
bool zen::somethingExists(const Zstring& objname)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
return attr != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
return ::lstat(objname.c_str(), &fileInfo) == 0;
#endif
@@ -112,13 +112,13 @@ bool zen::somethingExists(const Zstring& objname)
SymLinkType zen::getSymlinkType(const Zstring& linkname) //throw()
{
assert(symlinkExists(linkname));
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(linkname).c_str());
if (attr == INVALID_FILE_ATTRIBUTES)
return SYMLINK_TYPE_UNKNOWN;
return (attr & FILE_ATTRIBUTE_DIRECTORY) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
//S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link
struct ::stat fileInfo = {};
if (::stat(linkname.c_str(), &fileInfo) != 0)
@@ -130,14 +130,39 @@ SymLinkType zen::getSymlinkType(const Zstring& linkname) //throw()
namespace
{
+#ifdef ZEN_WIN
+//(try to) enhance error messages by showing which processes lock the file
+Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred
+{
+ if (vistaOrLater())
+ {
+ using namespace fileop;
+ const DllFun<FunType_getLockingProcesses> getLockingProcesses(getDllName(), funName_getLockingProcesses);
+ const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString);
+
+ if (getLockingProcesses && freeString)
+ {
+ const wchar_t* procList = nullptr;
+ if (getLockingProcesses(filename.c_str(), procList))
+ {
+ ZEN_ON_SCOPE_EXIT(freeString(procList));
+ return procList;
+ }
+ }
+ }
+ return Zstring();
+}
+#endif
+
+
void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl) //throw FileError
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
WIN32_FIND_DATA fileInfo = {};
{
const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo);
if (searchHandle == INVALID_HANDLE_VALUE)
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"FindFirstFile", getLastError()));
::FindClose(searchHandle);
}
// WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {};
@@ -173,25 +198,25 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl
FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
nullptr);
if (hFile == INVALID_HANDLE_VALUE)
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"CreateFile", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
BY_HANDLE_FILE_INFORMATION fileInfoHnd = {};
if (!::GetFileInformationByHandle(hFile, &fileInfoHnd))
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"GetFileInformationByHandle", getLastError()));
attr.fileSize = UInt64(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh);
attr.modificationTime = toTimeT(fileInfoHnd.ftLastWriteTime);
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
const int rv = procSl == SYMLINK_FOLLOW ?
:: stat(filename.c_str(), &fileInfo) :
::lstat(filename.c_str(), &fileInfo);
if (rv != 0) //follow symbolic links
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"stat", getLastError()));
attr.fileSize = UInt64(fileInfo.st_size);
attr.modificationTime = fileInfo.st_mtime;
@@ -218,119 +243,34 @@ Int64 zen::getFileTime(const Zstring& filename, ProcSymlink procSl) //throw File
UInt64 zen::getFreeDiskSpace(const Zstring& path) //throw FileError
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
ULARGE_INTEGER bytesFree = {};
if (!::GetDiskFreeSpaceEx(appendSeparator(path).c_str(), //__in_opt LPCTSTR lpDirectoryName, -> "UNC name [...] must include a trailing backslash, for example, "\\MyServer\MyShare\"
&bytesFree, //__out_opt PULARGE_INTEGER lpFreeBytesAvailable,
nullptr, //__out_opt PULARGE_INTEGER lpTotalNumberOfBytes,
nullptr)) //__out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), formatSystemError(L"GetDiskFreeSpaceEx", getLastError()));
return UInt64(bytesFree.LowPart, bytesFree.HighPart);
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct statfs info = {};
if (::statfs(path.c_str(), &info) != 0)
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), formatSystemError(L"statfs", getLastError()));
return UInt64(info.f_bsize) * info.f_bavail;
#endif
}
-namespace
-{
-#ifdef FFS_WIN
-//(try to) enhance error messages by showing which processes lock the file
-Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred
-{
- if (vistaOrLater())
- {
- using namespace fileop;
- const DllFun<FunType_getLockingProcesses> getLockingProcesses(getDllName(), funName_getLockingProcesses);
- const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString);
-
- if (getLockingProcesses && freeString)
- {
- const wchar_t* procList = nullptr;
- if (getLockingProcesses(filename.c_str(), procList))
- {
- ZEN_ON_SCOPE_EXIT(freeString(procList));
- return procList;
- }
- }
- }
- return Zstring();
-}
-
-
-DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
-{
- //note: this even works for network shares: \\share\dirname
-
- const DWORD bufferSize = 10000;
- std::vector<wchar_t> buffer(bufferSize);
-
- //full pathName need not yet exist!
- if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
- &buffer[0], //__out LPTSTR lpszVolumePathName,
- bufferSize)) //__in DWORD cchBufferLength
- return 0;
-
- DWORD volumeSerial = 0;
- if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName,
- nullptr, //__out LPTSTR lpVolumeNameBuffer,
- 0, //__in DWORD nVolumeNameSize,
- &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber,
- nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
- nullptr, //__out_opt LPDWORD lpFileSystemFlags,
- nullptr, //__out LPTSTR lpFileSystemNameBuffer,
- 0)) //__in DWORD nFileSystemNameSize
- return 0;
-
- return volumeSerial;
-}
-
-#elif defined FFS_LINUX || defined FFS_MAC
-dev_t retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
-{
- Zstring volumePathName = pathName;
-
- //remove trailing slash
- if (volumePathName.size() > 1 && endsWith(volumePathName, FILE_NAME_SEPARATOR)) //exception: allow '/'
- volumePathName = beforeLast(volumePathName, FILE_NAME_SEPARATOR);
-
- struct stat fileInfo = {};
- while (::lstat(volumePathName.c_str(), &fileInfo) != 0) //go up in folder hierarchy until existing folder is found
- {
- volumePathName = beforeLast(volumePathName, FILE_NAME_SEPARATOR); //returns empty string if ch not found
- if (volumePathName.empty())
- return 0; //this includes path "/" also!
- }
-
- return fileInfo.st_dev;
-}
-#endif
-}
-
-
-zen::ResponseSame zen::onSameVolume(const Zstring& folderLeft, const Zstring& folderRight) //throw()
-{
- const auto serialLeft = retrieveVolumeSerial(folderLeft); //returns 0 on error!
- const auto serialRight = retrieveVolumeSerial(folderRight); //returns 0 on error!
- if (serialLeft == 0 || serialRight == 0)
- return IS_SAME_CANT_SAY;
-
- return serialLeft == serialRight ? IS_SAME_YES : IS_SAME_NO;
-}
-
-
bool zen::removeFile(const Zstring& filename) //throw FileError
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"DeleteFile";
const Zstring& filenameFmt = applyLongPathPrefix(filename);
if (!::DeleteFile(filenameFmt.c_str()))
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"unlink";
if (::unlink(filename.c_str()) != 0)
#endif
{
@@ -338,7 +278,7 @@ bool zen::removeFile(const Zstring& filename) //throw FileError
if (errorCodeForNotExisting(lastError)) //no error situation if file is not existing! manual deletion relies on it!
return false;
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only
{
::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes
@@ -353,18 +293,19 @@ bool zen::removeFile(const Zstring& filename) //throw FileError
return false; //neither file nor any other object (e.g. broken symlink) with that name existing
//begin of "regular" error reporting
- const std::wstring shortMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filename));
+ const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filename));
+ std::wstring errorDescr = formatSystemError(functionName, lastError);
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message!
lastError == ERROR_LOCK_VIOLATION)
{
const Zstring procList = getLockingProcessNames(filename); //throw()
if (!procList.empty())
- throw FileError(shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList);
+ errorDescr = _("The file is locked by another process:") + L"\n" + procList;
}
#endif
- throw FileError(shortMsg + L"\n\n" + getLastErrorFormatted(lastError));
+ throw FileError(errorMsg, errorDescr);
}
return true;
}
@@ -382,7 +323,7 @@ namespace
//wrapper for file system rename function:
void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const Zstring oldNameFmt = applyLongPathPrefix(oldName);
const Zstring newNameFmt = applyLongPathPrefix(newName);
@@ -392,16 +333,6 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File
{
DWORD lastError = ::GetLastError();
- const std::wstring shortMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName));
-
- if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message!
- lastError == ERROR_LOCK_VIOLATION)
- {
- const Zstring procList = getLockingProcessNames(oldName); //throw()
- if (!procList.empty())
- throw FileError(shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList);
- }
-
if (lastError == ERROR_ACCESS_DENIED) //MoveFileEx may fail to rename a read-only file on a SAMBA-share -> (try to) handle this
{
const DWORD oldAttr = ::GetFileAttributes(oldNameFmt.c_str());
@@ -421,7 +352,6 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File
else
{
lastError = ::GetLastError(); //use error code from second call to ::MoveFileEx()
-
//cleanup: (try to) restore file attributes: assume oldName is still existing
::SetFileAttributes(oldNameFmt.c_str(), oldAttr);
}
@@ -429,37 +359,45 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File
}
}
- std::wstring errorMessage = shortMsg + L"\n\n" + getLastErrorFormatted(lastError);
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName));
+ std::wstring errorDescr = formatSystemError(L"MoveFileEx", lastError);
- if (lastError == ERROR_NOT_SAME_DEVICE)
- throw ErrorDifferentVolume(errorMessage);
+ if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message!
+ lastError == ERROR_LOCK_VIOLATION)
+ {
+ const Zstring procList = getLockingProcessNames(oldName); //throw()
+ if (!procList.empty())
+ errorDescr = _("The file is locked by another process:") + L"\n" + procList;
+ }
+ if (lastError == ERROR_NOT_SAME_DEVICE)
+ throw ErrorDifferentVolume(errorMsg, errorDescr);
else if (lastError == ERROR_ALREADY_EXISTS || //-> used on Win7 x64
lastError == ERROR_FILE_EXISTS) //-> used by XP???
- throw ErrorTargetExisting(errorMessage);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
else
- throw FileError(errorMessage);
+ throw FileError(errorMsg, errorDescr);
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
if (::rename(oldName.c_str(), newName.c_str()) != 0)
{
- const int lastError = errno;
- std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)) +
- L"\n\n" + getLastErrorFormatted(lastError);
+ const int lastError = errno; //copy before making other system calls!
+ std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName));
+ const std::wstring errorDescr = formatSystemError(L"rename", lastError);
if (lastError == EXDEV)
- throw ErrorDifferentVolume(errorMessage);
+ throw ErrorDifferentVolume(errorMsg, errorDescr);
else if (lastError == EEXIST)
- throw ErrorTargetExisting(errorMessage);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
else
- throw FileError(errorMessage);
+ throw FileError(errorMsg, errorDescr);
}
#endif
}
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
/*small wrapper around
::GetShortPathName()
::GetLongPathName() */
@@ -474,10 +412,10 @@ Zstring getFilenameFmt(const Zstring& filename, Function fun) //throw(); returns
std::vector<wchar_t> buffer(bufferSize);
- const DWORD rv = fun(filenameFmt.c_str(), //__in LPCTSTR lpszShortPath,
- &buffer[0], //__out LPTSTR lpszLongPath,
- bufferSize); //__in DWORD cchBuffer
- if (rv == 0 || rv >= bufferSize)
+ const DWORD charsWritten = fun(filenameFmt.c_str(), //__in LPCTSTR lpszShortPath,
+ &buffer[0], //__out LPTSTR lpszLongPath,
+ bufferSize); //__in DWORD cchBuffer
+ if (charsWritten == 0 || charsWritten >= bufferSize)
return Zstring();
return &buffer[0];
@@ -572,7 +510,7 @@ void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw Fil
}
catch (const ErrorTargetExisting&)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//try to handle issues with already existing short 8.3 file names on Windows
if (have8dot3NameClash(newName))
{
@@ -615,7 +553,7 @@ public:
}
return LINK_SKIP;
}
- virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName)
+ virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName)
{
dirs_.push_back(fullName);
return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively!
@@ -636,7 +574,7 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback)
{
assert(somethingExists(directory)); //[!]
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix
//(try to) normalize file attributes: actually NEEDED for symbolic links also!
@@ -647,12 +585,14 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback)
if (symlinkExists(directory)) //remove symlink directly
{
if (callback) callback->onBeforeDirDeletion(directory); //once per symlink
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"RemoveDirectory";
if (!::RemoveDirectory(directoryFmt.c_str()))
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"unlink";
if (::unlink(directory.c_str()) != 0)
#endif
- throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), formatSystemError(functionName, getLastError()));
}
else
{
@@ -681,12 +621,14 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback)
//parent directory is deleted last
if (callback) callback->onBeforeDirDeletion(directory); //and once per folder
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"RemoveDirectory";
if (!::RemoveDirectory(directoryFmt.c_str()))
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"rmdir";
if (::rmdir(directory.c_str()) != 0)
#endif
- throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), formatSystemError(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
@@ -708,7 +650,7 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback)
void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink procSl) //throw FileError
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
FILETIME creationTime = {};
FILETIME lastWriteTime = tofiletime(modTime);
@@ -751,18 +693,18 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
::SetFileAttributes(applyLongPathPrefix(filename).c_str(), attribs);
);
- auto removeReadonly = [&]() -> bool //may need to remove the readonly-attribute (e.g. on FAT usb drives)
+ 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(filename).c_str());
if (tmpAttr == INVALID_FILE_ATTRIBUTES)
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"GetFileAttributes", getLastError()));
if (tmpAttr & FILE_ATTRIBUTE_READONLY)
{
if (!::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_NORMAL))
- throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"SetFileAttributes", getLastError()));
attribs = tmpAttr; //reapplied on scope exit
return true;
@@ -799,19 +741,20 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
if (hFile == INVALID_HANDLE_VALUE)
{
if (::GetLastError() == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons)
- if (removeReadonly())
+ if (removeReadonly()) //throw FileError
continue;
//2. be a *little* fancy
hFile = openFile(false);
if (hFile == INVALID_HANDLE_VALUE)
{
- if (::GetLastError() == ERROR_ACCESS_DENIED)
- if (removeReadonly())
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
+ if (lastError == ERROR_ACCESS_DENIED)
+ if (removeReadonly()) //throw FileError
continue;
//3. after these herculean stunts we give up...
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"CreateFile", lastError));
}
}
break;
@@ -826,15 +769,15 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
nullptr, //__in_opt const FILETIME *lpLastAccessTime,
&lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime
{
- auto lastErr = ::GetLastError();
+ ErrorCode lastError = getLastError(); //copy before making other system calls!
//function may fail if file is read-only: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430
- if (lastErr == ERROR_ACCESS_DENIED)
+ if (lastError == ERROR_ACCESS_DENIED)
{
//dynamically load windows API function: available with Windows Vista and later
typedef BOOL (WINAPI* SetFileInformationByHandleFunc)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize);
-
const SysDllFun<SetFileInformationByHandleFunc> setFileInformationByHandle(L"kernel32.dll", "SetFileInformationByHandle");
+
if (setFileInformationByHandle) //if not: let the original error propagate!
{
auto setFileInfo = [&](FILE_BASIC_INFO basicInfo) //throw FileError; no const& since SetFileInformationByHandle() requires non-const parameter!
@@ -843,7 +786,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
&basicInfo, //__in LPVOID lpFileInformation,
sizeof(basicInfo))) //__in DWORD dwBufferSize
- throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"SetFileInformationByHandle", getLastError()));
};
auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER
@@ -876,15 +819,16 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
}
catch (FileError&) {}
- lastErr = ERROR_SUCCESS;
+ lastError = ERROR_SUCCESS;
}
}
}
- std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastErr);
+ std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename));
+ const std::wstring errorDescr = formatSystemError(L"SetFileTime", lastError);
//add more meaningful message: FAT accepts only a subset of the NTFS date range
- if (lastErr == ERROR_INVALID_PARAMETER &&
+ if (lastError == ERROR_INVALID_PARAMETER &&
dst::isFatDrive(filename))
{
//we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!!
@@ -929,8 +873,8 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
(!isNullTime(creationTime) ? L"\n\tcreate (UTC): \t" + fmtDate(creationTime) : L"");
}
- if (lastErr != ERROR_SUCCESS)
- throw FileError(errorMsg);
+ if (lastError != ERROR_SUCCESS)
+ throw FileError(errorMsg, errorDescr);
}
}
#ifndef NDEBUG //dst hack: verify data written
@@ -959,7 +903,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
}
#endif
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
//sigh, we can't use utimensat on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit???
// struct ::timespec newTimes[2] = {};
@@ -967,7 +911,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
// newTimes[1].tv_sec = to<time_t>(modTime); //modification time (seconds)
//
// if (::utimensat(AT_FDCWD, filename.c_str(), newTimes, procSl == SYMLINK_DIRECT ? AT_SYMLINK_NOFOLLOW : 0) != 0)
- // throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ // throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"utimensat", getLastError()));
//=> fallback to "retarded-idiot version"! -- DarkByte
@@ -979,20 +923,20 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
:: utimes(filename.c_str(), newTimes) : //utimensat() not yet implemented on OS X
::lutimes(filename.c_str(), newTimes);
if (rv != 0)
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"utimes", getLastError()));
#endif
}
bool zen::supportsPermissions(const Zstring& dirname) //throw FileError
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const DWORD bufferSize = MAX_PATH + 1;
std::vector<wchar_t> buffer(bufferSize);
if (!::GetVolumePathName(dirname.c_str(), //__in LPCTSTR lpszFileName,
&buffer[0], //__out LPTSTR lpszVolumePathName,
bufferSize)) //__in DWORD cchBufferLength
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"GetVolumePathName", getLastError()));
DWORD fsFlags = 0;
if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName,
@@ -1003,11 +947,11 @@ bool zen::supportsPermissions(const Zstring& dirname) //throw FileError
&fsFlags, //__out_opt LPDWORD lpFileSystemFlags,
nullptr, //__out LPTSTR lpFileSystemNameBuffer,
0)) //__in DWORD nFileSystemNameSize
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"GetVolumeInformation", getLastError()));
return (fsFlags & FILE_PERSISTENT_ACLS) != 0;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
return true;
#endif
}
@@ -1029,7 +973,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem
return;
- throw FileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtFileName(source)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtFileName(source)), formatSystemError(L"getfilecon", getLastError()));
}
ZEN_ON_SCOPE_EXIT(::freecon(contextSource));
@@ -1057,7 +1001,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
::setfilecon(target.c_str(), contextSource) :
::lsetfilecon(target.c_str(), contextSource);
if (rv3 < 0)
- throw FileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtFileName(target)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtFileName(target)), formatSystemError(L"setfilecon", getLastError()));
}
#endif //HAVE_SELINUX
@@ -1065,7 +1009,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
//copy permissions for files, directories or symbolic links: requires admin rights
void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags
//CAVEAT: if a file system does not support ACLs, GetFileSecurity() will return successfully with a *valid* security descriptor containing *no* ACL entries!
@@ -1089,7 +1033,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
}
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)) + L"\n\n" + e.toString());
+ throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), e.toString());
}
@@ -1108,7 +1052,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
if (bytesNeeded > buffer.size())
buffer.resize(bytesNeeded);
else
- throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), formatSystemError(L"GetFileSecurity", getLastError()));
}
SECURITY_DESCRIPTOR& secDescr = reinterpret_cast<SECURITY_DESCRIPTOR&>(buffer[0]);
@@ -1132,7 +1076,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInformation,
&secDescr)) //__in PSECURITY_DESCRIPTOR pSecurityDescriptor
- throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetResolved)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetResolved)), formatSystemError(L"SetFileSecurity", getLastError()));
/*
PSECURITY_DESCRIPTOR buffer = nullptr;
@@ -1214,7 +1158,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
throw FileError
*/
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#ifdef HAVE_SELINUX //copy SELinux security context
copySecurityContext(source, target, procSl); //throw FileError
@@ -1224,20 +1168,24 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
if (procSl == SYMLINK_FOLLOW)
{
if (::stat(source.c_str(), &fileInfo) != 0)
- throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), formatSystemError(L"stat", getLastError()));
+
+ if (::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
+ throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"chown", getLastError()));
- if (::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights!
- ::chmod(target.c_str(), fileInfo.st_mode) != 0)
- throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)) + L"\n\n" + getLastErrorFormatted());
+ if (::chmod(target.c_str(), fileInfo.st_mode) != 0)
+ throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"chmod", getLastError()));
}
else
{
if (::lstat(source.c_str(), &fileInfo) != 0)
- throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), formatSystemError(L"lstat", getLastError()));
- if (::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights!
- (!symlinkExists(target) && ::chmod(target.c_str(), fileInfo.st_mode) != 0)) //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
- throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)) + L"\n\n" + getLastErrorFormatted());
+ if (::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
+ throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"lchown", getLastError()));
+
+ if (!symlinkExists(target) && ::chmod(target.c_str(), fileInfo.st_mode) != 0) //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
+ throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"chmod", getLastError()));
}
#endif
}
@@ -1315,7 +1263,7 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
const Zstring& templateDir,
bool copyFilePermissions)
{
-#ifdef FFS_WIN
+#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(directory, FILE_NAME_SEPARATOR) ?
beforeLast(directory, FILE_NAME_SEPARATOR) :
@@ -1327,10 +1275,12 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
const ErrorCode lastError = dirExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND;
- const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp)) + L"\n\n" + getLastErrorFormatted(lastError);
+ const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp));
+ const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError);
+
if (lastError == ERROR_ALREADY_EXISTS)
- throw ErrorTargetExisting(msg);
- throw FileError(msg); //[!] this is NOT a ErrorTargetPathMissing case!
+ throw ErrorTargetExisting(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr); //[!] this is NOT a ErrorTargetPathMissing case!
}
//don't use ::CreateDirectoryEx:
@@ -1357,32 +1307,35 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
if (lastError != ERROR_SUCCESS)
{
- const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError);
+ const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory));
+ const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError);
+
if (lastError == ERROR_ALREADY_EXISTS)
- throw ErrorTargetExisting(msg);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
else if (lastError == ERROR_PATH_NOT_FOUND)
- throw ErrorTargetPathMissing(msg);
- throw FileError(msg);
+ throw ErrorTargetPathMissing(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
}
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
if (::mkdir(directory.c_str(), 0755) != 0) //mode: drwxr-xr-x
{
- const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted();
const ErrorCode lastError = getLastError();
+ const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory));
+ const std::wstring errorDescr = formatSystemError(L"mkdir", lastError);
if (lastError == EEXIST)
- throw ErrorTargetExisting(msg);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
else if (lastError == ENOENT)
- throw ErrorTargetPathMissing(msg);
- throw FileError(msg);
+ throw ErrorTargetPathMissing(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
}
#endif
if (!templateDir.empty())
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//try to copy file attributes (dereference symlinks and junctions)
const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(),
0,
@@ -1451,7 +1404,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
{
const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const bool isDirLink = [&]() -> bool
{
const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(sourceLink).c_str());
@@ -1463,22 +1416,24 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW");
if (!createSymbolicLink)
- throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\""));
+ throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\""));
+ const wchar_t functionName[] = L"CreateSymbolicLinkW";
if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required...
linkPath.c_str(), //__in LPTSTR lpTargetFileName,
(isDirLink ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"symlink";
if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
#endif
- throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), formatSystemError(functionName, getLastError()));
//allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist
- zen::ScopeGuard guardNewDir = zen::makeGuard([&]
+ zen::ScopeGuard guardNewLink = zen::makeGuard([&]
{
try
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
if (isDirLink)
removeDirectory(targetLink);
else
@@ -1497,13 +1452,13 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
if (copyFilePermissions)
copyObjectPermissions(sourceLink, targetLink, SYMLINK_DIRECT); //throw FileError
- guardNewDir.dismiss(); //target has been created successfully!
+ guardNewLink.dismiss(); //target has been created successfully!
}
namespace
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
/*
CopyFileEx() BackupRead() FileRead()
--------------------------------------------
@@ -1626,24 +1581,27 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
{
const DWORD lastError = ::GetLastError();
- const std::wstring shortMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile));
+ const std::wstring errorMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile));
+ std::wstring errorDescr = formatSystemError(L"CreateFile", lastError);
//if file is locked throw "ErrorFileLocked" instead!
if (lastError == ERROR_SHARING_VIOLATION ||
lastError == ERROR_LOCK_VIOLATION)
{
const Zstring procList = getLockingProcessNames(sourceFile); //throw()
- throw ErrorFileLocked(shortMsg + L"\n\n" + (!procList.empty() ? _("The file is locked by another process:") + L"\n" + procList : getLastErrorFormatted(lastError)));
+ if (!procList.empty())
+ errorDescr = _("The file is locked by another process:") + L"\n" + procList;
+ throw ErrorFileLocked(errorMsg, errorDescr);
}
- throw FileError(shortMsg + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)");
+ throw FileError(errorMsg, errorDescr);
}
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileSource));
//----------------------------------------------------------------------
BY_HANDLE_FILE_INFORMATION fileInfoSource = {};
if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource))
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), formatSystemError(L"GetFileInformationByHandle", getLastError()));
//----------------------------------------------------------------------
const DWORD validAttribs = FILE_ATTRIBUTE_NORMAL | //"This attribute is valid only if used alone."
@@ -1665,17 +1623,18 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
nullptr);
if (hFileTarget == INVALID_HANDLE_VALUE)
{
- const DWORD lastError = ::GetLastError();
- const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)";
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile));
+ const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError);
if (lastError == ERROR_FILE_EXISTS || //confirmed to be used
lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6
- throw ErrorTargetExisting(errorMessage);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
if (lastError == ERROR_PATH_NOT_FOUND)
- throw ErrorTargetPathMissing(errorMessage);
+ throw ErrorTargetPathMissing(errorMsg, errorDescr);
- throw FileError(errorMessage);
+ throw FileError(errorMsg, errorDescr);
}
ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: guard just after opening target and before managing hFileOut
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget));
@@ -1683,7 +1642,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
//----------------------------------------------------------------------
BY_HANDLE_FILE_INFORMATION fileInfoTarget = {};
if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget))
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"GetFileInformationByHandle", getLastError()));
//return up-to-date file attributes
if (newAttrib)
@@ -1732,8 +1691,8 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
0, //OutBufferSize
&bytesReturned, //number of bytes returned
nullptr)) //OVERLAPPED structure
- throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)) +
- L"\n\n" + zen::getLastErrorFormatted() + L" (NTFS sparse)");
+ throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)),
+ formatSystemError(L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError()));
}
//----------------------------------------------------------------------
@@ -1760,10 +1719,10 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
false, //__in BOOL bAbort,
false, //__in BOOL bProcessSecurity,
&contextRead)) //__out LPVOID *lpContext
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted()); //better use fine-granular error messages "reading/writing"!
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), formatSystemError(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"\n\n" + L"(buffer overflow)");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"buffer overflow"); //user should never see this
if (bytesRead < BUFFER_SIZE)
eof = true;
@@ -1776,10 +1735,10 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
false, //__in BOOL bAbort,
false, //__in BOOL bProcessSecurity,
&contextWrite)) //__out LPVOID *lpContext
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"BackupWrite", getLastError()));
if (bytesWritten != bytesRead)
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + L"(incomplete write)");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"incomplete write"); //user should never see this
//total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)!
@@ -1797,14 +1756,14 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
//::BackupRead() silently fails reading encrypted files -> double check!
if (!someBytesWritten && UInt64(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"\n\n" + L"(unknown error)");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"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))
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"SetFileTime", getLastError()));
guardTarget.dismiss();
@@ -1854,7 +1813,7 @@ public:
void reportUserException(CallbackCopyFile& userCallback) { exceptionInUserCallback = &userCallback; }
- void reportError(const std::wstring& message) { errorMsg = message; }
+ void reportError(const std::wstring& msg, const std::wstring& description) { errorMsg = std::make_pair(msg, description); }
//call context: copyFileWindowsDefault()
void evaluateErrors() //throw X
@@ -1865,14 +1824,14 @@ public:
if (exceptionInUserCallback)
exceptionInUserCallback->updateCopyStatus(0); //rethrow (hopefully!)
- if (!errorMsg.empty())
- throw FileError(errorMsg);
+ if (!errorMsg.first.empty())
+ throw FileError(errorMsg.first, errorMsg.second);
}
private:
- bool shouldCopyAsSparse; //
- std::wstring errorMsg; //these are exclusive!
- CallbackCopyFile* exceptionInUserCallback; //
+ bool shouldCopyAsSparse; //
+ std::pair<std::wstring, std::wstring> errorMsg; //these are exclusive!
+ CallbackCopyFile* exceptionInUserCallback; //
};
@@ -1935,14 +1894,14 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
BY_HANDLE_FILE_INFORMATION fileInfoSrc = {};
if (!::GetFileInformationByHandle(hSourceFile, &fileInfoSrc))
{
- cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.sourceFile_)) + L"\n\n" + getLastErrorFormatted());
+ cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.sourceFile_)), formatSystemError(L"GetFileInformationByHandle", getLastError()));
return PROGRESS_CANCEL;
}
BY_HANDLE_FILE_INFORMATION fileInfoTrg = {};
if (!::GetFileInformationByHandle(hDestinationFile, &fileInfoTrg))
{
- cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.targetFile_)) + L"\n\n" + getLastErrorFormatted());
+ cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.targetFile_)), formatSystemError(L"GetFileInformationByHandle", getLastError()));
return PROGRESS_CANCEL;
}
@@ -2055,16 +2014,17 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
throw ErrorShouldCopyAsSparse(L"sparse dummy value2");
//assemble error message...
- std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)) +
- L"\n\n" + getLastErrorFormatted(lastError);
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile));
+ std::wstring errorDescr = formatSystemError(L"CopyFileEx", lastError);
//if file is locked throw "ErrorFileLocked" instead!
if (lastError == ERROR_SHARING_VIOLATION ||
lastError == ERROR_LOCK_VIOLATION)
{
const Zstring procList = getLockingProcessNames(sourceFile); //throw() -> enhance error message!
- throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" +
- (!procList.empty() ? _("The file is locked by another process:") + L"\n" + procList : getLastErrorFormatted(lastError)));
+ if (!procList.empty())
+ errorDescr = _("The file is locked by another process:") + L"\n" + procList;
+ throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), errorDescr);
}
//if target is existing this functions is expected to throw ErrorTargetExisting!!!
@@ -2072,13 +2032,13 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
lastError == ERROR_ALREADY_EXISTS) //not sure if used -> better be safe than sorry!!!
{
guardTarget.dismiss(); //don't delete file that existed previously!
- throw ErrorTargetExisting(errorMessage);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
}
if (lastError == ERROR_PATH_NOT_FOUND)
{
guardTarget.dismiss(); //not relevant
- throw ErrorTargetPathMissing(errorMessage);
+ throw ErrorTargetPathMissing(errorMsg, errorDescr);
}
try //add more meaningful message
@@ -2087,13 +2047,13 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
if (lastError == ERROR_INVALID_PARAMETER &&
dst::isFatDrive(targetFile) &&
getFilesize(sourceFile) >= 4U * UInt64(1024U * 1024 * 1024)) //throw FileError
- errorMessage += L"\nFAT volume cannot store files larger than 4 gigabyte!";
+ errorDescr += L"\nFAT volume cannot store files larger than 4 gigabyte!";
//note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filename is of a restricted type.
}
catch (...) {}
- throw FileError(errorMessage);
+ throw FileError(errorMsg, errorDescr);
}
if (newAttrib)
@@ -2152,7 +2112,7 @@ void copyFileWindows(const Zstring& sourceFile, const Zstring& targetFile, Callb
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
void copyFileLinuxMac(const Zstring& sourceFile,
const Zstring& targetFile,
CallbackCopyFile* callback,
@@ -2163,7 +2123,7 @@ void copyFileLinuxMac(const Zstring& sourceFile,
struct ::stat sourceInfo = {};
if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), formatSystemError(L"fstat", getLastError()));
zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: place guard before lifetime of FileOutput
try
@@ -2190,7 +2150,7 @@ void copyFileLinuxMac(const Zstring& sourceFile,
//read and return file statistics
struct ::stat targetInfo = {};
if (::fstat(fileOut.getDescriptor(), &targetInfo) != 0)
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"fstat", getLastError()));
if (newAttrib)
{
@@ -2249,10 +2209,10 @@ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::Backu
inline
void copyFileSelectOs(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, FileAttrib* sourceAttr)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
copyFileWindows(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
copyFileLinuxMac(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
#endif
}
diff --git a/zen/file_handling.h b/zen/file_handling.h
index c18a68ac..f677358b 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -4,13 +4,13 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef FILE_HANDLING_H_INCLUDED
-#define FILE_HANDLING_H_INCLUDED
+#ifndef FILE_HANDLING_H_8017341345614857
+#define FILE_HANDLING_H_8017341345614857
#include "zstring.h"
#include "file_error.h"
-#include "int64.h"
#include "file_id_def.h"
+#include "int64.h"
namespace zen
{
@@ -18,20 +18,10 @@ struct CallbackRemoveDir;
struct CallbackCopyFile;
-bool fileExists (const Zstring& filename); //throw() check whether file or symlink exists
-bool dirExists (const Zstring& dirname); //throw() check whether directory or symlink exists
-bool symlinkExists (const Zstring& linkname); //throw() check whether a symbolic link exists
-bool somethingExists(const Zstring& objname); //throw() check whether any object with this name exists
-
-//check whether two folders are located on the same (logical) volume
-//left and right directories NEED NOT yet exist! volume prefix is sufficient! path may end with PATH_SEPARATOR
-enum ResponseSame
-{
- IS_SAME_YES,
- IS_SAME_NO,
- IS_SAME_CANT_SAY
-};
-ResponseSame onSameVolume(const Zstring& folderLeft, const Zstring& folderRight); //throw()
+bool fileExists (const Zstring& filename); //noexcept; check whether file *or* (file) symlink exists
+bool dirExists (const Zstring& dirname); //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
enum SymLinkType
{
@@ -39,7 +29,7 @@ enum SymLinkType
SYMLINK_TYPE_FILE, //Windows: may be broken
SYMLINK_TYPE_UNKNOWN, //Windows: unable to determine type; Linux: broken Symlink
};
-SymLinkType getSymlinkType(const Zstring& linkname); //throw()
+SymLinkType getSymlinkType(const Zstring& linkname); //noexcept
enum ProcSymlink
{
@@ -66,7 +56,7 @@ bool supportsPermissions(const Zstring& dirname); //throw FileError, derefernces
//if parent directory not existing: create recursively:
void makeDirectory(const Zstring& directory, bool failIfExists = false); //throw FileError, ErrorTargetExisting
-//fail if already existing or parent not existing:
+//fail if already existing or parent directory not existing:
//directory should not end with path separator
//templateDir may be empty
void makeDirectoryPlain(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
@@ -116,4 +106,4 @@ struct CallbackCopyFile
};
}
-#endif //FILE_HANDLING_H_INCLUDED
+#endif //FILE_HANDLING_H_8017341345614857
diff --git a/zen/file_id.cpp b/zen/file_id.cpp
index 8c66d1c9..310390da 100644
--- a/zen/file_id.cpp
+++ b/zen/file_id.cpp
@@ -6,19 +6,19 @@
#include "file_id.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
#include "long_path_prefix.h"
#include "scope_guard.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#include <sys/stat.h>
#endif
zen::FileId zen::getFileID(const Zstring& filename)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//WARNING: CreateFile() is SLOW, while GetFileInformationByHandle() is cheap! http://msdn.microsoft.com/en-us/library/aa363788(VS.85).aspx
//privilege SE_BACKUP_NAME doesn't seem to be required here at all
@@ -38,7 +38,7 @@ zen::FileId zen::getFileID(const Zstring& filename)
return extractFileID(fileInfo);
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct ::stat fileInfo = {};
if (::lstat(filename.c_str(), &fileInfo) == 0)
return extractFileID(fileInfo);
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
index 54012b4f..0608aacd 100644
--- a/zen/file_id_def.h
+++ b/zen/file_id_def.h
@@ -10,17 +10,17 @@
#include <utility>
#include "assert_static.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#include <sys/stat.h>
#endif
namespace zen
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
typedef DWORD DeviceId;
typedef ULONGLONG FileIndex;
@@ -49,7 +49,7 @@ assert_static(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFi
assert_static(sizeof(FileId().second) == sizeof(ULARGE_INTEGER));
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
namespace impl { typedef struct ::stat StatDummy; } //sigh...
typedef decltype(impl::StatDummy::st_dev) DeviceId;
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index cda48e36..e0f8c12e 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -6,13 +6,13 @@
#include "file_io.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "long_path_prefix.h"
#include "IFileOperation/file_op.h"
#include "win_ver.h"
#include "dll.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#include <fcntl.h> //open, close
#include <unistd.h> //read, write
#endif
@@ -22,7 +22,7 @@ using namespace zen;
namespace
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//(try to) enhance error messages by showing which processes lock the file
Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred
{
@@ -45,7 +45,7 @@ Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string
return Zstring();
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
//"filename" could be a named pipe which *blocks* forever during "open()"! https://sourceforge.net/p/freefilesync/bugs/221/
void checkForUnsupportedType(const Zstring& filename) //throw FileError
{
@@ -77,10 +77,11 @@ void checkForUnsupportedType(const Zstring& filename) //throw FileError
FileInput::FileInput(FileHandle handle, const Zstring& filename) : FileInputBase(filename), fileHandle(handle) {}
-FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting
+FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting
FileInputBase(filename)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"CreateFile";
fileHandle = ::CreateFile(applyLongPathPrefix(filename).c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_DELETE,
@@ -113,41 +114,42 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis
*/
nullptr);
if (fileHandle == INVALID_HANDLE_VALUE)
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
+ const wchar_t functionName[] = L"fopen";
fileHandle = ::fopen(filename.c_str(), "r,type=record,noseek"); //utilize UTF-8 filename
if (!fileHandle)
#endif
{
- const ErrorCode lastError = getLastError();
- const std::wstring shortMsg = errorCodeForNotExisting(lastError) ?
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
+ const std::wstring errorMsg = errorCodeForNotExisting(lastError) ?
replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) :
replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename));
- std::wstring errorMsg = shortMsg + L"\n\n" + zen::getLastErrorFormatted(lastError);
+ std::wstring errorDescr = formatSystemError(functionName, lastError);
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message!
lastError == ERROR_LOCK_VIOLATION)
{
const Zstring procList = getLockingProcessNames(filename); //throw()
if (!procList.empty())
- errorMsg = shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList;
+ errorDescr = _("The file is locked by another process:") + L"\n" + procList;
}
#endif
if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(errorMsg);
+ throw ErrorNotExisting(errorMsg, errorDescr);
- throw FileError(errorMsg + L" (open)");
+ throw FileError(errorMsg, errorDescr);
}
}
FileInput::~FileInput()
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
::CloseHandle(fileHandle);
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash!; fileHandle != nullptr in this context!
#endif
}
@@ -157,34 +159,36 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number
{
assert(!eof());
if (bytesToRead == 0) return 0;
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"ReadFile";
DWORD bytesRead = 0;
- if (!::ReadFile(fileHandle, //__in HANDLE hFile,
- buffer, //__out LPVOID lpBuffer,
+ if (!::ReadFile(fileHandle, //__in HANDLE hFile,
+ buffer, //__out LPVOID lpBuffer,
static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead,
- &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead,
- nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
-#elif defined FFS_LINUX || defined FFS_MAC
+ &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead,
+ nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"fread";
const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fread()!
#endif
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (ReadFile)");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(functionName, getLastError()));
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
if (bytesRead < bytesToRead) //verify only!
setEof();
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
if (::feof(fileHandle) != 0)
setEof();
if (bytesRead < bytesToRead)
if (!eof()) //pathologic!?
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete read!");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"Incomplete read!"); //user should never see this
#endif
if (bytesRead > bytesToRead)
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this
return bytesRead;
}
@@ -196,7 +200,7 @@ FileOutput::FileOutput(FileHandle handle, const Zstring& filename) : FileOutputB
FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
FileOutputBase(filename)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
const DWORD dwCreationDisposition = access == FileOutput::ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW;
auto getHandle = [&](DWORD dwFlagsAndAttributes)
@@ -218,7 +222,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
fileHandle = getHandle(FILE_ATTRIBUTE_NORMAL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
- DWORD lastError = ::GetLastError();
+ DWORD lastError = ::GetLastError(); //copy before making other system calls!
//CREATE_ALWAYS fails with ERROR_ACCESS_DENIED if the existing file is hidden or "system" http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
if (lastError == ERROR_ACCESS_DENIED &&
@@ -235,44 +239,46 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
//begin of "regular" error reporting
if (fileHandle == INVALID_HANDLE_VALUE)
{
- const std::wstring shortMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename));
- std::wstring errorMsg = shortMsg + L"\n\n" + zen::getLastErrorFormatted(lastError);
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename));
+ std::wstring errorDescr = formatSystemError(L"CreateFile", lastError);
if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message!
lastError == ERROR_LOCK_VIOLATION)
{
const Zstring procList = getLockingProcessNames(filename); //throw()
if (!procList.empty())
- errorMsg = shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList;
+ errorDescr = _("The file is locked by another process:") + L"\n" + procList;
}
if (lastError == ERROR_FILE_EXISTS || //confirmed to be used
lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6
- throw ErrorTargetExisting(errorMsg);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
if (lastError == ERROR_PATH_NOT_FOUND)
- throw ErrorTargetPathMissing(errorMsg);
+ throw ErrorTargetPathMissing(errorMsg, errorDescr);
- throw FileError(errorMsg);
+ throw FileError(errorMsg, errorDescr);
}
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
checkForUnsupportedType(filename); //throw FileError; writing a named pipe would block forever!
fileHandle = ::fopen(filename.c_str(),
//GNU extension: https://www.securecoding.cert.org/confluence/display/cplusplus/FIO03-CPP.+Do+not+make+assumptions+about+fopen()+and+file+creation
access == ACC_OVERWRITE ? "w,type=record,noseek" : "wx,type=record,noseek");
if (!fileHandle)
{
- const int lastError = errno;
- const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + zen::getLastErrorFormatted(lastError);
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename()));
+ const std::wstring errorDescr = formatSystemError(L"fopen", lastError);
+
if (lastError == EEXIST)
- throw ErrorTargetExisting(errorMessage);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
if (lastError == ENOENT)
- throw ErrorTargetPathMissing(errorMessage);
+ throw ErrorTargetPathMissing(errorMsg, errorDescr);
- throw FileError(errorMessage);
+ throw FileError(errorMsg, errorDescr);
}
#endif
}
@@ -280,9 +286,9 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
FileOutput::~FileOutput()
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
::CloseHandle(fileHandle);
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash!
#endif
}
@@ -290,25 +296,27 @@ FileOutput::~FileOutput()
void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"WriteFile";
DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx
if (!::WriteFile(fileHandle, //__in HANDLE hFile,
buffer, //__out LPVOID lpBuffer,
static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite,
&bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten,
nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"fwrite";
const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fwrite()!
#endif
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (WriteFile)");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(functionName, getLastError()));
if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes!
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete write!");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"Incomplete write!"); //user should never see this
}
-#if defined FFS_LINUX || defined FFS_MAC
+#if defined ZEN_LINUX || defined ZEN_MAC
//Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-5.0.tar.gz
FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError, ErrorNotExisting
@@ -316,14 +324,19 @@ FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBas
checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
fdFile = ::open(filename.c_str(), O_RDONLY);
- if (fdFile == -1)
+ if (fdFile == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle
{
- const ErrorCode lastError = getLastError();
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
+
+ const std::wstring errorMsg = errorCodeForNotExisting(lastError) ?
+ replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) :
+ replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename));
+ const std::wstring errorDescr = formatSystemError(L"open", lastError);
if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError));
+ throw ErrorNotExisting(errorMsg, errorDescr);
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)");
+ throw FileError(errorMsg, errorDescr);
}
}
@@ -344,11 +357,11 @@ size_t FileInputUnbuffered::read(void* buffer, size_t bytesToRead) //throw FileE
while (bytesRead < 0 && errno == EINTR);
if (bytesRead < 0)
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (read)");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(L"read", getLastError()));
else if (bytesRead == 0) //"zero indicates end of file"
setEof();
else if (bytesRead > static_cast<ssize_t>(bytesToRead)) //better safe than sorry
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this
//if ::read is interrupted (EINTR) right in the middle, it will return successfully with "bytesRead < bytesToRead"!
return bytesRead;
@@ -363,15 +376,17 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filename, mode_t mode)
fdFile = ::open(filename.c_str(), O_CREAT | O_WRONLY | O_EXCL, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
if (fdFile == -1)
{
- const int lastError = errno;
- const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + zen::getLastErrorFormatted(lastError);
+ const int lastError = errno; //copy before making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename));
+ const std::wstring errorDescr = formatSystemError(L"open", lastError);
+
if (lastError == EEXIST)
- throw ErrorTargetExisting(errorMessage);
+ throw ErrorTargetExisting(errorMsg, errorDescr);
if (lastError == ENOENT)
- throw ErrorTargetPathMissing(errorMessage);
+ throw ErrorTargetPathMissing(errorMsg, errorDescr);
- throw FileError(errorMessage);
+ throw FileError(errorMsg, errorDescr);
}
}
@@ -396,10 +411,10 @@ void FileOutputUnbuffered::write(const void* buffer, size_t bytesToWrite) //thro
if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers
errno = ENOSPC;
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (write)");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(L"write", getLastError()));
}
if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"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 407109f7..93267a39 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -10,9 +10,9 @@
#include "file_io_base.h"
#include "file_error.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#include <cstdio>
#include <sys/stat.h>
#endif
@@ -20,19 +20,19 @@
namespace zen
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
static const char LINE_BREAK[] = "\r\n";
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
static const char LINE_BREAK[] = "\n";
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
static const char LINE_BREAK[] = "\r";
#endif
//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks)
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
typedef HANDLE FileHandle;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
typedef FILE* FileHandle;
#endif
@@ -64,7 +64,7 @@ private:
FileHandle fileHandle;
};
-#if defined FFS_LINUX || defined FFS_MAC
+#if defined ZEN_LINUX || defined ZEN_MAC
class FileInputUnbuffered : public FileInputBase
{
public:
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 53049c21..a3e8491a 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -5,11 +5,11 @@
// **************************************************************************
#include "file_traverser.h"
-#include "last_error.h"
+#include "sys_error.h"
#include "assert_static.h"
#include "symlink_target.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include <zen/win_ver.h>
#include "long_path_prefix.h"
#include "dst_hack.h"
@@ -17,11 +17,11 @@
#include "dll.h"
#include "FindFilePlus/find_file_plus.h"
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
#include <zen/osx_string.h>
#endif
-#if defined FFS_LINUX || defined FFS_MAC
+#if defined ZEN_LINUX || defined ZEN_MAC
#include <sys/stat.h>
#include <dirent.h>
#endif
@@ -76,7 +76,7 @@ bool tryReportingItemError(Command cmd, zen::TraverseCallback& callback, const Z
}
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
void getInfoFromFileSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output) //throw FileError
{
//open handle to target of symbolic link
@@ -88,24 +88,25 @@ void getInfoFromFileSymlink(const Zstring& linkName, zen::TraverseCallback::File
FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory -> keep it even if we expect to open a file! See comment below
nullptr);
if (hFile == INVALID_HANDLE_VALUE)
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)");
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"CreateFile", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
BY_HANDLE_FILE_INFORMATION fileInfo = {};
if (!::GetFileInformationByHandle(hFile, &fileInfo))
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (GetFileInformationByHandle)");
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"GetFileInformationByHandle", getLastError()));
//a file symlink may incorrectly point to a directory, but both CreateFile() and GetFileInformationByHandle() will succeed and return garbage!
- //- if we did not use FILE_FLAG_BACKUP_SEMANTICS above, CreateFile() would error out with an even less helpful ERROR_ACCESS_DENIED!
+ //- if we did not use FILE_FLAG_BACKUP_SEMANTICS above, CreateFile() would error out with an even less helpful ERROR_ACCESS_DENIED!
//- reinterpreting the link as a directory symlink would still fail during traversal, so just show an error here
//- OTOH a directory symlink that points to a file fails immediately in ::FindFirstFile() with ERROR_DIRECTORY! -> nothing to do in this case
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted(ERROR_FILE_INVALID) + L" (GetFileInformationByHandle)");
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"GetFileInformationByHandle", static_cast<DWORD>(ERROR_FILE_INVALID)));
//write output
output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
output.lastWriteTime = toTimeT(fileInfo.ftLastWriteTime);
output.id = extractFileID(fileInfo); //consider detection of moved files: allow for duplicate file ids, renaming affects symlink, not target, ...
+ //output.symlinkInfo -> not filled here
}
@@ -191,15 +192,16 @@ struct Win32Traverser
//no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
if (hnd.searchHandle == INVALID_HANDLE_VALUE)
{
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
hnd.haveData = false;
- if (::GetLastError() == ERROR_FILE_NOT_FOUND)
+ if (lastError == ERROR_FILE_NOT_FOUND)
{
//1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive
// -> FindFirstFile() is a nice example of violation of API design principle of single responsibility
if (dirExists(dirname)) //yes, a race-condition, still the best we can do
return;
}
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"FindFirstFile", lastError));
}
}
@@ -220,10 +222,11 @@ struct Win32Traverser
if (!::FindNextFile(hnd.searchHandle, &fileInfo))
{
- if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
+ const ErrorCode lastError = getLastError(); //copy before making other system calls!
+ if (lastError == ERROR_NO_MORE_FILES) //not an error situation
return false;
//else we have a problem... report it:
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"FindNextFile", lastError));
}
return true;
}
@@ -259,7 +262,7 @@ struct FilePlusTraverser
{
hnd.searchHandle = ::openDir(applyLongPathPrefix(dirname).c_str());
if (hnd.searchHandle == nullptr)
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted() + L" (+)");
+ throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"openDir", getLastError()));
}
static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw()
@@ -284,7 +287,7 @@ struct FilePlusTraverser
}
//else we have a problem... report it:
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (+)");
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"readDir", lastError));
}
return true;
@@ -310,10 +313,8 @@ class DirTraverser
{
public:
DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) :
- isFatFileSystem(dst::isFatDrive(baseDirectory))
+ needDstHack(dstCallback ? dst::isFatDrive(baseDirectory) : false)
{
- warn_static("ineffizient, wenn kein dst hack/file ids gebraucht werden???")
-
try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail)
{
activatePrivilege(SE_BACKUP_NAME); //throw FileError
@@ -326,8 +327,9 @@ public:
traverse<Win32Traverser>(baseDirectory, sink, 0);
//apply daylight saving time hack AFTER file traversing, to give separate feedback to user
- if (dstCallback && isFatFileSystem)
- applyDstHack(*dstCallback);
+ if (needDstHack)
+ if (dstCallback) //bound if "needDstHack == true"
+ applyDstHack(*dstCallback);
}
private:
@@ -378,8 +380,11 @@ private:
case TraverseCallback::LINK_FOLLOW:
if (Trav::isDirectory(findData))
{
- if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse<Trav>(fullName, *rv, retrieveVolumeSerial(fullName)); //symlink may link to different volume => redetermine volume serial!
+ if (TraverseCallback* trav = sink.onDir(shortName, fullName))
+ {
+ ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav));
+ traverse<Trav>(fullName, *trav, retrieveVolumeSerial(fullName)); //symlink may link to different volume => redetermine volume serial!
+ }
}
else //a file
{
@@ -387,13 +392,12 @@ private:
const bool validLink = tryReportingItemError([&] //try to resolve symlink (and report error on failure!!!)
{
getInfoFromFileSymlink(fullName, targetInfo); //throw FileError
+ targetInfo.symlinkInfo = &linkInfo;
}, sink, shortName);
if (validLink)
sink.onFile(shortName, fullName, targetInfo);
- // else //broken symlink
- // sink.onFile(shortName, fullName, TraverseCallback::FileInfo());
- warn_static("impact of ignored broken file/incomplete dir read on two-way variant!?")
+ // else //broken symlink -> ignore: it's client's responsibility to handle error!
}
break;
@@ -403,8 +407,11 @@ private:
}
else if (Trav::isDirectory(findData))
{
- if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse<Trav>(fullName, *rv, volumeSerial);
+ if (TraverseCallback* trav = sink.onDir(shortName, fullName))
+ {
+ ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav));
+ traverse<Trav>(fullName, *trav, volumeSerial);
+ }
}
else //a file
{
@@ -412,12 +419,12 @@ private:
Trav::extractFileInfo(findData, volumeSerial, fileInfo);
//####################################### DST hack ###########################################
- if (isFatFileSystem)
+ if (needDstHack)
{
const dst::RawTime rawTime(Trav::getCreateTimeRaw(findData), Trav::getModTimeRaw(findData));
if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error
- fileInfo.lastWriteTime = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error)
+ fileInfo.lastWriteTime = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw std::runtime_error
else
markForDstHack.push_back(std::make_pair(fullName, toTimeT(rawTime.writeTimeRaw)));
}
@@ -476,13 +483,13 @@ private:
}
}
- const bool isFatFileSystem;
- typedef std::vector<std::pair<Zstring, Int64> > FilenameTimeList;
+ const bool needDstHack;
+ typedef std::vector<std::pair<Zstring, Int64>> FilenameTimeList;
FilenameTimeList markForDstHack;
//####################################### DST hack ###########################################
};
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
class DirTraverser
{
public:
@@ -498,8 +505,8 @@ public:
the buffer whose address is passed in entry as follows:
len = offsetof(struct dirent, d_name) + pathconf(dirpath, _PC_NAME_MAX) + 1
entryp = malloc(len); */
- const size_t maxPath = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
- buffer.resize(offsetof(struct ::dirent, d_name) + maxPath + 1);
+ const size_t nameMax = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
+ buffer.resize(offsetof(struct ::dirent, d_name) + nameMax + 1);
traverse(directoryFormatted, sink);
}
@@ -517,7 +524,7 @@ private:
{
dirObj = ::opendir(dirname.c_str()); //directory must NOT end with path separator, except "/"
if (!dirObj)
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"opendir", getLastError()));
}, sink))
return; //ignored error
ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash
@@ -528,7 +535,7 @@ private:
tryReportingDirError([&]
{
if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"readdir_r", getLastError()));
}, sink);
if (!dirEntry) //no more items or ignored error
return;
@@ -538,7 +545,7 @@ private:
if (shortName[0] == '.' &&
(shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0)))
continue;
-#ifdef FFS_MAC
+#ifdef ZEN_MAC
//some file system abstraction layers fail to properly return decomposed UTF8: http://developer.apple.com/library/mac/#qa/qa1173/_index.html
//so we need to do it ourselves; perf: ~600 ns per conversion
//note: it's not sufficient to apply this in z_impl::compareFilenamesNoCase: if UTF8 forms differ, FFS assumes a rename in case sensitivity and
@@ -564,7 +571,7 @@ private:
if (!tryReportingItemError([&]
{
if (::lstat(fullName.c_str(), &statData) != 0) //lstat() does not resolve symlinks
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(fullName)), formatSystemError(L"lstat", getLastError()));
}, sink, shortName))
continue; //ignore error: skip file
@@ -582,15 +589,18 @@ private:
bool validLink = tryReportingItemError([&]
{
if (::stat(fullName.c_str(), &statDataTrg) != 0)
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)), formatSystemError(L"stat", getLastError()));
}, sink, shortName);
if (validLink)
{
if (S_ISDIR(statDataTrg.st_mode)) //a directory
{
- if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse(fullName, *rv);
+ if (TraverseCallback* trav = sink.onDir(shortName, fullName))
+ {
+ ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav));
+ traverse(fullName, *trav);
+ }
}
else //a file or named pipe, ect.
{
@@ -598,11 +608,11 @@ private:
fileInfo.fileSize = zen::UInt64(statDataTrg.st_size);
fileInfo.lastWriteTime = statDataTrg.st_mtime; //UTC time (time_t format); unit: 1 second
fileInfo.id = extractFileID(statDataTrg);
+ fileInfo.symlinkInfo = &linkInfo;
sink.onFile(shortName, fullName, fileInfo);
}
}
- // else //report broken symlink as file!
- // sink.onFile(shortName, fullName, TraverseCallback::FileInfo());
+ // else //broken symlink -> ignore: it's client's responsibility to handle error!
}
break;
@@ -612,8 +622,11 @@ private:
}
else if (S_ISDIR(statData.st_mode)) //a directory
{
- if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse(fullName, *rv);
+ if (TraverseCallback* trav = sink.onDir(shortName, fullName))
+ {
+ ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav));
+ traverse(fullName, *trav);
+ }
}
else //a file or named pipe, ect.
{
@@ -635,7 +648,7 @@ private:
}
std::vector<char> buffer;
-#ifdef FFS_MAC
+#ifdef ZEN_MAC
std::vector<char> bufferUtfDecomposed;
#endif
};
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index 13b76966..a1d8ac9e 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -7,7 +7,7 @@
#ifndef FILETRAVERSER_H_INCLUDED
#define FILETRAVERSER_H_INCLUDED
-#include <memory>
+//#include <memory>
#include "zstring.h"
#include "int64.h"
#include "file_id_def.h"
@@ -20,17 +20,18 @@ struct TraverseCallback
{
virtual ~TraverseCallback() {}
- struct FileInfo
+ struct SymlinkInfo
{
- UInt64 fileSize; //unit: bytes!
Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
- FileId id; //optional: initial if not supported!
- //std::unique_ptr<SymlinkInfo> symlinkInfo; //only filled if file is dereferenced symlink
};
- struct SymlinkInfo
+ struct FileInfo
{
+ FileInfo() : symlinkInfo() {}
+ UInt64 fileSize; //unit: bytes!
Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
+ FileId id; //optional: initial if not supported!
+ const SymlinkInfo* symlinkInfo; //only filled if file is a followed symlink
};
enum HandleLink
@@ -45,22 +46,24 @@ struct TraverseCallback
ON_ERROR_IGNORE
};
- virtual std::shared_ptr<TraverseCallback> //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback
- /**/ onDir (const Zchar* shortName, const Zstring& fullName) = 0;
- virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0;
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0;
+ virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0;
+ virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0;
+ virtual TraverseCallback* onDir (const Zchar* shortName, const Zstring& fullName) = 0;
+ //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback => implement releaseDirTraverser() if necessary!
+ virtual void releaseDirTraverser(TraverseCallback* trav) {}
+
virtual HandleError reportDirError (const std::wstring& msg) = 0; //failed directory traversal -> consider directory data as incomplete!
- virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) = 0; //failed to get single file/dir/symlink only!
+ virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) = 0; //failed to get data for single file/dir/symlink only!
};
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
struct DstHackCallback
{
virtual ~DstHackCallback() {}
virtual void requestUiRefresh(const Zstring& filename) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user
};
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
struct DstHackCallback; //DST hack not required on Unix
#endif
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 60eb6869..9e5975d9 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -12,12 +12,12 @@
#include <ctime>
#include <cstdio>
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include <zen/win.h> //includes "windows.h"
#include <zen/win_ver.h>
-#elif defined FFS_LINUX || defined FFS_MAC
-#include <clocale> //thousands separator
+#elif defined ZEN_LINUX || defined ZEN_MAC
+#include <clocale> //thousands separator
#include <zen/utf.h> //
#endif
@@ -152,7 +152,7 @@ std::wstring zen::fractionToString(double fraction)
}
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
namespace
{
bool getUserSetting(LCTYPE lt, UINT& setting)
@@ -233,7 +233,7 @@ private:
std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
if (IntegerFormat::isValid())
{
int bufferSize = ::GetNumberFormat(LOCALE_USER_DEFAULT, 0, number.c_str(), &IntegerFormat::get(), nullptr, 0);
@@ -251,7 +251,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
}
return number;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
//we have to include thousands separator ourselves; this doesn't work for all countries (e.g india), but is better than nothing
//::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
@@ -277,7 +277,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
}
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
namespace
{
const bool useNewLocalTimeCalculation = zen::vistaOrLater();
@@ -289,7 +289,7 @@ std::wstring zen::utcToLocalTimeString(Int64 utcTime)
{
auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L")"; };
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
FILETIME lastWriteTimeUtc = tofiletime(utcTime); //convert ansi C time to FILETIME
SYSTEMTIME systemTimeLocal = {};
@@ -326,7 +326,7 @@ std::wstring zen::utcToLocalTimeString(Int64 utcTime)
loc.minute = systemTimeLocal.wMinute;
loc.second = systemTimeLocal.wSecond;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
zen::TimeComp loc = zen::localTime(to<time_t>(utcTime));
#endif
diff --git a/zen/int64.h b/zen/int64.h
index 4c63a24f..7d03f35d 100644
--- a/zen/int64.h
+++ b/zen/int64.h
@@ -15,7 +15,7 @@
#include "assert_static.h"
#include "type_tools.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h"
#endif
@@ -60,7 +60,7 @@ public:
Int64& operator=(const Int64& rhs) { value = rhs.value; return *this; }
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
Int64(DWORD low, LONG high)
{
assert_static(sizeof(low) + sizeof(high) == sizeof(value));
@@ -139,7 +139,7 @@ public:
UInt64& operator=(const UInt64& rhs) { value = rhs.value; return *this; }
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
UInt64(DWORD low, DWORD high)
{
assert_static(sizeof(low) + sizeof(high) == sizeof(value));
@@ -206,7 +206,7 @@ template <> inline UInt64 to(Int64 number) { checkRange<std::uint64_t>(number.va
template <> inline Int64 to(UInt64 number) { checkRange<std:: int64_t>(number.value); return Int64(number.value); }
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//convert FILETIME (number of 100-nanosecond intervals since January 1, 1601 UTC)
// to time_t (number of seconds since Jan. 1st 1970 UTC)
//
diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp
index 29958f0c..3815887a 100644
--- a/zen/notify_removal.cpp
+++ b/zen/notify_removal.cpp
@@ -46,7 +46,7 @@ private:
MessageProvider(const MessageProvider&);
MessageProvider& operator=(const MessageProvider&);
- static const wchar_t WINDOW_NAME[];
+ static const wchar_t dummyWindowName[];
friend LRESULT CALLBACK topWndProc(HWND, UINT, WPARAM, LPARAM);
void processMessage(UINT message, WPARAM wParam, LPARAM lParam);
@@ -58,7 +58,7 @@ private:
};
-const wchar_t MessageProvider::WINDOW_NAME[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name
+const wchar_t MessageProvider::dummyWindowName[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name
LRESULT CALLBACK topWndProc(HWND hwnd, //handle to window
@@ -82,22 +82,22 @@ MessageProvider::MessageProvider() :
windowHandle(nullptr)
{
if (!hMainModule)
- throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (GetModuleHandle)");
+ throw FileError(_("Failed to register to receive system messages."), formatSystemError(L"GetModuleHandle", getLastError()));
//register the main window class
WNDCLASS wc = {};
wc.lpfnWndProc = topWndProc;
wc.hInstance = hMainModule;
- wc.lpszClassName = WINDOW_NAME;
+ wc.lpszClassName = dummyWindowName;
if (::RegisterClass(&wc) == 0)
- throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (RegisterClass)");
+ throw FileError(_("Failed to register to receive system messages."), formatSystemError(L"RegisterClass", getLastError()));
- ScopeGuard guardClass = makeGuard([&] { ::UnregisterClass(WINDOW_NAME, hMainModule); });
+ ScopeGuard guardClass = makeGuard([&] { ::UnregisterClass(dummyWindowName, hMainModule); });
//create dummy-window
- windowHandle = ::CreateWindow(WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word!
- nullptr, //LPCTSTR lpWindowName,
+ windowHandle = ::CreateWindow(dummyWindowName, //LPCTSTR lpClassName OR ATOM in low-order word!
+ nullptr, //LPCTSTR lpWindowName,
0, //DWORD dwStyle,
0, //int x,
0, //int y,
@@ -108,7 +108,7 @@ MessageProvider::MessageProvider() :
hMainModule, //HINSTANCE hInstance,
nullptr); //LPVOID lpParam
if (!windowHandle)
- throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (CreateWindow)");
+ throw FileError(_("Failed to register to receive system messages."), formatSystemError(L"CreateWindow", getLastError()));
guardClass.dismiss();
}
@@ -118,7 +118,7 @@ MessageProvider::~MessageProvider()
{
//clean-up in reverse order
::DestroyWindow(windowHandle);
- ::UnregisterClass(WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word!
+ ::UnregisterClass(dummyWindowName, //LPCTSTR lpClassName OR ATOM in low-order word!
hMainModule); //HINSTANCE hInstance
}
@@ -128,8 +128,8 @@ void MessageProvider::processMessage(UINT message, WPARAM wParam, LPARAM lParam)
std::for_each(listener.begin(), listener.end(),
[&](Listener* ls) { ls->onMessage(message, wParam, lParam); });
}
-//####################################################################################################
+//####################################################################################################
class NotifyRequestDeviceRemoval::Pimpl : private MessageProvider::Listener
{
@@ -139,6 +139,8 @@ public:
{
MessageProvider::instance().registerListener(*this); //throw FileError
+ ScopeGuard guardProvider = makeGuard([&] { MessageProvider::instance().unregisterListener(*this); });
+
//register handles to receive notifications
DEV_BROADCAST_HANDLE filter = {};
filter.dbch_size = sizeof(filter);
@@ -154,13 +156,16 @@ public:
if (lastError != ERROR_CALL_NOT_IMPLEMENTED && //fail on SAMBA share: this shouldn't be a showstopper!
lastError != ERROR_SERVICE_SPECIFIC_ERROR && //neither should be fail for "Pogoplug" mapped network drives
lastError != ERROR_INVALID_DATA) //this seems to happen for a NetDrive-mapped FTP server
- throw zen::FileError(L"Could not register device removal notifications:" L"\n\n" + getLastErrorFormatted(lastError));
+ throw zen::FileError(_("Failed to register to receive system messages."), formatSystemError(L"RegisterDeviceNotification", lastError));
}
+
+ guardProvider.dismiss();
}
~Pimpl()
{
- ::UnregisterDeviceNotification(hNotification);
+ if (hNotification)
+ ::UnregisterDeviceNotification(hNotification);
MessageProvider::instance().unregisterListener(*this);
}
@@ -172,10 +177,9 @@ private:
{
//DBT_DEVICEQUERYREMOVE example: http://msdn.microsoft.com/en-us/library/aa363427(v=VS.85).aspx
if (message == WM_DEVICECHANGE)
- {
- if ( wParam == DBT_DEVICEQUERYREMOVE ||
- wParam == DBT_DEVICEQUERYREMOVEFAILED ||
- wParam == DBT_DEVICEREMOVECOMPLETE)
+ if (wParam == DBT_DEVICEQUERYREMOVE ||
+ wParam == DBT_DEVICEQUERYREMOVEFAILED ||
+ wParam == DBT_DEVICEREMOVECOMPLETE)
{
PDEV_BROADCAST_HDR header = reinterpret_cast<PDEV_BROADCAST_HDR>(lParam);
if (header->dbch_devicetype == DBT_DEVTYP_HANDLE)
@@ -202,14 +206,13 @@ private:
}
}
}
- }
}
NotifyRequestDeviceRemoval& parent_;
HDEVNOTIFY hNotification;
};
-//####################################################################################################
+//####################################################################################################
NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir)
{
@@ -217,4 +220,4 @@ NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir)
}
-NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~auto_ptr() works with complete type
+NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~unique_ptr() works with complete type
diff --git a/zen/notify_removal.h b/zen/notify_removal.h
index dc8149a0..aca0912f 100644
--- a/zen/notify_removal.h
+++ b/zen/notify_removal.h
@@ -7,7 +7,6 @@
#ifndef NOTIFY_H_INCLUDED
#define NOTIFY_H_INCLUDED
-#include <vector>
#include <memory>
#include "win.h" //includes "windows.h"
#include "file_error.h"
diff --git a/zen/osx_error.h b/zen/osx_error.h
deleted file mode 100644
index 4b0aeb3b..00000000
--- a/zen/osx_error.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef OSX_ERRROR_834270598342753425
-#define OSX_ERRROR_834270598342753425
-
-#include <string>
-
-namespace osx
-{
-class OsxError //Exception base class used to notify file/directory copy/delete errors
-{
-public:
- explicit OsxError(const std::wstring& message) : msg(message) {}
- virtual ~OsxError() {}
-
- const std::wstring& toString() const { return msg; }
-
-private:
- std::wstring msg;
-};
-}
-
-#endif //OSX_ERRROR_834270598342753425
diff --git a/zen/osx_string.h b/zen/osx_string.h
index ce8b1062..0d77345c 100644
--- a/zen/osx_string.h
+++ b/zen/osx_string.h
@@ -7,8 +7,8 @@
#ifndef OSX_STRING_1873641732143214324
#define OSX_STRING_1873641732143214324
-#include <zen/zstring.h>
#include <CoreFoundation/CoreFoundation.h> //CFString
+#include "zstring.h"
namespace osx
{
diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h
index 31592854..018e9456 100644
--- a/zen/osx_throw_exception.h
+++ b/zen/osx_throw_exception.h
@@ -8,21 +8,21 @@
#define OSX_EXCEPTION_89274305834255
#import <Cocoa/Cocoa.h>
-#include <zen/osx_error.h>
-#include <zen/utf.h>
+#include "sys_error.h"
+#include "utf.h"
namespace osx
{
//for use in Objective C implementation files only!
-void throwOsxError(NSException* e); //throw OsxError
+void throwSysError(NSException* e); //throw SysError
-#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw OsxError
+#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw SysError
/*
-Example: ZEN_COM_ASSERT(obj);
+Example: ZEN_OSX_ASSERT(obj);
Equivalent to:
if (!obj)
- throw OsxError(L"Assertion failed: \"obj\".");
+ throw zen::SysError(L"Assertion failed: \"obj\".");
*/
@@ -32,7 +32,7 @@ Equivalent to:
//######################## implmentation ############################
inline
-void throwOsxError(NSException* e) //throw OsxError
+void throwSysError(NSException* e) //throw SysError
{
std::string msg;
if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us!
@@ -42,7 +42,7 @@ if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding])
msg += "\n";
msg += descr;
}
- throw OsxError(zen::utfCvrtTo<std::wstring>(msg));
+ throw zen::SysError(zen::utfCvrtTo<std::wstring>(msg));
/*
e.g.
NSInvalidArgumentException
@@ -51,6 +51,6 @@ if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding])
}
}
-#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw osx::OsxError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".");
+#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".");
#endif //OSX_EXCEPTION_89274305834255
diff --git a/zen/perf.h b/zen/perf.h
index 4a334bff..ccaa3f07 100644
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -10,7 +10,7 @@
#include "deprecate.h"
#include "tick_count.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include <sstream>
#else
#include <iostream>
@@ -33,7 +33,7 @@ public:
ticksPerSec_(ticksPerSec()), startTime(), resultShown(false)
{
//std::clock() - "counts CPU time in C and wall time in VC++" - WTF!???
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0) //"should not be required unless there are bugs in BIOS or HAL" - msdn, QueryPerformanceCounter
throw TimerError();
#endif
@@ -51,7 +51,7 @@ public:
throw TimerError();
const auto delta = static_cast<long>(1000.0 * dist(startTime, now) / ticksPerSec_);
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
std::ostringstream ss;
ss << delta << " ms";
::MessageBoxA(nullptr, ss.str().c_str(), "Timer", 0);
diff --git a/zen/privilege.cpp b/zen/privilege.cpp
index b474958e..44318517 100644
--- a/zen/privilege.cpp
+++ b/zen/privilege.cpp
@@ -15,14 +15,14 @@ bool privilegeIsActive(LPCTSTR privilege) //throw FileError
if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
TOKEN_QUERY, //__in DWORD DesiredAccess,
&hToken)) //__out PHANDLE TokenHandle
- throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"OpenProcessToken", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));
LUID luid = {};
if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName,
privilege, //__in LPCTSTR lpName,
&luid )) //__out PLUID lpLuid
- throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"LookupPrivilegeValue", getLastError()));
PRIVILEGE_SET priv = {};
priv.PrivilegeCount = 1;
@@ -34,7 +34,7 @@ bool privilegeIsActive(LPCTSTR privilege) //throw FileError
if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken,
&priv, //__inout PPRIVILEGE_SET RequiredPrivileges,
&alreadyGranted)) //__out LPBOOL pfResult
- throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"PrivilegeCheck", getLastError()));
return alreadyGranted != FALSE;
}
@@ -46,14 +46,14 @@ void setPrivilege(LPCTSTR privilege, bool enable) //throw FileError
if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess,
&hToken)) //__out PHANDLE TokenHandle
- throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"OpenProcessToken", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));
LUID luid = {};
if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName,
privilege, //__in LPCTSTR lpName,
&luid )) //__out PLUID lpLuid
- throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"LookupPrivilegeValue", getLastError()));
TOKEN_PRIVILEGES tp = {};
tp.PrivilegeCount = 1;
@@ -66,10 +66,11 @@ void setPrivilege(LPCTSTR privilege, bool enable) //throw FileError
0, //__in DWORD BufferLength,
nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState,
nullptr)) //__out_opt PDWORD ReturnLength
- throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"AdjustTokenPrivileges", getLastError()));
- if (::GetLastError() == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success!
- throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted());
+ const ErrorCode lastError = getLastError();
+ if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success!
+ throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"AdjustTokenPrivileges", lastError));
}
diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp
index 96a6c2de..b09b35f5 100644
--- a/zen/process_priority.cpp
+++ b/zen/process_priority.cpp
@@ -5,16 +5,16 @@
// **************************************************************************
#include "process_priority.h"
-#include <zen/last_error.h>
+#include <zen/sys_error.h>
#include <zen/i18n.h>
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
//#include <sys/syscall.h>
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
#include <sys/resource.h> //getiopolicy_np
#include <IOKit/pwr_mgt/IOPMLib.h> //keep in .cpp file to not pollute global namespace! e.g. with UInt64
#endif
@@ -22,7 +22,7 @@
using namespace zen;
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
struct PreventStandby::Pimpl {};
PreventStandby::PreventStandby()
@@ -49,7 +49,7 @@ struct ScheduleForBackgroundProcessing::Pimpl {};
ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing()
{
if (!::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN)) //this call lowers CPU priority, too!!
- throw FileError(_("Cannot change process I/O priorities.") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot change process I/O priorities."), formatSystemError(L"SetPriorityClass", getLastError()));
}
@@ -58,7 +58,7 @@ ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing()
::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END);
}
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
struct PreventStandby::Pimpl {};
PreventStandby::PreventStandby() {}
PreventStandby::~PreventStandby() {}
@@ -101,7 +101,7 @@ private:
};
*/
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
//https://developer.apple.com/library/mac/#qa/qa1340
struct PreventStandby::Pimpl
{
@@ -111,11 +111,12 @@ struct PreventStandby::Pimpl
PreventStandby::PreventStandby() : pimpl(make_unique<Pimpl>())
{
- if (::IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep,
- kIOPMAssertionLevelOn,
- CFSTR("FreeFileSync"),
- &pimpl->assertionID) != kIOReturnSuccess)
- throw FileError(_("Failed to suspend system sleep mode."));
+ IOReturn rv = ::IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep,
+ kIOPMAssertionLevelOn,
+ CFSTR("FreeFileSync"),
+ &pimpl->assertionID);
+ if (rv != kIOReturnSuccess)
+ throw FileError(_("Failed to suspend system sleep mode."), replaceCpy<std::wstring>(L"IOReturn Code %x", L"%x", numberTo<std::wstring>(rv))); //could not find a better way to convert IOReturn to string
}
PreventStandby::~PreventStandby()
@@ -135,10 +136,10 @@ ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() : pimpl(make_
{
pimpl->oldIoPrio = ::getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS);
if (pimpl->oldIoPrio == -1)
- throw FileError(_("Cannot change process I/O priorities.") + L" (r)" + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot change process I/O priorities."), formatSystemError(L"getiopolicy_np", getLastError()));
if (::setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE) != 0)
- throw FileError(_("Cannot change process I/O priorities.") + L" (w)" + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot change process I/O priorities."), formatSystemError(L"setiopolicy_np", getLastError()));
}
@@ -146,4 +147,4 @@ ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing()
{
::setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, pimpl->oldIoPrio);
}
-#endif \ No newline at end of file
+#endif
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index c062a26c..f1b8381a 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -9,7 +9,7 @@
//#include <iterator>
#include <zen/file_handling.h>
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//#include <algorithm>
//#include <functional>
#include <zen/dll.h>
@@ -18,19 +18,19 @@
#include <zen/long_path_prefix.h>
#include "IFileOperation/file_op.h"
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
#include <zen/scope_guard.h>
#include <sys/stat.h>
#include <gio/gio.h>
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
#include <CoreServices/CoreServices.h>
#endif
using namespace zen;
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
namespace
{
/*
@@ -91,7 +91,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli
const DllFun<FunType_getLastError> getLastError (getDllName(), funName_getLastError);
if (!moveToRecycler || !getLastError)
- throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filenames[0])) + L"\n\n" +
+ throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filenames[0])),
replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName())));
std::vector<const wchar_t*> cNames;
@@ -108,8 +108,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli
if (filenames.size() > 1)
filenameFmt += 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", filenameFmt) +
- L"\n\n" + getLastError()); //already includes details about locking errors!
+ throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", filenameFmt), getLastError()); //already includes details about locking errors!
}
}
else //regular recycle bin usage: available since XP
@@ -134,7 +133,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli
//"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)
{
- throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filenames[0]))); //probably not the correct file name for file list larger than 1!
+ throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filenames[0]))); //probably not the correct file name for file list larger than 1!
}
}
}
@@ -146,12 +145,12 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
if (!somethingExists(filename)) //[!] do not optimize away, OS X needs this for reliable detection of "recycle bin missing"
return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it!
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
std::vector<Zstring> filenames;
filenames.push_back(filename);
recycleOrDelete(filenames, nullptr); //throw FileError
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
GFile* file = ::g_file_new_for_path(filename.c_str()); //never fails according to docu
ZEN_ON_SCOPE_EXIT(g_object_unref(file);)
@@ -160,10 +159,10 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
if (!::g_file_trash(file, nullptr, &error))
{
- const std::wstring shortMsg = replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filename));
+ const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filename));
if (!error)
- throw FileError(shortMsg + L"\n\n" + L"Unknown error.");
+ throw FileError(errorMsg, L"Unknown error."); //user should never see this
//implement same behavior as in Windows: if recycler is not existing, delete permanently
if (error->code == G_IO_ERROR_NOT_SUPPORTED)
@@ -179,11 +178,11 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
return true;
}
- throw FileError(shortMsg + L"\n\n" + L"Glib Error Code " + numberTo<std::wstring>(error->code) + /* L", " +
- g_quark_to_string(error->domain) + */ L": " + utfCvrtTo<std::wstring>(error->message));
+ throw FileError(errorMsg, replaceCpy<std::wstring>(L"Glib Error Code %x:", L"%x", numberTo<std::wstring>(error->code)) + L" " + utfCvrtTo<std::wstring>(error->message));
+ //g_quark_to_string(error->domain)
}
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
//we cannot use FSPathMoveObjectToTrashSync directly since it follows symlinks!
assert_static(sizeof(Zchar) == sizeof(char));
@@ -191,12 +190,12 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
auto throwFileError = [&](OSStatus oss)
{
- std::wstring msg = replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filename)) + L"\n\n"
- + L"Result Code " + numberTo<std::wstring>(oss);
- const char* description = GetMacOSStatusCommentString(oss);
- if (description) //found no documentation for proper use of GetMacOSStatusCommentString
- msg += L": " + utfCvrtTo<std::wstring>(description);
- throw FileError(msg);
+ const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filename));
+ std::wstring errorDescr = L"OSStatus Code " + numberTo<std::wstring>(oss);
+
+ if (const char* description = ::GetMacOSStatusCommentString(oss)) //found no documentation for proper use of GetMacOSStatusCommentString
+ errorDescr += L": " + utfCvrtTo<std::wstring>(description);
+ throw FileError(errorMsg, errorDescr);
};
FSRef objectRef;
@@ -234,7 +233,7 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
}
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
{
const DWORD bufferSize = MAX_PATH + 1;
@@ -323,7 +322,7 @@ StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
*/
}
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
/*
We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not!
diff --git a/zen/recycler.h b/zen/recycler.h
index 8aca0ff3..f55971ac 100644
--- a/zen/recycler.h
+++ b/zen/recycler.h
@@ -34,7 +34,7 @@ Already included in package "gtk+-2.0"!
bool recycleOrDelete(const Zstring& filename); //throw FileError, return "true" if file/dir was actually deleted
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
enum StatusRecycler
{
STATUS_REC_EXISTS,
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index cb28904f..000853fd 100644
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
diff --git a/zen/serialize.h b/zen/serialize.h
index 982165f9..415bd430 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -167,7 +167,7 @@ BinContainer loadBinStream(const Zstring& filename) //throw FileError, ErrorNotE
const size_t blockSize = 128 * 1024;
do
{
- contOut.resize(contOut.size() + blockSize);
+ contOut.resize(contOut.size() + blockSize); //container better implement exponential growth!
const size_t bytesRead = fileIn.read(&*contOut.begin() + contOut.size() - blockSize, blockSize); //throw FileError
if (bytesRead < blockSize)
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index b394b128..c93f2d61 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
@@ -129,7 +129,7 @@ ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const
template <class BidirectionalIterator, class T> inline
-BidirectionalIterator find_last(const BidirectionalIterator first, BidirectionalIterator last, const T& value)
+BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value)
{
for (BidirectionalIterator iter = last; iter != first;) //reverse iteration: 1. check 2. decrement 3. evaluate
{
@@ -143,8 +143,8 @@ BidirectionalIterator find_last(const BidirectionalIterator first, Bidirectional
template <class BidirectionalIterator1, class BidirectionalIterator2> inline
-BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
- const BidirectionalIterator2 first2, BidirectionalIterator2 last2)
+BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
+ const BidirectionalIterator2 first2, const BidirectionalIterator2 last2)
{
const BidirectionalIterator1 iterNotFound = last1;
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 990c823a..4639e1dd 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
@@ -463,41 +463,69 @@ perf: integer to string: (executed 10 mio. times)
formatInteger - 778 ms
*/
-template <class S, class Num> inline
-S formatInteger(Num n, bool hasMinus)
+template <class OutputIterator, class Num> inline
+void formatNegativeInteger(Num n, OutputIterator& it)
{
- assert(n >= 0);
- typedef typename GetCharType<S>::Type CharType;
- CharType buffer[2 + sizeof(Num) * 5 / 2]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
- //required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) < 2 + floor(sizeof(n) * 2.5)
-
- auto iter = std::end(buffer);
+ assert(n < 0);
+ typedef typename std::iterator_traits<OutputIterator>::value_type CharType;
do
{
const Num tmp = n / 10;
- *--iter = static_cast<CharType>('0' + (n - tmp * 10)); //8% faster than using modulus operator!
+ *--it = static_cast<CharType>('0' + (tmp * 10 - n)); //8% faster than using modulus operator!
n = tmp;
}
while (n != 0);
- if (hasMinus)
- *--iter = static_cast<CharType>('-');
+ *--it = static_cast<CharType>('-');
+}
- return S(&*iter, std::end(buffer) - iter);
+template <class OutputIterator, class Num> inline
+void formatPositiveInteger(Num n, OutputIterator& it)
+{
+ assert(n >= 0);
+ typedef typename std::iterator_traits<OutputIterator>::value_type CharType;
+ do
+ {
+ const Num tmp = n / 10;
+ *--it = static_cast<CharType>('0' + (n - tmp * 10)); //8% faster than using modulus operator!
+ n = tmp;
+ }
+ while (n != 0);
}
+
template <class S, class Num> inline
S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>)
{
- return formatInteger<S>(number < 0 ? -number : number, number < 0);
- //bug for "INT_MIN"! technically -INT_MIN == INT_MIN -> not worth the trouble
+ typedef typename GetCharType<S>::Type CharType;
+ CharType buffer[2 + sizeof(Num) * 241 / 100]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
+ //required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1!
+ // <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41)
+
+ //caveat: consider INT_MIN: technically -INT_MIN == INT_MIN
+ auto it = std::end(buffer);
+ if (number < 0)
+ formatNegativeInteger(number, it);
+ else
+ formatPositiveInteger(number, it);
+ assert(it >= std::begin(buffer));
+
+ return S(&*it, std::end(buffer) - it);
}
template <class S, class Num> inline
S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>)
{
- return formatInteger<S>(number, false);
+ typedef typename GetCharType<S>::Type CharType;
+ CharType buffer[1 + sizeof(Num) * 241 / 100];
+ //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41)
+
+ auto it = std::end(buffer);
+ formatPositiveInteger(number, it);
+ assert(it >= std::begin(buffer));
+
+ return S(&*it, std::end(buffer) - it);
}
//--------------------------------------------------------------------------------
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 22aa2ffc..eb79831e 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index bbced0fa..bfd9e038 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -10,27 +10,27 @@
#include "scope_guard.h"
#include "file_error.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
#include "WinIoCtl.h"
#include "privilege.h"
#include "long_path_prefix.h"
#include "dll.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#include <unistd.h>
+#include <stdlib.h> //realpath
#endif
namespace zen
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
bool isSymlink(const WIN32_FIND_DATA& data); //*not* a simple FILE_ATTRIBUTE_REPARSE_POINT check!
bool isSymlink(DWORD fileAttributes, DWORD reparseTag);
-
-Zstring getResolvedFilePath(const Zstring& filename); //throw FileError; requires Vista or later!
#endif
+Zstring getResolvedFilePath(const Zstring& linkPath); //throw FileError; Win: requires Vista or later!
Zstring getSymlinkTargetRaw(const Zstring& linkPath); //throw FileError
}
@@ -84,7 +84,7 @@ namespace
Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileError
{
using namespace zen;
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
//FSCTL_GET_REPARSE_POINT: http://msdn.microsoft.com/en-us/library/aa364571(VS.85).aspx
//reading certain symlinks/junctions requires admin rights!
@@ -100,7 +100,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
nullptr);
if (hLink == INVALID_HANDLE_VALUE)
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), formatSystemError(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
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), formatSystemError(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"\n\n" + L"Not a symbolic link or junction!");
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), L"Not a symbolic link or junction!");
//absolute symlinks and junctions technically start with \??\ while relative ones do not
if (startsWith(output, Zstr("\\??\\")))
@@ -140,29 +140,29 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
return output;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
const size_t BUFFER_SIZE = 10000;
std::vector<char> buffer(BUFFER_SIZE);
const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE);
if (bytesWritten < 0 || bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation!
{
- std::wstring errorMessage = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath));
+ ErrorCode lastError = getLastError();
+ const std::wstring errorMsg = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath));
if (bytesWritten < 0)
- errorMessage += L"\n\n" + getLastErrorFormatted();
- throw FileError(errorMessage);
+ throw FileError(errorMsg, formatSystemError(L"readlink", lastError));
+ throw FileError(errorMsg);
}
return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination!
#endif
}
-#ifdef FFS_WIN
-Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError
+Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
{
using namespace zen;
-
- const HANDLE hDir = ::CreateFile(applyLongPathPrefix(filename).c_str(),
+#ifdef ZEN_WIN
+ const HANDLE hDir = ::CreateFile(applyLongPathPrefix(linkPath).c_str(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
@@ -170,7 +170,7 @@ Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError
FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
nullptr);
if (hDir == INVALID_HANDLE_VALUE)
- throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)");
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"CreateFile", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
//GetFinalPathNameByHandle() is not available before Vista!
@@ -178,11 +178,11 @@ Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError
const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
if (!getFinalPathNameByHandle)
- throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
const DWORD bufferSize = getFinalPathNameByHandle(hDir, nullptr, 0, 0);
if (bufferSize == 0)
- throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"GetFinalPathNameByHandle", getLastError()));
std::vector<wchar_t> targetPath(bufferSize);
const DWORD charsWritten = getFinalPathNameByHandle(hDir, //__in HANDLE hFile,
@@ -191,16 +191,23 @@ Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError
0); //__in DWORD dwFlags
if (charsWritten == 0 || charsWritten >= bufferSize)
{
- std::wstring errorMessage = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename));
+ const std::wstring errorMsg = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath));
if (charsWritten == 0)
- errorMessage += L"\n\n" + getLastErrorFormatted();
- throw FileError(errorMessage);
+ throw FileError(errorMsg, formatSystemError(L"GetFinalPathNameByHandle", getLastError()));
+ throw FileError(errorMsg);
}
return Zstring(&targetPath[0], charsWritten);
-}
+
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ char* targetPath = ::realpath(linkPath.c_str(), nullptr);
+ if (!targetPath)
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"realpath", getLastError()));
+ ZEN_ON_SCOPE_EXIT(::free(targetPath));
+ return targetPath;
#endif
}
+}
namespace zen
@@ -208,11 +215,10 @@ namespace zen
inline
Zstring getSymlinkTargetRaw(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); }
-
-#ifdef FFS_WIN
inline
-Zstring getResolvedFilePath(const Zstring& filename) { return getResolvedFilePath_impl(filename); }
+Zstring getResolvedFilePath(const Zstring& linkPath) { return getResolvedFilePath_impl(linkPath); }
+#ifdef ZEN_WIN
/*
Reparse Point Tags
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx
diff --git a/zen/last_error.h b/zen/sys_error.h
index ddee552f..bbac2eaa 100644
--- a/zen/last_error.h
+++ b/zen/sys_error.h
@@ -4,17 +4,18 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef SYSTEMFUNCTIONS_H_INCLUDED
-#define SYSTEMFUNCTIONS_H_INCLUDED
+#ifndef LAST_ERROR_H_3284791347018951324534
+#define LAST_ERROR_H_3284791347018951324534
#include <string>
#include "utf.h"
#include "i18n.h"
+#include "scope_guard.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
#include <cstring>
#include <cerrno>
#endif
@@ -23,18 +24,30 @@
namespace zen
{
//evaluate GetLastError()/errno and assemble specific error message
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
typedef DWORD ErrorCode;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
typedef int ErrorCode;
#endif
-std::wstring getLastErrorFormatted(ErrorCode lastError = 0);
ErrorCode getLastError();
+std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError);
+
bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" aliases
+//A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"!
+class SysError
+{
+public:
+ explicit SysError(const std::wstring& msg) : msg_(msg) {}
+ const std::wstring& toString() const { return msg_; }
+
+private:
+ std::wstring msg_;
+};
+
@@ -46,44 +59,50 @@ bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" al
inline
ErrorCode getLastError()
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
return ::GetLastError();
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
return errno; //don't use "::", errno is a macro!
#endif
}
+
+std::wstring formatSystemError(const std::wstring& functionName, long long lastError); //not implemented! intentional overload ambiguity to catch usage errors with HRESULT!
+
+
inline
-std::wstring getLastErrorFormatted(ErrorCode lastError)
+std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError)
{
- //determine error code if none was specified
+ //determine error code if none was specified -> still required??
if (lastError == 0)
lastError = getLastError();
- std::wstring output = _("Error Code %x:");
- replace(output, L"%x", numberTo<std::wstring>(lastError));
-#ifdef FFS_WIN
+ std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo<std::wstring>(lastError));
+
+#ifdef ZEN_WIN
LPWSTR buffer = nullptr;
if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_MAX_WIDTH_MASK |
FORMAT_MESSAGE_IGNORE_INSERTS | //important: without this flag ::FormatMessage() will fail if message contains placeholders
FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, lastError, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0)
- {
if (buffer) //"don't trust nobody"
{
+ ZEN_ON_SCOPE_EXIT(::LocalFree(buffer));
output += L" ";
output += buffer;
- ::LocalFree(buffer);
}
- }
::SetLastError(lastError); //restore last error
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
output += L" ";
output += utfCvrtTo<std::wstring>(::strerror(lastError));
errno = lastError; //restore errno
#endif
+ if (!endsWith(output, L" ")) //Windows messages seem to end with a blank...
+ output += L" ";
+ output += L"(" + functionName + L")";
+
return output;
}
@@ -91,15 +110,15 @@ std::wstring getLastErrorFormatted(ErrorCode lastError)
inline
bool errorCodeForNotExisting(ErrorCode lastError)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
return lastError == ERROR_FILE_NOT_FOUND ||
lastError == ERROR_PATH_NOT_FOUND ||
lastError == ERROR_BAD_NETPATH ||
lastError == ERROR_NETNAME_DELETED;
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined ZEN_LINUX || defined ZEN_MAC
return lastError == ENOENT;
#endif
}
}
-#endif // SYSTEMFUNCTIONS_H_INCLUDED
+#endif //LAST_ERROR_H_3284791347018951324534
diff --git a/zen/tick_count.h b/zen/tick_count.h
index d005a828..893d8e80 100644
--- a/zen/tick_count.h
+++ b/zen/tick_count.h
@@ -1,6 +1,6 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
-// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
@@ -10,13 +10,13 @@
#include <cstdint>
#include "type_traits.h"
#include "basic_math.h"
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
#include <time.h> //Posix ::clock_gettime()
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
#include <mach/mach_time.h>
#endif
//#include <algorithm>
@@ -57,11 +57,11 @@ TickVal getTicks(); //return invalid value on error: !TickVal::isValid()
class TickVal
{
public:
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
typedef LARGE_INTEGER NativeVal;
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
typedef timespec NativeVal;
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
typedef uint64_t NativeVal;
#endif
@@ -71,16 +71,16 @@ public:
inline friend
std::int64_t dist(const TickVal& lhs, const TickVal& rhs)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow!
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
const auto distSec = numeric::dist(lhs.val_.tv_sec, rhs.val_.tv_sec);
const auto distNsec = numeric::dist(lhs.val_.tv_nsec, rhs.val_.tv_nsec);
if (distSec > (std::numeric_limits<std::int64_t>::max() - distNsec) / 1000000000) //truncate instead of overflow!
return std::numeric_limits<std::int64_t>::max();
return distSec * 1000000000 + distNsec;
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
return numeric::dist(lhs.val_, rhs.val_);
#endif
}
@@ -88,13 +88,13 @@ public:
inline friend
bool operator<(const TickVal& lhs, const TickVal& rhs)
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
return lhs.val_.QuadPart < rhs.val_.QuadPart;
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
if (lhs.val_.tv_sec != rhs.val_.tv_sec)
return lhs.val_.tv_sec < rhs.val_.tv_sec;
return lhs.val_.tv_nsec < rhs.val_.tv_nsec;
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
return lhs.val_ < rhs.val_;
#endif
}
@@ -109,17 +109,17 @@ private:
inline
std::int64_t ticksPerSec() //return 0 on error
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
LARGE_INTEGER frequency = {};
if (!::QueryPerformanceFrequency(&frequency)) //MSDN promises: "The frequency cannot change while the system is running."
return 0;
static_assert(sizeof(std::int64_t) >= sizeof(frequency.QuadPart), "");
return frequency.QuadPart;
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
return 1000000000; //precision: nanoseconds
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
mach_timebase_info_data_t tbi = {};
if (::mach_timebase_info(&tbi) != KERN_SUCCESS)
return 0;
@@ -131,18 +131,18 @@ std::int64_t ticksPerSec() //return 0 on error
inline
TickVal getTicks() //return !isValid() on error
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
LARGE_INTEGER now = {};
if (!::QueryPerformanceCounter(&now)) //msdn: SetThreadAffinityMask() may be required if there are bugs in BIOS or HAL"
return TickVal();
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
//gettimeofday() seems fine but is deprecated
timespec now = {};
if (::clock_gettime(CLOCK_MONOTONIC_RAW, &now) != 0) //CLOCK_MONOTONIC measures time reliably across processors!
return TickVal();
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
uint64_t now = ::mach_absolute_time(); //can this call fail???
#endif
return TickVal(now);
diff --git a/zen/time.h b/zen/time.h
index 80481856..e94300a0 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -1,6 +1,6 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
-// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
@@ -197,8 +197,8 @@ template <class CharType> inline
size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const struct std::tm* timeptr)
{
#if defined _MSC_VER && !defined NDEBUG
- //it's no use: application init must register an invalid parameter that does nothing !!!
- //=> strftime will abort with 0 and set errno to EINVAL instead of CRASH THE APPLICATION!
+ //there's no way around: application init must register an invalid parameter handler that does nothing !!!
+ //=> strftime will abort with 0 and set errno to EINVAL instead of CRASHING THE APPLICATION!
_invalid_parameter_handler oldHandler = _set_invalid_parameter_handler(nullptr);
assert(oldHandler);
_set_invalid_parameter_handler(oldHandler);
diff --git a/zen/type_tools.h b/zen/type_tools.h
index c83e8f1e..e5ce29bd 100644
--- a/zen/type_tools.h
+++ b/zen/type_tools.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
diff --git a/zen/type_traits.h b/zen/type_traits.h
index 15352e8c..4f71f961 100644
--- a/zen/type_traits.h
+++ b/zen/type_traits.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
@@ -80,11 +80,6 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat
-
-
-
-
-
//################ implementation ######################
#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : StaticBool<true> {};
diff --git a/zen/utf.h b/zen/utf.h
index 8da588cd..e428422b 100644
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -1,5 +1,5 @@
// **************************************************************************
-// * This file is part of the zenXML project. It is distributed under the *
+// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 085e6f72..a469ade2 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -7,11 +7,11 @@
#include "zstring.h"
#include <stdexcept>
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
#include "dll.h"
#include "win_ver.h"
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
//#include <zen/scope_guard.h>
#include <ctype.h> //toupper()
#endif
@@ -64,7 +64,7 @@ private:
const std::string message = std::string("Memory leak detected!") + "\n\n"
+ "Candidates:\n" + leakingStrings;
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
MessageBoxA(nullptr, message.c_str(), "Error", 0);
#else
std::cerr << message;
@@ -85,7 +85,7 @@ private:
void reportProblem(const std::string& message) //throw std::logic_error
{
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
::MessageBoxA(nullptr, message.c_str(), "Error", 0);
#else
std::cerr << message;
@@ -128,7 +128,7 @@ time per call | function
*/
-#ifdef FFS_WIN
+#ifdef ZEN_WIN
namespace
{
#ifdef __MINGW32__ //MinGW is clueless...
@@ -227,7 +227,7 @@ void z_impl::makeFilenameUpperCase(wchar_t* str, size_t size)
throw std::runtime_error("Error converting to upper case! (LCMapString)");
}
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
int z_impl::compareFilenamesNoCase(const char* lhs, const char* rhs, size_t sizeLhs, size_t sizeRhs)
{
return ::strcasecmp(lhs, rhs); //locale-dependent!
diff --git a/zen/zstring.h b/zen/zstring.h
index 435f03a2..80c267e3 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -8,9 +8,9 @@
#define ZSTRING_H_INCLUDED
#include "string_base.h"
-#ifdef FFS_LINUX
+#ifdef ZEN_LINUX
#include <cstring> //strcmp
-#elif defined FFS_MAC
+#elif defined ZEN_MAC
//#include <strings.h> //strcasecmp
#endif
@@ -49,12 +49,12 @@ public:
//############################## helper functions #############################################
-#ifdef FFS_WIN //Windows encodes Unicode as UTF-16 wchar_t
+#ifdef ZEN_WIN //Windows encodes Unicode as UTF-16 wchar_t
typedef wchar_t Zchar;
#define Zstr(x) L ## x
const Zchar FILE_NAME_SEPARATOR = L'\\';
-#elif defined FFS_LINUX || defined FFS_MAC //Linux uses UTF-8
+#elif defined ZEN_LINUX || defined ZEN_MAC //Linux uses UTF-8
typedef char Zchar;
#define Zstr(x) x
const Zchar FILE_NAME_SEPARATOR = '/';
@@ -82,7 +82,7 @@ struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux
bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) == 0; }
};
-#if defined FFS_WIN || defined FFS_MAC
+#if defined ZEN_WIN || defined ZEN_MAC
template <template <class, class> class SP, class AP>
void makeUpper(zen::Zbase<Zchar, SP, AP>& str);
#endif
@@ -101,7 +101,7 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
//################################# inline implementation ########################################
namespace z_impl
{
-#if defined FFS_WIN || defined FFS_MAC
+#if defined ZEN_WIN || defined ZEN_MAC
int compareFilenamesNoCase(const Zchar* lhs, const Zchar* rhs, size_t sizeLhs, size_t sizeRhs);
void makeFilenameUpperCase(Zchar* str, size_t size);
#endif
@@ -111,17 +111,17 @@ void makeFilenameUpperCase(Zchar* str, size_t size);
template <template <class, class> class SP, class AP> inline
int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs)
{
-#if defined FFS_WIN || defined FFS_MAC
+#if defined ZEN_WIN || defined ZEN_MAC
return z_impl::compareFilenamesNoCase(lhs.data(), rhs.data(), lhs.length(), rhs.length());
-#elif defined FFS_LINUX
+#elif defined ZEN_LINUX
return std::strcmp(lhs.c_str(), rhs.c_str()); //POSIX filenames don't have embedded 0
- //#elif defined FFS_MAC
+ //#elif defined ZEN_MAC
// return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent!
#endif
}
-#if defined FFS_WIN || defined FFS_MAC
+#if defined ZEN_WIN || defined ZEN_MAC
template <template <class, class> class SP, class AP> inline
void makeUpper(zen::Zbase<Zchar, SP, AP>& str)
{
bgstack15