diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:19:49 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:19:49 +0200 |
commit | c8e0e909b4a8d18319fc65434a10dc446434817c (patch) | |
tree | eee91e7d2ce229dd043811eae8f1e2bd78061916 /lib | |
parent | 5.2 (diff) | |
download | FreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.tar.gz FreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.tar.bz2 FreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.zip |
5.3
Diffstat (limited to 'lib')
38 files changed, 822 insertions, 1494 deletions
diff --git a/lib/IFileOperation/FileOperation_Vista.vcxproj b/lib/IFileOperation/FileOperation_Vista.vcxproj deleted file mode 100644 index 3d454a0b..00000000 --- a/lib/IFileOperation/FileOperation_Vista.vcxproj +++ /dev/null @@ -1,240 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectName>Vista IFileOperation</ProjectName> - <ProjectGuid>{70394AEF-5897-4911-AFA1-82EAF0581EFA}</ProjectGuid> - <RootNamespace>ShadowDll</RootNamespace> - <Keyword>Win32Proj</Keyword> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <CharacterSet>Unicode</CharacterSet> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>Windows7.1SDK</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>Windows7.1SDK</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <CharacterSet>Unicode</CharacterSet> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>Windows7.1SDK</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>Windows7.1SDK</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_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> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">FileOperation_$(Platform)</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">FileOperation_$(Platform)</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">FileOperation_$(Platform)</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">FileOperation_$(Platform)</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <BuildLog> - <Path>$(IntDir)Build.html</Path> - </BuildLog> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> - <SubSystem>Windows</SubSystem> - <ProfileGuidedDatabase> - </ProfileGuidedDatabase> - <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> - <TargetMachine>MachineX86</TargetMachine> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <BuildLog> - <Path>$(IntDir)Build.html</Path> - </BuildLog> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> - <SubSystem>Windows</SubSystem> - <ProfileGuidedDatabase> - </ProfileGuidedDatabase> - <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> - <TargetMachine>MachineX64</TargetMachine> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <BuildLog> - <Path>$(IntDir)Build.html</Path> - </BuildLog> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> - <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>false</GenerateDebugInformation> - <SubSystem>Windows</SubSystem> - <OptimizeReferences>true</OptimizeReferences> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> - <ProfileGuidedDatabase> - </ProfileGuidedDatabase> - <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> - <TargetMachine>MachineX86</TargetMachine> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <BuildLog> - <Path>$(IntDir)Build.html</Path> - </BuildLog> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> - <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>false</GenerateDebugInformation> - <SubSystem>Windows</SubSystem> - <OptimizeReferences>true</OptimizeReferences> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> - <ProfileGuidedDatabase> - </ProfileGuidedDatabase> - <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> - <TargetMachine>MachineX64</TargetMachine> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="dll_main.cpp"> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - </PrecompiledHeader> - <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - </PrecompiledHeader> - <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - </PrecompiledHeader> - <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - </PrecompiledHeader> - <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged> - </ClCompile> - <ClCompile Include="file_op.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="file_op.h" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project>
\ No newline at end of file diff --git a/lib/IFileOperation/dll_main.cpp b/lib/IFileOperation/dll_main.cpp deleted file mode 100644 index 46c65311..00000000 --- a/lib/IFileOperation/dll_main.cpp +++ /dev/null @@ -1,25 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -//optional: add init/teardown logic here -BOOL APIENTRY DllMain(HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) -{ - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - case DLL_PROCESS_DETACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - break; - } - return TRUE; -} diff --git a/lib/IFileOperation/file_op.cpp b/lib/IFileOperation/file_op.cpp deleted file mode 100644 index 3d7717f4..00000000 --- a/lib/IFileOperation/file_op.cpp +++ /dev/null @@ -1,190 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "file_op.h" -#include <algorithm> -#include <string> -#define WIN32_LEAN_AND_MEAN -#include <zen/com_ptr.h> -#include <zen/com_error.h> - -#include <shellapi.h> //shell constants such as FO_* values -#include <shobjidl.h> - -using namespace zen; - - -namespace -{ -void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError - size_t fileNo) //size of fileNames array -{ - ComPtr<IFileOperation> fileOp; - ZEN_CHECK_COM(::CoCreateInstance(CLSID_FileOperation, //throw ComError - nullptr, - CLSCTX_ALL, - IID_PPV_ARGS(fileOp.init()))); - - // Set the operation flags. Turn off all UI - // from being shown to the user during the - // operation. This includes error, confirmation - // and progress dialogs. - ZEN_CHECK_COM(fileOp->SetOperationFlags(FOF_ALLOWUNDO | //throw ComError - FOF_NOCONFIRMATION | - FOF_SILENT | - FOFX_EARLYFAILURE | - FOF_NOERRORUI)); - - int operationCount = 0; - - for (size_t i = 0; i < fileNo; ++i) - { - //create file/folder item object - ComPtr<IShellItem> psiFile; - HRESULT hr = ::SHCreateItemFromParsingName(fileNames[i], - nullptr, - IID_PPV_ARGS(psiFile.init())); - if (FAILED(hr)) - { - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || //file not existing anymore - hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) - continue; - throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\"" + fileNames[i] + L"\".", hr); - } - - ZEN_CHECK_COM(fileOp->DeleteItem(psiFile.get(), nullptr)); - - ++operationCount; - } - - if (operationCount == 0) //calling PerformOperations() without anything to do would result in E_UNEXPECTED - return; - - //perform actual operations - ZEN_CHECK_COM(fileOp->PerformOperations()); - - //check if errors occured: if FOFX_EARLYFAILURE is not used, PerformOperations() can return with success despite errors! - BOOL pfAnyOperationsAborted = FALSE; - ZEN_CHECK_COM(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted)); - - if (pfAnyOperationsAborted == TRUE) - throw ComError(L"Operation did not complete successfully."); -} - - -void copyFile(const wchar_t* sourceFile, //throw ComError - const wchar_t* targetFile) -{ - ComPtr<IFileOperation> fileOp; - ZEN_CHECK_COM(::CoCreateInstance(CLSID_FileOperation, //throw ComError - nullptr, - CLSCTX_ALL, - IID_PPV_ARGS(fileOp.init()))); - - // Set the operation flags. Turn off all UI - // from being shown to the user during the - // operation. This includes error, confirmation - // and progress dialogs. - ZEN_CHECK_COM(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw ComError - FOF_SILENT | - FOFX_EARLYFAILURE | - FOF_NOERRORUI)); - //create source object - ComPtr<IShellItem> psiSourceFile; - { - HRESULT hr = ::SHCreateItemFromParsingName(sourceFile, - nullptr, - IID_PPV_ARGS(psiSourceFile.init())); - if (FAILED(hr)) - throw ComError(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."); - - const std::wstring targetFolder(targetFile, pos); - const std::wstring targetFileNameShort = targetFile + pos + 1; - - //create target folder object - ComPtr<IShellItem> psiTargetFolder; - { - HRESULT hr = ::SHCreateItemFromParsingName(targetFolder.c_str(), - nullptr, - IID_PPV_ARGS(psiTargetFolder.init())); - if (FAILED(hr)) - throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for folder:\n") + L"\"" + targetFolder + L"\".", hr); - } - - //schedule file copy operation - ZEN_CHECK_COM(fileOp->CopyItem(psiSourceFile.get(), psiTargetFolder.get(), targetFileNameShort.c_str(), nullptr)); - - //perform actual operations - ZEN_CHECK_COM(fileOp->PerformOperations()); - - //check if errors occured: if FOFX_EARLYFAILURE is not used, PerformOperations() can return with success despite errors! - BOOL pfAnyOperationsAborted = FALSE; - ZEN_CHECK_COM(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted)); - - if (pfAnyOperationsAborted == TRUE) - throw ComError(L"Operation did not complete successfully."); -} - - -inline -void copyString(const std::wstring& input, wchar_t* buffer, size_t bufferSize) -{ - if (bufferSize > 0) - { - //size_t endPos = input.copy(buffer, bufferSize - 1); - //buffer[endPos] = 0; - const size_t maxSize = std::min(input.length(), bufferSize - 1); - std::copy(input.begin(), input.begin() + maxSize, buffer); - buffer[maxSize] = 0; - } -} - -std::wstring lastErrorMessage; //this should really be thread-local!!! -} - - -bool fileop::moveToRecycleBin(const wchar_t* fileNames[], - size_t fileNo) //size of fileNames array -{ - try - { - ::moveToRecycleBin(fileNames, fileNo); //throw ComError - return true; - } - catch (const zen::ComError& e) - { - lastErrorMessage = e.toString(); - return false; - } -} - - -bool fileop::copyFile(const wchar_t* sourceFile, - const wchar_t* targetFile) -{ - try - { - ::copyFile(sourceFile, targetFile); //throw ComError - return true; - } - catch (const zen::ComError& e) - { - lastErrorMessage = e.toString(); - return false; - } -} - - -//if any of the functions above returns 'false', this message returns last error -void fileop::getLastError(wchar_t* errorMessage, size_t errorBufferLen) -{ - copyString(lastErrorMessage, errorMessage, errorBufferLen); -} diff --git a/lib/IFileOperation/file_op.h b/lib/IFileOperation/file_op.h deleted file mode 100644 index 530226d3..00000000 --- a/lib/IFileOperation/file_op.h +++ /dev/null @@ -1,62 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef RECYCLER_DLL_H -#define RECYCLER_DLL_H - -#ifdef FILE_OP_DLL_EXPORTS -#define FILE_OP_DLL_API extern "C" __declspec(dllexport) -#else -#define FILE_OP_DLL_API extern "C" __declspec(dllimport) -#endif - -#include <zen/build_info.h> - - -namespace fileop -{ -/*-------------- - |declarations| - --------------*/ - -//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize - -FILE_OP_DLL_API -bool moveToRecycleBin(const wchar_t* fileNames[], - size_t fileNo); //size of fileNames array - -FILE_OP_DLL_API -bool copyFile(const wchar_t* sourceFile, - const wchar_t* targetFile); - -//if any of the functions above returns 'false', this message returns last error -FILE_OP_DLL_API -void getLastError(wchar_t* errorMessage, size_t errorBufferLen); - -/*---------- - |typedefs| - ----------*/ -typedef bool (*MoveToRecycleBinFct)(const wchar_t* fileNames[], size_t fileNo); -typedef bool (*CopyFileFct)(const wchar_t* sourceFile, const wchar_t* targetFile); -typedef void (*GetLastErrorFct)(wchar_t* errorMessage, size_t errorBufferLen); - -/*-------------- - |symbol names| - --------------*/ -//(use const pointers to ensure internal linkage) -const char moveToRecycleBinFctName[] = "moveToRecycleBin"; -const char copyFileFctName[] = "copyFile"; -const char getLastErrorFctName[] = "getLastError"; - -/*--------------- - |library names| - ---------------*/ -inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"FileOperation_x64.dll" : L"FileOperation_Win32.dll"; } -} - - - -#endif //RECYCLER_DLL_H diff --git a/lib/ShadowCopy/LockFile.cpp b/lib/ShadowCopy/LockFile.cpp index b9008863..aac4c170 100644 --- a/lib/ShadowCopy/LockFile.cpp +++ b/lib/ShadowCopy/LockFile.cpp @@ -20,10 +20,9 @@ int wmain(int argc, wchar_t* argv[]) } std::wstring filename = argv[1]; - //obtain exclusive lock on test file HANDLE hFile = ::CreateFile(filename.c_str(), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, + GENERIC_READ, + 0, //obtain *exclusive* lock on test file nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, diff --git a/lib/ShadowCopy/Shadow_Server2003.vcxproj b/lib/ShadowCopy/Shadow_Server2003.vcxproj index a893a389..ad24d4c1 100644 --- a/lib/ShadowCopy/Shadow_Server2003.vcxproj +++ b/lib/ShadowCopy/Shadow_Server2003.vcxproj @@ -99,7 +99,7 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -111,6 +111,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX86</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> @@ -132,7 +133,7 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -144,6 +145,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage64\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> @@ -163,7 +165,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -177,6 +179,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX86</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -199,7 +202,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -213,6 +216,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage64\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/lib/ShadowCopy/Shadow_Windows7.vcxproj b/lib/ShadowCopy/Shadow_Windows7.vcxproj index 2392fa99..5381372b 100644 --- a/lib/ShadowCopy/Shadow_Windows7.vcxproj +++ b/lib/ShadowCopy/Shadow_Windows7.vcxproj @@ -99,7 +99,7 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -111,6 +111,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX86</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> @@ -131,7 +132,7 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> @@ -144,6 +145,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage64\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> @@ -163,7 +165,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -177,6 +179,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX86</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -198,7 +201,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> @@ -213,6 +216,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage64\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/lib/ShadowCopy/Shadow_XP.vcxproj b/lib/ShadowCopy/Shadow_XP.vcxproj index e49e8941..fce942d5 100644 --- a/lib/ShadowCopy/Shadow_XP.vcxproj +++ b/lib/ShadowCopy/Shadow_XP.vcxproj @@ -99,7 +99,7 @@ <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -111,6 +111,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX86</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> @@ -133,7 +134,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -145,6 +146,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage64\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> @@ -164,7 +166,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -178,6 +180,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX86</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -200,7 +203,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings> - <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -214,6 +217,7 @@ </ProfileGuidedDatabase> <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> + <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage64\lib</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/lib/ShadowCopy/shadow.cpp b/lib/ShadowCopy/shadow.cpp index bc27e2c3..c47ce983 100644 --- a/lib/ShadowCopy/shadow.cpp +++ b/lib/ShadowCopy/shadow.cpp @@ -10,6 +10,7 @@ #include <zen/com_ptr.h> #include <zen/com_error.h> #include <zen/scope_guard.h> +#include <boost/thread/tss.hpp> #ifdef USE_SHADOW_XP #include "xp/inc/vss.h" @@ -74,21 +75,6 @@ std::wstring formatVssError(HRESULT hr) //at least the one's from IVssBackupComp } - -inline -void copyString(const std::wstring& input, wchar_t* buffer, size_t bufferSize) -{ - if (bufferSize > 0) - { - //size_t endPos = input.copy(buffer, bufferSize - 1); - //buffer[endPos] = 0; - const size_t maxSize = std::min(input.length(), bufferSize - 1); - std::copy(input.begin(), input.begin() + maxSize, buffer); - buffer[maxSize] = 0; - } -} - - shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError { ComPtr<IVssBackupComponents> backupComp; @@ -156,12 +142,12 @@ shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError //finally: write volume name of newly created shadow copy return shadow::ShadowData(backupComp, props.m_pwszSnapshotDeviceObject); } + +boost::thread_specific_ptr<std::wstring> lastErrorMessage; //use "thread_local" in C++11 } -shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName, - wchar_t* errorMessage, - unsigned int errorBufferLen) +shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName) { try { @@ -170,17 +156,15 @@ shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName, } catch (const zen::ComError& e) { - copyString(e.toString(), errorMessage, errorBufferLen); + lastErrorMessage.reset(new std::wstring(e.toString())); return nullptr; } } -void shadow::getShadowVolume(shadow::ShadowHandle handle, - wchar_t* buffer, - unsigned int bufferLen) +const wchar_t* shadow::getShadowVolume(shadow::ShadowHandle handle) { - copyString(handle->shadowVolume_, buffer, bufferLen); + return handle ? handle->shadowVolume_.c_str() : nullptr; //better fail in client code than here! } @@ -188,3 +172,9 @@ void shadow::releaseShadowCopy(ShadowHandle handle) { delete handle; } + + +const wchar_t* shadow::getLastError() +{ + return !lastErrorMessage.get() ? L"" : lastErrorMessage->c_str(); +} diff --git a/lib/ShadowCopy/shadow.h b/lib/ShadowCopy/shadow.h index ea113dae..66d29300 100644 --- a/lib/ShadowCopy/shadow.h +++ b/lib/ShadowCopy/shadow.h @@ -31,44 +31,37 @@ typedef ShadowData* ShadowHandle; //volumeName *must* end with "\" SHADOWDLL_API -ShadowHandle createShadowCopy(const wchar_t* volumeName, // in Returns nullptr on failure! - wchar_t* errorMessage, // out - unsigned int errorBufferLen); // in +ShadowHandle createShadowCopy(const wchar_t* volumeName); //returns nullptr on failure! -//don't forget to release the backupHandle after shadow copy is not needed anymore! +//release the backupHandle after shadow copy is not needed anymore! SHADOWDLL_API void releaseShadowCopy(ShadowHandle handle); SHADOWDLL_API -void getShadowVolume(ShadowHandle handle, //shadowVolName does *not* end with "\" - wchar_t* buffer, - unsigned int bufferLen); +const wchar_t* getShadowVolume(ShadowHandle handle); //never fails, returns shadowVolName, never ending with "\" +//get last error message if any of the functions above fail +SHADOWDLL_API +const wchar_t* getLastError(); //no nullptr check required! //########################################################################################## /*---------- |typedefs| ----------*/ -typedef ShadowHandle (*CreateShadowCopyFct)(const wchar_t* volumeName, - wchar_t* errorMessage, - unsigned int errorBufferLen); - -typedef void (*ReleaseShadowCopyFct)(ShadowHandle handle); - -typedef void (*GetShadowVolumeFct)(ShadowHandle handle, - wchar_t* buffer, - unsigned int bufferLen); - +typedef ShadowHandle (*FunType_createShadowCopy )(const wchar_t* volumeName); +typedef void (*FunType_releaseShadowCopy)(ShadowHandle handle); +typedef const wchar_t* (*FunType_getShadowVolume )(ShadowHandle handle); +typedef const wchar_t* (*FunType_getLastError)(); /*-------------- |symbol names| --------------*/ //(use const pointers to ensure internal linkage) -const char createShadowCopyFctName [] = "createShadowCopy"; -const char releaseShadowCopyFctName[] = "releaseShadowCopy"; -const char getShadowVolumeFctName [] = "getShadowVolume"; - +const char funName_createShadowCopy [] = "createShadowCopy"; +const char funName_releaseShadowCopy[] = "releaseShadowCopy"; +const char funName_getShadowVolume [] = "getShadowVolume"; +const char funName_getLastError [] = "getLastError"; /*--------------- |library names| ---------------*/ diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp index 0176f89b..d35f15c3 100644 --- a/lib/Thumbnail/thumbnail.cpp +++ b/lib/Thumbnail/thumbnail.cpp @@ -27,22 +27,22 @@ thumb::HICON thumb::getThumbnail(const wchar_t* filename, int requestedSize) //r { const std::wstring filenameStr(filename); - ComPtr<IShellFolder> shellFolder; + ComPtr<IShellFolder> desktopFolder; { - HRESULT hr = ::SHGetDesktopFolder(shellFolder.init()); - if (FAILED(hr) || !shellFolder) + HRESULT hr = ::SHGetDesktopFolder(desktopFolder.init()); + if (FAILED(hr) || !desktopFolder) return nullptr; } PIDLIST_RELATIVE pidlFolder = nullptr; { - const std::wstring& pathName = beforeLast(filenameStr, '\\'); - HRESULT hr = shellFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, - nullptr, // [in] IBindCtx *pbc, - const_cast<LPWSTR>(pathName.c_str()), // [in] LPWSTR pszDisplayName, - nullptr, // [out] ULONG *pchEaten, - &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl, - nullptr); // [in, out] ULONG *pdwAttributes + const std::wstring& pathName = beforeLast(filenameStr, L'\\'); + HRESULT hr = desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, + nullptr, // [in] IBindCtx *pbc, + const_cast<LPWSTR>(pathName.c_str()), // [in] LPWSTR pszDisplayName, + nullptr, // [out] ULONG *pchEaten, + &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl, + nullptr); // [in, out] ULONG *pdwAttributes if (FAILED(hr) || !pidlFolder) return nullptr; } @@ -50,16 +50,16 @@ thumb::HICON thumb::getThumbnail(const wchar_t* filename, int requestedSize) //r ComPtr<IShellFolder> imageFolder; { - HRESULT hr = shellFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl, - nullptr, - IID_PPV_ARGS(imageFolder.init())); + HRESULT hr = desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl, + nullptr, // [in] IBindCtx *pbc, + IID_PPV_ARGS(imageFolder.init())); if (FAILED(hr) || !imageFolder) return nullptr; } PIDLIST_RELATIVE pidImage = nullptr; { - const std::wstring& shortName = afterLast(filenameStr, '\\'); + const std::wstring& shortName = afterLast(filenameStr, L'\\'); HRESULT hr = imageFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, nullptr, // [in] IBindCtx *pbc, const_cast<LPWSTR>(shortName.c_str()), // [in] LPWSTR pszDisplayName, diff --git a/lib/Thumbnail/thumbnail.h b/lib/Thumbnail/thumbnail.h index b8b95d8f..6c3575b5 100644 --- a/lib/Thumbnail/thumbnail.h +++ b/lib/Thumbnail/thumbnail.h @@ -49,15 +49,15 @@ HICON getIconByIndex(int iconIndex, int shilIconType); //return 0 on failure, ca /*---------- |typedefs| ----------*/ -typedef HICON (*GetThumbnailFct )(const wchar_t* filename, int requestedSize); -typedef HICON (*GetIconByIndexFct)(int iconIndex, int shilIconType); +typedef HICON (*FunType_getThumbnail )(const wchar_t* filename, int requestedSize); +typedef HICON (*FunType_getIconByIndex)(int iconIndex, int shilIconType); /*-------------- |symbol names| --------------*/ //(use const pointers to ensure internal linkage) -const char getThumbnailFctName [] = "getThumbnail"; -const char getIconByIndexFctName [] = "getIconByIndex"; +const char funName_getThumbnail [] = "getThumbnail"; +const char funName_getIconByIndex[] = "getIconByIndex"; /*--------------- |library names| diff --git a/lib/binary.cpp b/lib/binary.cpp index 1da93ee6..c01bb4d2 100644 --- a/lib/binary.cpp +++ b/lib/binary.cpp @@ -113,12 +113,12 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) return false; - bytesCompared += length1 * 2; + bytesCompared += length1; callback.updateCompareStatus(bytesCompared); //send progress updates } while (!file1.eof()); - if (!file2.eof()) //highly unlikely, but theoretically possible! (but then again, not in this context where both files have same size...) + if (!file2.eof()) //highly unlikely, but possible! (but then again, not in this context where both files have same size...) return false; return true; diff --git a/lib/binary.h b/lib/binary.h index 1fcfdf57..b419e9e2 100644 --- a/lib/binary.h +++ b/lib/binary.h @@ -19,7 +19,7 @@ class CompareCallback { public: virtual ~CompareCallback() {} - virtual void updateCompareStatus(zen::UInt64 totalBytesTransferred) = 0; + virtual void updateCompareStatus(zen::UInt64 totalBytes) = 0; }; bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError diff --git a/lib/db_file.cpp b/lib/db_file.cpp index 8ae8f94b..1bc75d82 100644 --- a/lib/db_file.cpp +++ b/lib/db_file.cpp @@ -66,7 +66,7 @@ public: CheckedDbReader(wxInputStream& stream, const Zstring& errorObjName) : CheckedReader(stream), errorObjName_(errorObjName) {} private: - virtual void throwException() const { throw FileError(_("Error reading from synchronization database:") + L" \n" + L"\"" + errorObjName_ + L"\""); } + virtual void throwException() const { throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(errorObjName_))); } const Zstring errorObjName_; }; @@ -77,7 +77,8 @@ class CheckedDbWriter : public CheckedWriter public: CheckedDbWriter(wxOutputStream& stream, const Zstring& errorObjName) : CheckedWriter(stream), errorObjName_(errorObjName) {} - virtual void throwException() const { throw FileError(_("Error writing to synchronization database:") + L" \n" + L"\"" + errorObjName_ + L"\""); } +private: + virtual void throwException() const { throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(errorObjName_))); } const Zstring errorObjName_; }; @@ -96,7 +97,7 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError rawStream.Read(formatDescr, sizeof(formatDescr)); //throw FileError if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) - throw FileError(_("Incompatible synchronization database format:") + L" \n" + L"\"" + filename + L"\""); + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); wxZlibInputStream decompressed(rawStream, wxZLIB_ZLIB); @@ -104,7 +105,7 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError std::int32_t version = cr.readPOD<std::int32_t>(); if (version != FILE_FORMAT_VER) //read file format version# - throw FileError(_("Incompatible synchronization database format:") + L" \n" + L"\"" + filename + L"\""); + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); //read stream lists StreamMapping output; @@ -122,13 +123,12 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError } catch (ErrorNotExisting&) { - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" + - _("One of the FreeFileSync database files is not yet existing:") + L" \n" + - L"\"" + filename + L"\""); + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename))); } - catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file + catch (const std::bad_alloc& e) { - throw FileError(_("Error reading from synchronization database:") + L" (bad alloc)"); + throw FileError(_("Out of memory!") + L" " + utf8CvrtTo<std::wstring>(e.what())); } } @@ -146,9 +146,9 @@ public: StreamParser(buffer, fileName, *dirInfo); //throw FileError return dirInfo; } - catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file + catch (const std::bad_alloc& e) { - throw FileError(_("Error reading from synchronization database:") + L" (bad alloc)"); + throw FileError(_("Out of memory!") + L" " + utf8CvrtTo<std::wstring>(e.what())); } } @@ -168,9 +168,9 @@ private: assert_static(sizeof(FileId().first ) <= sizeof(std::uint64_t)); assert_static(sizeof(FileId().second) <= sizeof(std::uint64_t)); - const auto devId = static_cast<decltype(FileId().first )>(readPOD<std::uint64_t>()); // - const auto fId = static_cast<decltype(FileId().second)>(readPOD<std::uint64_t>()); //silence "loss of precision" compiler warnings - return std::make_pair(devId, fId); + const auto deviceId = static_cast<decltype(FileId().first )>(readPOD<std::uint64_t>()); // + const auto fileId = static_cast<decltype(FileId().second)>(readPOD<std::uint64_t>()); //silence "loss of precision" compiler warnings + return std::make_pair(deviceId, fileId); } void recurse(DirContainer& dirCont) const @@ -438,10 +438,8 @@ std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMa } } - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" + - _("Database files do not share a common synchronization session:") + L" \n" + - L"\"" + fileNameLeft + L"\"\n" + - L"\"" + fileNameRight + L"\""); + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + + _("Database files do not share a common session.")); } diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h index f3826eb3..40ddffc7 100644 --- a/lib/dir_exist_async.h +++ b/lib/dir_exist_async.h @@ -9,7 +9,8 @@ #include <zen/thread.h> #include <zen/file_handling.h> -#include "status_handler.h" +//#include "status_handler.h" +#include "process_callback.h" #include <zen/file_error.h> #include "resolve_path.h" @@ -20,7 +21,7 @@ bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, Proces { using namespace zen; - procCallback.reportStatus(replaceCpy(_("Searching for directory %x..."), L"%x", std::wstring(L"\"") + dirname + L"\"", false)); + procCallback.reportStatus(replaceCpy(_("Searching for directory %x..."), L"%x", fmtFileName(dirname), false)); auto ft = async([=]() -> bool { diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp index 87864ce4..402892c6 100644 --- a/lib/dir_lock.cpp +++ b/lib/dir_lock.cpp @@ -25,6 +25,8 @@ #include <tlhelp32.h> #include <zen/win.h> //includes "windows.h" #include <zen/long_path_prefix.h> +#include <Sddl.h> //login sid +#include <Lmcons.h> //UNLEN #elif defined FFS_LINUX #include <sys/stat.h> @@ -37,12 +39,12 @@ using namespace std::rel_ops; namespace { -const size_t EMIT_LIFE_SIGN_INTERVAL = 5000; //show life sign; unit [ms] -const size_t POLL_LIFE_SIGN_INTERVAL = 6000; //poll for life sign; unit [ms] -const size_t DETECT_EXITUS_INTERVAL = 30000; //assume abandoned lock; unit [ms] +const size_t EMIT_LIFE_SIGN_INTERVAL = 5000; //show life sign; unit: [ms] +const size_t POLL_LIFE_SIGN_INTERVAL = 4000; //poll for life sign; unit: [ms] +const size_t DETECT_ABANDONED_INTERVAL = 30000; //assume abandoned lock; unit: [ms] const char LOCK_FORMAT_DESCR[] = "FreeFileSync"; -const int LOCK_FORMAT_VER = 1; //lock file format version +const int LOCK_FORMAT_VER = 2; //lock file format version } //worker thread @@ -66,7 +68,7 @@ public: } catch (const std::exception& e) //exceptions must be catched per thread { - wxSafeShowMessage(wxString(_("An exception occurred!")) + wxT("(Dirlock)"), utf8CvrtTo<wxString>(e.what())); //simple wxMessageBox won't do for threads + wxSafeShowMessage(_("An exception occurred!") + L" (Dirlock)", utf8CvrtTo<wxString>(e.what())); //simple wxMessageBox won't do for threads } } @@ -97,9 +99,10 @@ public: return; DWORD bytesWritten = 0; + /*bool rv = */ ::WriteFile(fileHandle, //__in HANDLE hFile, buffer, //__out LPVOID lpBuffer, - 1, //__in DWORD nNumberOfBytesToRead, + 1, //__in DWORD nNumberOfBytesToWrite, &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, nullptr); //__inout_opt LPOVERLAPPED lpOverlapped @@ -129,17 +132,17 @@ UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExist if (searchHandle != INVALID_HANDLE_VALUE) { ::FindClose(searchHandle); - return zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); + return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); } #elif defined FFS_LINUX struct ::stat fileInfo = {}; if (::stat(filename.c_str(), &fileInfo) == 0) //follow symbolic links - return zen::UInt64(fileInfo.st_size); + return UInt64(fileInfo.st_size); #endif const ErrorCode lastError = getLastError(); - const std::wstring errorMessage = _("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted(lastError); + const std::wstring errorMessage = replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError); if (errorCodeForNotExisting(lastError)) throw ErrorNotExisting(errorMessage); @@ -158,73 +161,186 @@ Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT } -namespace +class CheckedLockReader : public CheckedReader { -std::string getComputerId() //returns empty string on error +public: + CheckedLockReader(wxInputStream& stream, const Zstring& errorObjName) : CheckedReader(stream), errorObjName_(errorObjName) {} + virtual void throwException() const { throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(errorObjName_))); } + +private: + const Zstring errorObjName_; +}; + +class CheckedLockWriter : public CheckedWriter { - const wxString fhn = ::wxGetFullHostName(); - if (fhn.empty()) return std::string(); +public: + CheckedLockWriter(wxOutputStream& stream, const Zstring& errorObjName) : CheckedWriter(stream), errorObjName_(errorObjName) {} + virtual void throwException() const { throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(errorObjName_))); } + +private: + const Zstring errorObjName_; +}; + + #ifdef FFS_WIN - return "Windows " + utf8CvrtTo<std::string>(fhn); -#elif defined FFS_LINUX - return "Linux " + utf8CvrtTo<std::string>(fhn); -#endif +std::wstring getLoginSid() //throw FileError +{ + HANDLE hToken = 0; + if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, + TOKEN_ALL_ACCESS, //__in DWORD DesiredAccess, + &hToken)) //__out PHANDLE TokenHandle + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); + + DWORD bufferSize = 0; + ::GetTokenInformation(hToken, TokenGroups, nullptr, 0, &bufferSize); + + std::vector<char> buffer(bufferSize); + if (!::GetTokenInformation(hToken, //__in HANDLE TokenHandle, + TokenGroups, //__in TOKEN_INFORMATION_CLASS TokenInformationClass, + &buffer[0], //__out_opt LPVOID TokenInformation, + bufferSize, //__in DWORD TokenInformationLength, + &bufferSize)) //__out PDWORD ReturnLength + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); + + auto groups = reinterpret_cast<const TOKEN_GROUPS*>(&buffer[0]); + + for (DWORD i = 0; i < groups->GroupCount; ++i) + if ((groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) + { + LPTSTR sidStr = nullptr; + if (!::ConvertSidToStringSid(groups->Groups[i].Sid, //__in PSID Sid, + &sidStr)) //__out LPTSTR *StringSid + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); + ZEN_ON_SCOPE_EXIT(::LocalFree(sidStr)); + + return sidStr; + } + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted() + L"(no login found)"); //shouldn't happen } +#endif + +class FromCurrentProcess {}; //tag -struct LockInformation +struct LockInformation //throw FileError { - LockInformation() - { - lockId = zen::generateGUID(); + explicit LockInformation(FromCurrentProcess) : + lockId(zen::generateGUID()), #ifdef FFS_WIN - procDescr.processId = ::GetCurrentProcessId(); -#elif defined FFS_LINUX - procDescr.processId = ::getpid(); -#endif - procDescr.computerId = getComputerId(); + sessionId(utf8CvrtTo<std::string>(getLoginSid())), //throw FileError + processId(::GetCurrentProcessId()) //never fails + { + DWORD bufferSize = 0; + ::GetComputerNameEx(ComputerNameDnsFullyQualified, nullptr, &bufferSize); //get required buffer size + + std::vector<wchar_t> buffer(bufferSize); + if (!GetComputerNameEx(ComputerNameDnsFullyQualified, //__in COMPUTER_NAME_FORMAT NameType, + &buffer[0], //__out LPTSTR lpBuffer, + &bufferSize)) //__inout LPDWORD lpnSize + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); + computerName = "Windows." + utf8CvrtTo<std::string>(&buffer[0]); + + bufferSize = UNLEN + 1; + buffer.resize(bufferSize); + if (!::GetUserName(&buffer[0], //__out LPTSTR lpBuffer, + &bufferSize)) //__inout LPDWORD lpnSize + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); + userId = utf8CvrtTo<std::string>(&buffer[0]); } - - LockInformation(wxInputStream& stream) //read +#elif defined FFS_LINUX + processId(::getpid()) //never fails { - char formatDescr[sizeof(LOCK_FORMAT_DESCR)] = {}; - stream.Read(formatDescr, sizeof(LOCK_FORMAT_DESCR)); //file format header - const int lockFileVersion = readPOD<boost::int32_t>(stream); // - (void)lockFileVersion; + std::vector<char> buffer(10000); + if (::gethostname(&buffer[0], buffer.size()) != 0) + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); - //some format checking here? + computerName += "Linux."; //distinguish linux/windows lock files + computerName += &buffer[0]; - lockId = readString<std::string>(stream); - procDescr.processId = static_cast<ProcessId>(readPOD<std::uint64_t>(stream)); //possible loss of precision (32/64 bit process) covered by buildId - procDescr.computerId = readString<std::string>(stream); + if (::getdomainname(&buffer[0], buffer.size()) != 0) + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); + computerName += "."; + computerName += &buffer[0]; + + uid_t userIdTmp = ::getuid(); //never fails + userId.assign(reinterpret_cast<const char*>(&userIdTmp), sizeof(userIdTmp)); + + pid_t sessionIdTmp = ::getsid(0); //new after each restart! + if (sessionIdTmp == -1) + throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted()); + sessionId.assign(reinterpret_cast<const char*>(&sessionIdTmp), sizeof(sessionIdTmp)); } +#endif - void toStream(wxOutputStream& stream) const //write + explicit LockInformation(CheckedLockReader& reader) { - assert_static(sizeof(ProcessId) <= sizeof(std::uint64_t)); //ensure portability + char tmp[sizeof(LOCK_FORMAT_DESCR)] = {}; + reader.readArray(&tmp, sizeof(tmp)); //file format header + const int lockFileVersion = reader.readPOD<boost::int32_t>(); // + + if (!std::equal(std::begin(tmp), std::end(tmp), std::begin(LOCK_FORMAT_DESCR)) || + lockFileVersion != LOCK_FORMAT_VER) + reader.throwException(); + + reader.readString(lockId); + reader.readString(computerName); + reader.readString(userId); + reader.readString(sessionId); + processId = static_cast<decltype(processId)>(reader.readPOD<std::uint64_t>()); //[!] conversion + } + + void toStream(CheckedLockWriter& writer) const + { + writer.writeArray(LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); + writer.writePOD<boost::int32_t>(LOCK_FORMAT_VER); - stream.Write(LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); - writePOD<boost::int32_t>(stream, LOCK_FORMAT_VER); + assert_static(sizeof(processId) <= sizeof(std::uint64_t)); //ensure portability - writeString(stream, lockId); - writePOD<std::uint64_t>(stream, procDescr.processId); - writeString(stream, procDescr.computerId); + writer.writeString(lockId); + writer.writeString(computerName); + writer.writeString(userId); + writer.writeString(sessionId); + writer.writePOD<std::uint64_t>(processId); } + std::string lockId; //16 byte GUID - a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.) + + std::string computerName; //format: HostName.DomainName + std::string userId; //non-readable! + std::string sessionId; // #ifdef FFS_WIN - typedef DWORD ProcessId; //same size on 32 and 64 bit windows! + DWORD processId; #elif defined FFS_LINUX - typedef pid_t ProcessId; + pid_t processId; #endif +}; - std::string lockId; //16 byte UUID - struct ProcessDescription - { - ProcessId processId; - std::string computerId; - } procDescr; -}; +//wxGetFullHostName() is a performance killer for some users, so don't touch! + + +void writeLockInfo(const Zstring& lockfilename) //throw FileError +{ + FileOutputStream stream(lockfilename); //throw FileError + CheckedLockWriter writer(stream, lockfilename); + LockInformation(FromCurrentProcess()).toStream(writer); //throw FileError +} + + +LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, ErrorNotExisting +{ + FileInputStream stream(lockfilename); //throw FileError, ErrorNotExisting + CheckedLockReader reader(stream, lockfilename); + return LockInformation(reader); //throw FileError +} + + +inline +std::string retrieveLockId(const Zstring& lockfilename) //throw FileError, ErrorNotExisting +{ + return retrieveLockInfo(lockfilename).lockId; +} //true: process not available, false: cannot say anything @@ -235,20 +351,24 @@ enum ProcessStatus PROC_STATUS_ITS_US, PROC_STATUS_NO_IDEA }; -ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDescr) +ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileError { - if (procDescr.computerId != getComputerId() || - procDescr.computerId.empty()) //both names are empty - return PROC_STATUS_NO_IDEA; //lock owned by different computer + const LockInformation localInfo((FromCurrentProcess())); //throw FileError -#ifdef FFS_WIN - if (procDescr.processId == ::GetCurrentProcessId()) //may seem obscure, but it's possible: deletion failed or a lock file is "stolen" and put back while the program is running + if (lockInfo.computerName != localInfo.computerName || + lockInfo.userId != localInfo.userId) //another user may run a session right now! + return PROC_STATUS_NO_IDEA; //lock owned by different computer in this network + + if (lockInfo.sessionId != localInfo.sessionId) + return PROC_STATUS_NOT_RUNNING; //different session but same user? there can be only one + + if (lockInfo.processId == localInfo.processId) //obscure, but possible: deletion failed or a lock file is "stolen" and put back while the program is running return PROC_STATUS_ITS_US; - //note: ::OpenProcess() is no option as it may successfully return for crashed processes! - HANDLE snapshot = ::CreateToolhelp32Snapshot( - TH32CS_SNAPPROCESS, //__in DWORD dwFlags, - 0); //__in DWORD th32ProcessID +#ifdef FFS_WIN + //note: ::OpenProcess() is no alternative as it may successfully return for crashed processes! -> remark: "WaitForSingleObject" may identify this case! + HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, //__in DWORD dwFlags, + 0); //__in DWORD th32ProcessID if (snapshot == INVALID_HANDLE_VALUE) return PROC_STATUS_NO_IDEA; ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot)); @@ -258,122 +378,104 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe if (!::Process32First(snapshot, //__in HANDLE hSnapshot, &processEntry)) //__inout LPPROCESSENTRY32 lppe - return PROC_STATUS_NO_IDEA; + return PROC_STATUS_NO_IDEA; //ERROR_NO_MORE_FILES not possible do { - if (processEntry.th32ProcessID == procDescr.processId) + if (processEntry.th32ProcessID == lockInfo.processId) return PROC_STATUS_RUNNING; //process still running } while (::Process32Next(snapshot, &processEntry)); + if (::GetLastError() != ERROR_NO_MORE_FILES) //yes, they call it "files" + return PROC_STATUS_NO_IDEA; return PROC_STATUS_NOT_RUNNING; #elif defined FFS_LINUX - if (procDescr.processId == ::getpid()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running - return PROC_STATUS_ITS_US; - - if (procDescr.processId <= 0 || procDescr.processId >= 65536) + if (lockInfo.processId <= 0 || lockInfo.processId >= 65536) return PROC_STATUS_NO_IDEA; //invalid process id - return zen::dirExists(Zstr("/proc/") + zen::numberTo<Zstring>(procDescr.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; + return zen::dirExists("/proc/" + zen::numberTo<Zstring>(lockInfo.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; #endif } -void writeLockInfo(const Zstring& lockfilename) //throw FileError -{ - //write GUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.) - FileOutputStream lockFile(lockfilename); //throw FileError - LockInformation().toStream(lockFile); -} - - -LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, ErrorNotExisting -{ - //read GUID from beginning of file - FileInputStream lockFile(lockfilename); //throw FileError, ErrorNotExisting - return LockInformation(lockFile); -} - - -std::string retrieveLockId(const Zstring& lockfilename) //throw FileError, ErrorNotExisting -{ - return retrieveLockInfo(lockfilename).lockId; -} -} - - void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError { - const std::wstring infoMsg = replaceCpy(_("Waiting while directory is locked (%x)..."), L"%x", std::wstring(L"\"") + lockfilename + L"\""); + const std::wstring infoMsg = replaceCpy(_("Waiting while directory is locked (%x)..."), L"%x", fmtFileName(lockfilename)); if (callback) callback->reportInfo(infoMsg); //--------------------------------------------------------------- try { - const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw FileError, ErrorNotExisting - - bool lockOwnderDead = false; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec - switch (getProcessStatus(lockInfo.procDescr)) + //convenience optimization only: if we know the owning process crashed, we needn't wait DETECT_ABANDONED_INTERVAL sec + bool lockOwnderDead = false; + std::string originalLockId; //empty if it cannot be retrieved + try { - case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process - case PROC_STATUS_NOT_RUNNING: - lockOwnderDead = true; - break; - case PROC_STATUS_RUNNING: - case PROC_STATUS_NO_IDEA: - break; + const LockInformation& lockInfo = retrieveLockInfo(lockfilename); //throw FileError, ErrorNotExisting + originalLockId = lockInfo.lockId; + switch (getProcessStatus(lockInfo)) //throw FileError + { + case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process + case PROC_STATUS_NOT_RUNNING: + lockOwnderDead = true; + break; + case PROC_STATUS_RUNNING: + case PROC_STATUS_NO_IDEA: + break; + } } + catch (FileError&) {} //logfile may be only partly written -> this is no error! - zen::UInt64 fileSizeOld; - wxLongLong lockSilentStart = wxGetLocalTimeMillis(); + UInt64 fileSizeOld; + wxMilliClock_t lastLifeSign = wxGetLocalTimeMillis(); while (true) { - const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError, ErrorNotExisting - wxLongLong currentTime = wxGetLocalTimeMillis(); + wxMilliClock_t currentTime = wxGetLocalTimeMillis(); + const UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError, ErrorNotExisting if (fileSizeNew != fileSizeOld) //received life sign from lock { - fileSizeOld = fileSizeNew; - lockSilentStart = currentTime; + fileSizeOld = fileSizeNew; + lastLifeSign = currentTime; } if (lockOwnderDead || //no need to wait any longer... - currentTime - lockSilentStart > DETECT_EXITUS_INTERVAL) + currentTime - lastLifeSign > DETECT_ABANDONED_INTERVAL) { DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw FileError //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! - if (retrieveLockId(lockfilename) != lockInfo.lockId) //throw FileError, ErrorNotExisting - return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... + if (!originalLockId.empty()) + if (retrieveLockId(lockfilename) != originalLockId) //throw FileError, ErrorNotExisting -> since originalLockId is filled, we are not expecting errors! + return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... - if (getLockFileSize(lockfilename) != fileSizeOld) //throw FileError, ErrorNotExisting - continue; //belated lifesign + if (::getLockFileSize(lockfilename) != fileSizeOld) //throw FileError, ErrorNotExisting + continue; //late life sign removeFile(lockfilename); //throw FileError return; } //wait some time... - const size_t GUI_CALLBACK_INTERVAL = 100; + assert_static(POLL_LIFE_SIGN_INTERVAL % GUI_CALLBACK_INTERVAL == 0); for (size_t i = 0; i < POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i) { if (callback) callback->requestUiRefresh(); wxMilliSleep(GUI_CALLBACK_INTERVAL); - //show some countdown on abandoned locks if (callback) { - if (currentTime - lockSilentStart > EMIT_LIFE_SIGN_INTERVAL) //one signal missed: it's likely this is an abandoned lock: + //one signal missed: it's likely this is an abandoned lock => show countdown + if (currentTime - lastLifeSign > EMIT_LIFE_SIGN_INTERVAL) { - long remainingSeconds = ((DETECT_EXITUS_INTERVAL - (wxGetLocalTimeMillis() - lockSilentStart)) / 1000).ToLong(); + long remainingSeconds = ((DETECT_ABANDONED_INTERVAL - (wxGetLocalTimeMillis() - lastLifeSign)) / 1000).ToLong(); remainingSeconds = std::max(0L, remainingSeconds); const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo<std::wstring>(remainingSeconds)); - callback->reportInfo(infoMsg + L" " + remSecMsg); } else @@ -411,30 +513,32 @@ bool tryLock(const Zstring& lockfilename) //throw FileError nullptr); if (fileHandle == INVALID_HANDLE_VALUE) { - if (::GetLastError() == ERROR_FILE_EXISTS) + const DWORD lastError = ::GetLastError(); + 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 return false; else - throw FileError(_("Error setting directory lock:") + L"\n\"" + lockfilename + L"\"" + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set directory lock %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + getLastErrorFormatted()); } ::CloseHandle(fileHandle); #elif defined FFS_LINUX //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open - ::umask(0); //important! + ::umask(0); //important! -> why? const int fileHandle = ::open(lockfilename.c_str(), O_CREAT | O_WRONLY | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO); if (fileHandle == -1) { if (errno == EEXIST) return false; else - throw FileError(_("Error setting directory lock:") + L"\n\"" + lockfilename + L"\"" + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set directory lock %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + getLastErrorFormatted()); } ::close(fileHandle); #endif ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); - //write UUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, etc.) + //write housekeeping info: user, process info, lock GUID writeLockInfo(lockfilename); //throw FileError guardLockFile.dismiss(); //lockfile created successfully @@ -485,32 +589,32 @@ public: //create or retrieve a SharedDirLock std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError { - //optimization: check if there is an active(!) lock for "lockfilename" - FileToUuidMap::const_iterator iterUuid = fileToUuid.find(lockfilename); - if (iterUuid != fileToUuid.end()) - { - if (const std::shared_ptr<SharedDirLock>& activeLock = findActive(iterUuid->second)) //returns null-lock if not found + tidyUp(); + + //optimization: check if we already own a lock for this path + auto iterGuid = fileToGuid.find(lockfilename); + if (iterGuid != fileToGuid.end()) + if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(iterGuid->second)) //returns null-lock if not found return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership - } - try //actual check based on lock UUID, deadlock prevention: "lockfilename" may be an alternative name for an already active lock + try //check based on lock GUID, deadlock prevention: "lockfilename" may be an alternative name for a lock already owned by this process { const std::string lockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting - if (const std::shared_ptr<SharedDirLock>& activeLock = findActive(lockId)) //returns null-lock if not found + if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(lockId)) //returns null-lock if not found { - fileToUuid[lockfilename] = lockId; //perf-optimization: update relation + fileToGuid[lockfilename] = lockId; //found an alias for one of our active locks return activeLock; } } - catch (FileError&) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock file + catch (FileError&) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock files - //not yet in buffer, so create a new directory lock + //lock not owned by us => create a new one auto newLock = std::make_shared<SharedDirLock>(lockfilename, callback); //throw FileError - const std::string& newLockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting + const std::string& newLockGuid = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting //update registry - fileToUuid[lockfilename] = newLockId; //throw() - uuidToLock[newLockId] = newLock; // + fileToGuid[lockfilename] = newLockGuid; //throw() + guidToLock[newLockGuid] = newLock; // return newLock; } @@ -518,19 +622,24 @@ public: private: LockAdmin() {} - std::shared_ptr<SharedDirLock> findActive(const std::string& lockId) //returns null-lock if not found + typedef std::string UniqueId; + typedef std::map<Zstring, UniqueId, LessFilename> FileToGuidMap; //n:1 handle uppper/lower case correctly + typedef std::map<UniqueId, std::weak_ptr<SharedDirLock>> GuidToLockMap; //1:1 + + std::shared_ptr<SharedDirLock> getActiveLock(const UniqueId& lockId) //returns null if none found { - auto iterLock = uuidToLock.find(lockId); - return iterLock != uuidToLock.end() ? - iterLock->second.lock() : nullptr; //try to get shared_ptr; throw() + auto iterLock = guidToLock.find(lockId); + return iterLock != guidToLock.end() ? iterLock->second.lock() : nullptr; //try to get shared_ptr; throw() } - typedef std::string UniqueId; - typedef std::map<Zstring, UniqueId, LessFilename> FileToUuidMap; //n:1 handle uppper/lower case correctly - typedef std::map<UniqueId, std::weak_ptr<SharedDirLock>> UuidToLockMap; //1:1 + void tidyUp() //remove obsolete lock entries + { + map_remove_if(guidToLock, [ ](const GuidToLockMap::value_type& v) { return !v.second.lock(); }); + map_remove_if(fileToGuid, [&](const FileToGuidMap::value_type& v) { return guidToLock.find(v.second) == guidToLock.end(); }); + } - FileToUuidMap fileToUuid; //lockname |-> UUID; locks can be referenced by a lockfilename or alternatively a UUID - UuidToLockMap uuidToLock; //UUID |-> "shared lock ownership" + FileToGuidMap fileToGuid; //lockname |-> GUID; locks can be referenced by a lockfilename or alternatively a GUID + GuidToLockMap guidToLock; //GUID |-> "shared lock ownership" }; @@ -549,5 +658,5 @@ DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw } #endif - sharedLock = LockAdmin::instance().retrieve(lockfilename, callback); + sharedLock = LockAdmin::instance().retrieve(lockfilename, callback); //throw FileError } diff --git a/lib/dir_lock.h b/lib/dir_lock.h index 3e9f2a79..925d99d6 100644 --- a/lib/dir_lock.h +++ b/lib/dir_lock.h @@ -9,6 +9,9 @@ #include <memory> #include <zen/file_error.h> +namespace zen +{ +const size_t GUI_CALLBACK_INTERVAL = 100; struct DirLockCallback //while waiting for the lock { @@ -20,12 +23,12 @@ struct DirLockCallback //while waiting for the lock /* RAII structure to place a directory lock against other FFS processes: - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc. - - ownership shared between all object instances refering to a specific lock location(= UUID) + - ownership shared between all object instances refering to a specific lock location(= GUID) - can be copied safely and efficiently! (ref-counting) - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds) - temporary locks created during abandoned lock resolution keep "lockfilename"'s extension - race-free (Windows, almost on Linux(NFS)) - - currently NOT thread-safe! (static LockAdmin) + - NOT thread-safe! (1. static LockAdmin 2. directory name aliases must be resolved sequentially!) */ class DirLock { @@ -37,5 +40,6 @@ private: class SharedDirLock; std::shared_ptr<SharedDirLock> sharedLock; }; +} #endif // DIR_LOCK_H_INCLUDED diff --git a/lib/ffs_paths.h b/lib/ffs_paths.h index ac032e6b..9376825b 100644 --- a/lib/ffs_paths.h +++ b/lib/ffs_paths.h @@ -79,14 +79,7 @@ Zstring getResourceDir() if (isPortableVersion()) return impl::getBinaryDir(); else //use OS' standard paths - { - Zstring resourceDir = toZ(wxStandardPathsBase::Get().GetResourcesDir()); - - if (!endsWith(resourceDir, FILE_NAME_SEPARATOR)) - resourceDir += FILE_NAME_SEPARATOR; - - return resourceDir; - } + return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); #endif } @@ -113,10 +106,7 @@ Zstring getConfigDir() } catch (const FileError&) {} - if (!endsWith(userDirectory, FILE_NAME_SEPARATOR)) - userDirectory += FILE_NAME_SEPARATOR; - - return userDirectory; + return appendSeparator(userDirectory); } } diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp index e1518d30..daf32a86 100644 --- a/lib/icon_buffer.cpp +++ b/lib/icon_buffer.cpp @@ -9,7 +9,7 @@ #include <set> #include <zen/thread.h> //includes <boost/thread.hpp> #include <zen/scope_guard.h> -#include <boost/thread/once.hpp> +//#include <boost/thread/once.hpp> #ifdef FFS_WIN #include <zen/dll.h> @@ -147,7 +147,7 @@ Zstring getFileExtension(const Zstring& filename) { const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! - return shortName.find(Zchar('.')) != Zstring::npos ? + return contains(shortName, Zchar('.')) ? afterLast(filename, Zchar('.')) : Zstring(); } @@ -193,7 +193,7 @@ int getShilIconType(IconBuffer::IconSize sz) } -DllFun<thumb::GetIconByIndexFct> getIconByIndex; +DllFun<thumb::FunType_getIconByIndex> getIconByIndex; boost::once_flag initGetIconByIndexOnce = BOOST_ONCE_INIT; @@ -210,9 +210,10 @@ IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffe if (!imgList) return IconHolder(); - boost::call_once(initGetIconByIndexOnce, []() //thread-safe init + boost::call_once(initGetIconByIndexOnce, [] //thread-safe init { - getIconByIndex = DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); + using namespace thumb; + getIconByIndex = DllFun<FunType_getIconByIndex>(getDllName(), funName_getIconByIndex); }); return IconHolder(getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : nullptr); } @@ -221,11 +222,11 @@ IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffe IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz) { //no read-access to disk! determine icon by extension - return getIconByAttribute((Zstr("dummy.") + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); + return getIconByAttribute((L"dummy." + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); } -DllFun<thumb::GetThumbnailFct> getThumbnailIcon; +DllFun<thumb::FunType_getThumbnail> getThumbnailIcon; boost::once_flag initThumbnailOnce = BOOST_ONCE_INIT; #endif } @@ -235,11 +236,10 @@ boost::once_flag initThumbnailOnce = BOOST_ONCE_INIT; IconHolder getThumbnail(const Zstring& filename, int requestedSize) //return 0 on failure { #ifdef FFS_WIN - using namespace thumb; - - boost::call_once(initThumbnailOnce, []() //note: "getThumbnail" function itself is already thread-safe + boost::call_once(initThumbnailOnce, [] //note: "getThumbnail" function itself is already thread-safe { - getThumbnailIcon = DllFun<GetThumbnailFct>(getDllName(), getThumbnailFctName); + using namespace thumb; + getThumbnailIcon = DllFun<FunType_getThumbnail>(getDllName(), funName_getThumbnail); }); return IconHolder(getThumbnailIcon ? static_cast< ::HICON>(getThumbnailIcon(filename.c_str(), requestedSize)) : nullptr); @@ -253,6 +253,7 @@ IconHolder getThumbnail(const Zstring& filename, int requestedSize) //return 0 o const char* mimeFileIcons[] = { "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes + "text-x-generic", //http://live.gnome.org/GnomeArt/Tutorials/IconThemes "empty", // "gtk-file", //Ubuntu: /usr/share/icons/Humanity/mimes/48 "gnome-fs-regular", // @@ -320,7 +321,7 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) boost::call_once(initGetIconByIndexOnce, [] //thread-safe init { - getIconByIndex = DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); + getIconByIndex = DllFun<thumb::FunType_getIconByIndex>(thumb::getDllName(), thumb::funName_getIconByIndex); }); return IconHolder(getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : nullptr); @@ -382,7 +383,7 @@ public: boost::unique_lock<boost::mutex> dummy(lockFiles); while (filesToLoad.empty()) - conditionNewFiles.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(50)); //interruption point! + conditionNewFiles.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! Zstring fileName = filesToLoad.back(); filesToLoad.pop_back(); diff --git a/lib/localization.cpp b/lib/localization.cpp index 3c997074..f056aea0 100644 --- a/lib/localization.cpp +++ b/lib/localization.cpp @@ -284,11 +284,16 @@ wxLanguage mapLanguageDialect(wxLanguage language) case wxLANGUAGE_SWEDISH_FINLAND: return wxLANGUAGE_SWEDISH; + //variants of wxLANGUAGE_NORWEGIAN_BOKMAL + case wxLANGUAGE_NORWEGIAN_NYNORSK: + return wxLANGUAGE_NORWEGIAN_BOKMAL; + //case wxLANGUAGE_CZECH: //case wxLANGUAGE_DANISH: //case wxLANGUAGE_FINNISH: //case wxLANGUAGE_GREEK: //case wxLANGUAGE_JAPANESE: + //case wxLANGUAGE_LITHUANIAN: //case wxLANGUAGE_POLISH: //case wxLANGUAGE_SLOVENIAN: //case wxLANGUAGE_HUNGARIAN: @@ -384,22 +389,21 @@ void zen::setLanguage(int language) //load language file into buffer if (!languageFile.empty()) //if languageFile is empty texts will be english per default - { try { zen::setTranslator(new FFSLocale(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, PluralForm::ParsingError } catch (lngfile::ParsingError& e) { - wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + languageFile + wxT("\"") + wxT("\n\n") + - wxT("Row: ") + zen::toStringSep(e.row) + wxT("\n") + - wxT("Column: ") + zen::toStringSep(e.col) + wxT("\n"), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), + L"%x", fmtFileName(toZ(languageFile))), + L"%y", numberTo<std::wstring>(e.row)), + L"%z", numberTo<std::wstring>(e.col)), _("Error"), wxOK | wxICON_ERROR); } catch (PluralForm::ParsingError&) { - wxMessageBox(wxT("Invalid Plural Form"), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(L"Invalid Plural Form", _("Error"), wxOK | wxICON_ERROR); } - } } diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp index 6c9fd3ee..9ab99920 100644 --- a/lib/parallel_scan.cpp +++ b/lib/parallel_scan.cpp @@ -85,7 +85,7 @@ DiskInfo retrieveDiskInfo(const Zstring& pathName) HANDLE hVolume = ::CreateFile(volnameFmt.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, + nullptr, OPEN_EXISTING, 0, nullptr); @@ -237,12 +237,12 @@ public: std::wstring getCurrentStatus() //context of main thread, call repreatedly { - std::wstring filename; + Zstring filename; std::wstring statusMsg; { boost::lock_guard<boost::mutex> dummy(lockCurrentStatus); if (!currentFile.empty()) - filename = utf8CvrtTo<std::wstring>(currentFile); + filename = currentFile; else if (!currentStatus.empty()) statusMsg = copyStringTo<std::wstring>(currentStatus); } @@ -252,11 +252,9 @@ public: std::wstring statusText = copyStringTo<std::wstring>(textScanning); const long activeCount = activeWorker; if (activeCount >= 2) - { - statusText += L" " + _P("[1 Thread]", "[%x Threads]", activeCount); - replace(statusText, L"%x", numberTo<std::wstring>(activeCount)); - } - statusText += std::wstring(L" \n") + L'\"' + filename + L'\"'; + statusText += L" " + replaceCpy(_P("[1 Thread]", "[%x Threads]", activeCount), L"%x", numberTo<std::wstring>(activeCount)); + + statusText += L" " + fmtFileName(filename); return statusText; } else @@ -451,18 +449,17 @@ public: DstHackCallbackImpl(AsyncCallback& acb, int threadID) : acb_(acb), threadID_(threadID), - textApplyingDstHack(toZ(replaceCpy(_("Encoding extended time information: %x"), L"%x", L"\n\"%x\""))) {} + textApplyingDstHack(replaceCpy(_("Encoding extended time information: %x"), L"%x", L"\n%x")) {} private: virtual void requestUiRefresh(const Zstring& filename) //applying DST hack imposes significant one-time performance drawback => callback to inform user { - const Zstring statusText = replaceCpy(textApplyingDstHack, Zstr("%x"), filename); - acb_.reportCurrentStatus(utf8CvrtTo<std::wstring>(statusText), threadID_); + acb_.reportCurrentStatus(replaceCpy(textApplyingDstHack, L"%x", fmtFileName(filename)), threadID_); } AsyncCallback& acb_; int threadID_; - const Zstring textApplyingDstHack; + const std::wstring textApplyingDstHack; }; #endif //------------------------------------------------------------------------------------------ diff --git a/lib/parse_plural.h b/lib/parse_plural.h index 3c5820d0..141266b6 100644 --- a/lib/parse_plural.h +++ b/lib/parse_plural.h @@ -82,7 +82,7 @@ struct BinaryExp : public Expr<typename StlOp::result_type> template <class StlOp> inline -BinaryExp<StlOp> makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw (std::bad_cast) +BinaryExp<StlOp> makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw std::bad_cast { return BinaryExp<StlOp>(dynamic_cast<const Expr<typename StlOp::first_argument_type >&>(lhs), dynamic_cast<const Expr<typename StlOp::second_argument_type>&>(rhs), biop); diff --git a/lib/statistics.cpp b/lib/perf_check.cpp index 7b3bceae..897be12c 100644 --- a/lib/statistics.cpp +++ b/lib/perf_check.cpp @@ -4,104 +4,72 @@ // * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#include "statistics.h" +#include "perf_check.h" #include <limits> -#include <wx/ffile.h> -#include <wx/stopwatch.h> +//#include <wx/ffile.h> #include <zen/basic_math.h> -#include <zen/assert_static.h> #include <zen/i18n.h> #include <wx+/format_unit.h> - using namespace zen; -RetrieveStatistics::~RetrieveStatistics() +PerfCheck::PerfCheck(unsigned windowSizeRemainingTime, + unsigned windowSizeBytesPerSecond) : + windowSizeRemTime(windowSizeRemainingTime), + windowSizeBPS(windowSizeBytesPerSecond), + windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)) {} + + +PerfCheck::~PerfCheck() { - //write statistics to a file + /* + //write samples to a file wxFFile outputFile(wxT("statistics.dat"), wxT("w")); outputFile.Write(wxT("Time(ms);Objects;Data\n")); - std::for_each(data.begin(), data.end(), - [&](const StatEntry& entry) + for (auto iter = samples.begin(); iter != samples.end(); ++iter) { - outputFile.Write(numberTo<wxString>(entry.time)); + outputFile.Write(numberTo<wxString>(iter->first)); outputFile.Write(wxT(";")); - outputFile.Write(numberTo<wxString>(entry.objects)); + outputFile.Write(numberTo<wxString>(iter->second.objCount_)); outputFile.Write(wxT(";")); - outputFile.Write(numberTo<wxString>(entry.value)); + outputFile.Write(numberTo<wxString>(iter->second.data_)); outputFile.Write(wxT("\n")); - }); -} - - -void RetrieveStatistics::writeEntry(double value, int objects) -{ - StatEntry newEntry; - newEntry.value = value; - newEntry.objects = objects; - newEntry.time = timer.Time(); - data.push_back(newEntry); + } + */ } -//######################################################################################## -Statistics::Statistics(int totalObjectCount, - double totalDataAmount, - unsigned windowSizeRemainingTime, - unsigned windowSizeBytesPerSecond) : - objectsTotal(totalObjectCount), - dataTotal(totalDataAmount), - windowSizeRemTime(windowSizeRemainingTime), - windowSizeBPS(windowSizeBytesPerSecond), - windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)) {} - - -void Statistics::addMeasurement(int objectsCurrent, double dataCurrent) +void PerfCheck::addSample(int objectsCurrent, double dataCurrent, long timeMs) { - Record newRecord; - newRecord.objects = objectsCurrent; - newRecord.data = dataCurrent; - - const long now = timer.Time(); - - measurements.insert(measurements.end(), std::make_pair(now, newRecord)); //use fact that time is monotonously ascending + samples.insert(samples.end(), std::make_pair(timeMs, Record(objectsCurrent, dataCurrent))); //use fact that time is monotonously ascending //remove all records earlier than "now - windowMax" - const long newBegin = now - windowMax; - TimeRecordMap::iterator windowBegin = measurements.upper_bound(newBegin); - if (windowBegin != measurements.begin()) - measurements.erase(measurements.begin(), --windowBegin); //retain one point before newBegin in order to handle "measurement holes" + const long newBegin = timeMs - windowMax; + auto iterWindowBegin = samples.upper_bound(newBegin); + if (iterWindowBegin != samples.begin()) + samples.erase(samples.begin(), --iterWindowBegin); //keep one point before newBegin in order to handle "measurement holes" } -void Statistics::setNewTotal(int totalObjectCount, double totalDataAmount) +wxString PerfCheck::getRemainingTime(double dataRemaining) const { - objectsTotal = totalObjectCount; - dataTotal = totalDataAmount; -} - - -wxString Statistics::getRemainingTime() const -{ - if (!measurements.empty()) + if (!samples.empty()) { - const TimeRecordMap::value_type& backRecord = *measurements.rbegin(); + const auto& recordBack = *samples.rbegin(); //find start of records "window" - const long frontTime = backRecord.first - windowSizeRemTime; - TimeRecordMap::const_iterator windowBegin = measurements.upper_bound(frontTime); - if (windowBegin != measurements.begin()) - --windowBegin; //one point before window begin in order to handle "measurement holes" + auto iterFront = samples.upper_bound(recordBack.first - windowSizeRemTime); + if (iterFront != samples.begin()) + --iterFront; //one point before window begin in order to handle "measurement holes" - const TimeRecordMap::value_type& frontRecord = *windowBegin; + const auto& recordFront = *iterFront; //----------------------------------------------------------------------------------------------- - const double timeDelta = backRecord.first - frontRecord.first; - const double dataDelta = backRecord.second.data - frontRecord.second.data; + const double timeDelta = recordBack.first - recordFront.first; + const double dataDelta = recordBack.second.data_ - recordFront.second.data_; - const double dataRemaining = dataTotal - backRecord.second.data; //objects do *NOT* correspond to disk accesses, so we better play safe and use "bytes" only! //https://sourceforge.net/tracker/index.php?func=detail&aid=3452469&group_id=234430&atid=1093083 @@ -111,47 +79,51 @@ wxString Statistics::getRemainingTime() const return zen::remainingTimeToShortString(remTimeSec); } } - return wxT("-"); //fallback } -wxString Statistics::getBytesPerSecond() const +wxString PerfCheck::getBytesPerSecond() const { - if (!measurements.empty()) + if (!samples.empty()) { - const TimeRecordMap::value_type& backRecord = *measurements.rbegin(); + const auto& recordBack = *samples.rbegin(); //find start of records "window" - const long frontTime = backRecord.first - windowSizeBPS; - TimeRecordMap::const_iterator windowBegin = measurements.upper_bound(frontTime); - if (windowBegin != measurements.begin()) - --windowBegin; //one point before window begin in order to handle "measurement holes" + auto iterFront = samples.upper_bound(recordBack.first - windowSizeBPS); + if (iterFront != samples.begin()) + --iterFront; //one point before window begin in order to handle "measurement holes" - const TimeRecordMap::value_type& frontRecord = *windowBegin; + const auto& recordFront = *iterFront; //----------------------------------------------------------------------------------------------- - const double timeDelta = backRecord.first - frontRecord.first; - const double dataDelta = backRecord.second.data - frontRecord.second.data; + const double timeDelta = recordBack.first - recordFront.first; + const double dataDelta = recordBack.second.data_ - recordFront.second.data_; if (!numeric::isNull(timeDelta)) if (dataDelta > 0) //may be negative if user cancels copying return zen::filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec"); } - return wxT("-"); //fallback } -void Statistics::pauseTimer() +wxString PerfCheck::getOverallBytesPerSecond() const //for all samples { - timer.Pause(); -} - + if (!samples.empty()) + { + const auto& recordBack = *samples.rbegin(); + const auto& recordFront = *samples.begin(); + //----------------------------------------------------------------------------------------------- + const double timeDelta = recordBack.first - recordFront.first; + const double dataDelta = recordBack.second.data_ - recordFront.second.data_; -void Statistics::resumeTimer() -{ - timer.Resume(); + if (!numeric::isNull(timeDelta)) + if (dataDelta > 0) //may be negative if user cancels copying + return zen::filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec"); + } + return wxT("-"); //fallback } + /* class for calculation of remaining time: ---------------------------------------- diff --git a/lib/perf_check.h b/lib/perf_check.h new file mode 100644 index 00000000..b0d4db89 --- /dev/null +++ b/lib/perf_check.h @@ -0,0 +1,40 @@ +// ************************************************************************** +// * 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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef STATISTICS_H_INCLUDED +#define STATISTICS_H_INCLUDED + +#include <map> +#include <wx/string.h> + +class PerfCheck +{ +public: + PerfCheck(unsigned windowSizeRemainingTime, //unit: [ms] + unsigned windowSizeBytesPerSecond); // + ~PerfCheck(); + + void addSample(int objectsCurrent, double dataCurrent, long timeMs); //timeMs must be ascending! + + wxString getRemainingTime(double dataRemaining) const; + wxString getBytesPerSecond() const; //for window + wxString getOverallBytesPerSecond() const; //for all samples + +private: + const long windowSizeRemTime; //unit: [ms] + const long windowSizeBPS; // + const long windowMax; + + struct Record + { + Record(int objCount, double data) : objCount_(objCount), data_(data) {} + int objCount_; + double data_; //unit: [bytes] + }; + std::multimap<long, Record> samples; ////time, unit: [ms] +}; + +#endif // STATISTICS_H_INCLUDED diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp index 7fc7b066..a3ed5bfc 100644 --- a/lib/process_xml.cpp +++ b/lib/process_xml.cpp @@ -35,13 +35,13 @@ XmlType getXmlType(const zen::XmlDoc& doc) //throw() } -XmlType xmlAccess::getXmlType(const wxString& filename) //throw() +XmlType xmlAccess::getXmlType(const Zstring& filename) //throw() { XmlDoc doc; try { //do NOT use zen::loadStream as it will superfluously load even huge files! - loadXmlDocument(toZ(filename), doc); //throw FfsXmlError, quick exit if file is not an FFS XML + loadXmlDocument(filename, doc); //throw FfsXmlError, quick exit if file is not an FFS XML } catch (const FfsXmlError&) { @@ -110,7 +110,7 @@ xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchCo } -xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg, const wxString& referenceFile) +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceFile) { //try to take over batch-specific settings from reference if (!referenceFile.empty() && getXmlType(referenceFile) == XML_TYPE_BATCH) @@ -118,7 +118,7 @@ xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiCo { XmlBatchConfig output; - std::vector<wxString> filenames; + std::vector<Zstring> filenames; filenames.push_back(referenceFile); convertConfig(filenames, output); //throw xmlAccess::FfsXmlError @@ -144,7 +144,7 @@ xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiCo } -xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filenames) //throw () +xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<Zstring>& filenames) //throw() { bool guiCfgExists = false; bool batchCfgExists = false; @@ -181,7 +181,7 @@ xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filena namespace { template <class XmlCfg> -XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlError>& exeption) //throw xmlAccess::FfsXmlError +XmlCfg loadCfgImpl(const Zstring& filename, std::unique_ptr<xmlAccess::FfsXmlError>& exeption) //throw xmlAccess::FfsXmlError { XmlCfg cfg; try @@ -200,7 +200,7 @@ XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlEr template <class XmlCfg> -void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw xmlAccess::FfsXmlError +void mergeConfigFilesImpl(const std::vector<Zstring>& filenames, XmlCfg& config) //throw xmlAccess::FfsXmlError { assert(!filenames.empty()); if (filenames.empty()) @@ -208,9 +208,10 @@ void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config std::vector<zen::MainConfiguration> mainCfgs; std::unique_ptr<FfsXmlError> savedException; + Zstring invalidFile; std::for_each(filenames.begin(), filenames.end(), - [&](const wxString& filename) + [&](const Zstring& filename) { switch (getXmlType(filename)) { @@ -224,12 +225,13 @@ void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config case XML_TYPE_GLOBAL: case XML_TYPE_OTHER: + invalidFile = filename; break; } }); if (mainCfgs.empty()) - throw FfsXmlError(_("Invalid FreeFileSync config file!")); + throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(invalidFile))); try //...to init all non-"mainCfg" settings with first config file { @@ -245,15 +247,15 @@ void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config } -void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config) //throw (xmlAccess::FfsXmlError) +void xmlAccess::convertConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config) //throw FfsXmlError { - mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError) + mergeConfigFilesImpl(filenames, config); //throw FfsXmlError } -void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config) //throw (xmlAccess::FfsXmlError); +void xmlAccess::convertConfig(const std::vector<Zstring>& filenames, XmlBatchConfig& config) //throw FfsXmlError { - mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError) + mergeConfigFilesImpl(filenames, config); //throw FfsXmlError } @@ -838,7 +840,8 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inShared["CopyLockedFiles" ](config.copyLockedFiles); inShared["CopyFilePermissions" ](config.copyFilePermissions); inShared["TransactionalFileCopy"](config.transactionalFileCopy); - inShared["VerifyCopiedFiles" ](config.verifyFileCopy); + inShared["LockDirectoriesDuringSync"](config.createLockFile); + inShared["VerifyCopiedFiles" ](config.verifyFileCopy); inShared["RunWithBackgroundPriority"](config.runWithBackgroundPriority); //max. allowed file time deviation @@ -866,28 +869,30 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inWnd.attribute("PosY", config.gui.dlgPos.y); inWnd.attribute("Maximized", config.gui.isMaximized); - inWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); - - inWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); - inWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); - inWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); + XmlIn inManualDel = inWnd["ManualDeletion"]; + inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + inManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); - inWnd["ShowIcons"](config.gui.showIcons); - inWnd["IconSize"](config.gui.iconSize); + inWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase); + inWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); //########################################################### //read column attributes - XmlIn inColNavi = inWnd["CompressedView"]; + XmlIn inColNavi = inWnd["OverviewColumns"]; inColNavi(config.gui.columnAttribNavi); inColNavi.attribute("ShowPercentage", config.gui.showPercentBar); inColNavi.attribute("SortByColumn", config.gui.naviLastSortColumn); inColNavi.attribute("SortAscending", config.gui.naviLastSortAscending); - XmlIn inColLeft = inWnd["ColumnsLeft"]; + XmlIn inMainGrid = inWnd["MainGrid"]; + inMainGrid.attribute("ShowIcons", config.gui.showIcons); + inMainGrid.attribute("IconSize", config.gui.iconSize); + + XmlIn inColLeft = inMainGrid["ColumnsLeft"]; inColLeft(config.gui.columnAttribLeft); - XmlIn inColRight = inWnd["ColumnsRight"]; + XmlIn inColRight = inMainGrid["ColumnsRight"]; inColRight(config.gui.columnAttribRight); //########################################################### @@ -922,27 +927,27 @@ void readConfig(const Zstring& filename, XmlType type, ConfigType& config) loadXmlDocument(filename, doc); //throw FfsXmlError if (getXmlType(doc) != type) //throw() - throw FfsXmlError(_("Error parsing configuration file:") + L"\n\"" + filename + L"\""); + throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); XmlIn in(doc); ::readConfig(in, config); if (in.errorsOccured()) - throw FfsXmlError(_("Configuration loaded partially only:") + L"\n\"" + filename + L"\"\n\n" + + throw FfsXmlError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + getErrorMessageFormatted(in), FfsXmlError::WARNING); } } -void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) +void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlGuiConfig& config) { - ::readConfig(toZ(filename), XML_TYPE_GUI, config); + ::readConfig(filename, XML_TYPE_GUI, config); } -void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) +void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlBatchConfig& config) { - ::readConfig(toZ(filename), XML_TYPE_BATCH, config); + ::readConfig(filename, XML_TYPE_BATCH, config); } @@ -1104,7 +1109,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outShared["CopyLockedFiles" ](config.copyLockedFiles); outShared["CopyFilePermissions" ](config.copyFilePermissions); outShared["TransactionalFileCopy"](config.transactionalFileCopy); - outShared["VerifyCopiedFiles" ](config.verifyFileCopy); + outShared["LockDirectoriesDuringSync"](config.createLockFile); + outShared["VerifyCopiedFiles" ](config.verifyFileCopy); outShared["RunWithBackgroundPriority"](config.runWithBackgroundPriority); //max. allowed file time deviation @@ -1132,28 +1138,30 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outWnd.attribute("PosY", config.gui.dlgPos.y); outWnd.attribute("Maximized", config.gui.isMaximized); - outWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); - - outWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); - outWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); - outWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); + XmlOut outManualDel = outWnd["ManualDeletion"]; + outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + outManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); - outWnd["ShowIcons"](config.gui.showIcons); - outWnd["IconSize"](config.gui.iconSize); + outWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase); + outWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); //########################################################### //write column attributes - XmlOut outColNavi = outWnd["CompressedView"]; + XmlOut outColNavi = outWnd["OverviewColumns"]; outColNavi(config.gui.columnAttribNavi); outColNavi.attribute("ShowPercentage", config.gui.showPercentBar); outColNavi.attribute("SortByColumn", config.gui.naviLastSortColumn); outColNavi.attribute("SortAscending", config.gui.naviLastSortAscending); - XmlOut outColLeft = outWnd["ColumnsLeft"]; + XmlOut outMainGrid = outWnd["MainGrid"]; + outMainGrid.attribute("ShowIcons", config.gui.showIcons); + outMainGrid.attribute("IconSize", config.gui.iconSize); + + XmlOut outColLeft = outMainGrid["ColumnsLeft"]; outColLeft(config.gui.columnAttribLeft); - XmlOut outColRight = outWnd["ColumnsRight"]; + XmlOut outColRight = outMainGrid["ColumnsRight"]; outColRight(config.gui.columnAttribRight); //########################################################### @@ -1182,7 +1190,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) template <class ConfigType> -void writeConfig(const ConfigType& config, XmlType type, const wxString& filename) +void writeConfig(const ConfigType& config, XmlType type, const Zstring& filename) { XmlDoc doc("FreeFileSync"); setXmlType(doc, type); //throw() @@ -1190,17 +1198,17 @@ void writeConfig(const ConfigType& config, XmlType type, const wxString& filenam XmlOut out(doc); writeConfig(config, out); - saveXmlDocument(doc, toZ(filename)); //throw FfsXmlError + saveXmlDocument(doc, filename); //throw FfsXmlError } } -void xmlAccess::writeConfig(const XmlGuiConfig& config, const wxString& filename) +void xmlAccess::writeConfig(const XmlGuiConfig& config, const Zstring& filename) { ::writeConfig(config, XML_TYPE_GUI, filename); //throw FfsXmlError } -void xmlAccess::writeConfig(const XmlBatchConfig& config, const wxString& filename) +void xmlAccess::writeConfig(const XmlBatchConfig& config, const Zstring& filename) { ::writeConfig(config, XML_TYPE_BATCH, filename); //throw FfsXmlError } @@ -1208,13 +1216,13 @@ void xmlAccess::writeConfig(const XmlBatchConfig& config, const wxString& filena void xmlAccess::writeConfig(const XmlGlobalSettings& config) { - ::writeConfig(config, XML_TYPE_GLOBAL, getGlobalConfigFile()); //throw FfsXmlError + ::writeConfig(config, XML_TYPE_GLOBAL, toZ(getGlobalConfigFile())); //throw FfsXmlError } wxString xmlAccess::extractJobName(const wxString& configFilename) { - const wxString shortName = configFilename.AfterLast(FILE_NAME_SEPARATOR); //returns the whole string if seperator not found - const wxString jobName = shortName.BeforeLast(wxChar('.')); //returns empty string if seperator not found + const wxString shortName = afterLast(configFilename, utf8CvrtTo<wxString>(FILE_NAME_SEPARATOR)); //returns the whole string if separator not found + const wxString jobName = beforeLast(shortName, L'.'); //returns empty string if seperator not found return jobName.IsEmpty() ? shortName : jobName; } diff --git a/lib/process_xml.h b/lib/process_xml.h index 801deede..767e4a40 100644 --- a/lib/process_xml.h +++ b/lib/process_xml.h @@ -23,7 +23,7 @@ enum XmlType XML_TYPE_OTHER }; -XmlType getXmlType(const wxString& filename); //throw() +XmlType getXmlType(const Zstring& filename); //throw() enum OnError @@ -124,7 +124,8 @@ struct XmlGlobalSettings runWithBackgroundPriority(false), fileTimeTolerance(2), //default 2s: FAT vs NTFS verifyFileCopy(false), - transactionalFileCopy(true) {} + transactionalFileCopy(true), + createLockFile(true) {} int programLanguage; bool copyLockedFiles; //VSS usage @@ -135,6 +136,7 @@ struct XmlGlobalSettings size_t fileTimeTolerance; //max. allowed file time deviation bool verifyFileCopy; //verify copied files bool transactionalFileCopy; + bool createLockFile; OptionalDialogs optDialogs; @@ -222,17 +224,17 @@ struct XmlGlobalSettings //struct Batch }; -void readConfig(const wxString& filename, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError -void readConfig(const wxString& filename, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError -void readConfig( XmlGlobalSettings& config); //throw xmlAccess::FfsXmlError +void readConfig(const Zstring& filename, XmlGuiConfig& config); //throw FfsXmlError +void readConfig(const Zstring& filename, XmlBatchConfig& config); //throw FfsXmlError +void readConfig( XmlGlobalSettings& config); //throw FfsXmlError -void writeConfig(const XmlGuiConfig& config, const wxString& filename); //throw xmlAccess::FfsXmlError -void writeConfig(const XmlBatchConfig& config, const wxString& filename); //throw xmlAccess::FfsXmlError -void writeConfig(const XmlGlobalSettings& config); //throw xmlAccess::FfsXmlError +void writeConfig(const XmlGuiConfig& config, const Zstring& filename); //throw FfsXmlError +void writeConfig(const XmlBatchConfig& config, const Zstring& filename); //throw FfsXmlError +void writeConfig(const XmlGlobalSettings& config); //throw FfsXmlError //config conversion utilities XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); -XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg, const wxString& referenceFile); +XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg, const Zstring& referenceFile); //convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: @@ -243,10 +245,10 @@ enum MergeType MERGE_GUI_BATCH, //gui and batch files MERGE_OTHER }; -MergeType getMergeType(const std::vector<wxString>& filenames); //throw () +MergeType getMergeType(const std::vector<Zstring>& filenames); //throw () -void convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError -void convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError +void convertConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError +void convertConfig(const std::vector<Zstring>& filenames, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError wxString extractJobName(const wxString& configFilename); } diff --git a/lib/recycler.cpp b/lib/recycler.cpp deleted file mode 100644 index 1c743a0c..00000000 --- a/lib/recycler.cpp +++ /dev/null @@ -1,210 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "recycler.h" -#include <stdexcept> -#include <iterator> -#include <zen/file_handling.h> - -#ifdef FFS_WIN -#include <algorithm> -#include <functional> -#include <vector> -#include <boost/thread/once.hpp> -#include <zen/dll.h> -#include <zen/win.h> //includes "windows.h" -#include <zen/assert_static.h> -#include <zen/win_ver.h> -#include <zen/long_path_prefix.h> -#include "IFileOperation/file_op.h" - -#elif defined FFS_LINUX -#include <zen/scope_guard.h> -#include <sys/stat.h> -#include <gio/gio.h> -#endif - -using namespace zen; - - -namespace -{ -#ifdef FFS_WIN -/* -Performance test: delete 1000 files ------------------------------------- -SHFileOperation - single file 33s -SHFileOperation - multiple files 2,1s -IFileOperation - single file 33s -IFileOperation - multiple files 2,1s - -=> SHFileOperation and IFileOperation have nearly IDENTICAL performance characteristics! - -Nevertheless, let's use IFileOperation for better error reporting! -*/ - -void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw FileError -{ - if (filesToDelete.empty()) - return; - - static bool useIFileOperation = false; - static boost::once_flag once = BOOST_ONCE_INIT; //caveat: function scope static initialization is not thread-safe in VS 2010! - boost::call_once(once, [] { useIFileOperation = vistaOrLater(); }); - - if (useIFileOperation) //new recycle bin usage: available since Vista - { - std::vector<const wchar_t*> fileNames; - std::transform(filesToDelete.begin(), filesToDelete.end(), - std::back_inserter(fileNames), std::mem_fun_ref(&Zstring::c_str)); - - using namespace fileop; - const DllFun<MoveToRecycleBinFct> moveToRecycler(getDllName(), moveToRecycleBinFctName); - const DllFun<GetLastErrorFct> getLastError (getDllName(), getLastErrorFctName); - - if (!moveToRecycler || !getLastError) - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", std::wstring(L"\"") + fileNames[0] + L"\"") + L"\n\n" + //report first file only... better than nothing - replaceCpy(_("Cannot load file %x."), L"%x", std::wstring(L"\"") + getDllName() + L"\"")); - - //#warning moving long file paths to recycler does not work! clarify! - // std::vector<Zstring> temp; - // std::transform(filesToDelete.begin(), filesToDelete.end(), - // std::back_inserter(temp), std::ptr_fun(zen::removeLongPathPrefix)); //::IFileOperation() can't handle \\?\-prefix! - - if (!moveToRecycler(&fileNames[0], //array must not be empty - fileNames.size())) - { - std::vector<wchar_t> msgBuffer(2000); - getLastError(&msgBuffer[0], msgBuffer.size()); - - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", std::wstring(L"\"") + fileNames[0] + L"\"") + L"\n\n" + //report first file only... better than nothing - &msgBuffer[0]); - } - } - else //regular recycle bin usage: available since XP - { - Zstring filenameDoubleNull; - for (std::vector<Zstring>::const_iterator i = filesToDelete.begin(); i != filesToDelete.end(); ++i) - { - //#warning moving long file paths to recycler does not work! clarify! - //filenameDoubleNull += removeLongPathPrefix(*i); //::SHFileOperation() can't handle \\?\-prefix! - //You should use fully-qualified path names with this function. Using it with relative path names is not thread safe. - filenameDoubleNull += *i; //::SHFileOperation() can't handle \\?\-prefix! - filenameDoubleNull += L'\0'; - } - - SHFILEOPSTRUCT fileOp = {}; - fileOp.hwnd = nullptr; - fileOp.wFunc = FO_DELETE; - fileOp.pFrom = filenameDoubleNull.c_str(); - fileOp.pTo = nullptr; - fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI; - fileOp.fAnyOperationsAborted = false; - fileOp.hNameMappings = nullptr; - fileOp.lpszProgressTitle = nullptr; - - if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) - { - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", std::wstring(L"\"") + filenameDoubleNull.c_str() + L"\"")); //report first file only... better than nothing - } - } -} -#endif -} - - -bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError -{ - if (!somethingExists(filename)) - return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! - -#ifdef FFS_WIN - //::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_NORMAL); - //both SHFileOperation and IFileOperation are not able to delete a folder named "System Volume Information" with normal attributes but shamelessly report success - - std::vector<Zstring> fileNames; - fileNames.push_back(filename); - ::moveToWindowsRecycler(fileNames); //throw FileError - -#elif defined FFS_LINUX - GFile* file = g_file_new_for_path(filename.c_str()); //never fails according to docu - ZEN_ON_SCOPE_EXIT(g_object_unref(file);) - - GError* error = nullptr; - ZEN_ON_SCOPE_EXIT(if (error) g_error_free(error);); - - if (!g_file_trash(file, nullptr, &error)) - { - if (!error) - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", L"\"" + filename + L"\"") + L"\n\n" + - L"Unknown error."); - - //implement same behavior as in Windows: if recycler is not existing, delete permanently - if (error->code == G_IO_ERROR_NOT_SUPPORTED) - { - struct stat fileInfo = {}; - if (::lstat(filename.c_str(), &fileInfo) != 0) - return false; - - if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)) - removeFile(filename); //throw FileError - else if (S_ISDIR(fileInfo.st_mode)) - removeDirectory(filename); //throw FileError - return true; - } - - //assemble error message - const std::wstring errorMessage = L"Glib Error Code " + numberTo<std::wstring>(error->code) + /* L", " + - g_quark_to_string(error->domain) + */ L": " + utf8CvrtTo<std::wstring>(error->message); - - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", L"\"" + filename + L"\"") + L"\n\n" + - errorMessage); - } -#endif - return true; -} - - -#ifdef FFS_WIN -zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName) -{ - const DWORD bufferSize = MAX_PATH + 1; - std::vector<wchar_t> buffer(bufferSize); - if (::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - { - Zstring rootPath = &buffer[0]; - if (!endsWith(rootPath, FILE_NAME_SEPARATOR)) //a trailing backslash is required - rootPath += FILE_NAME_SEPARATOR; - - SHQUERYRBINFO recInfo = {}; - recInfo.cbSize = sizeof(recInfo); - HRESULT rv = ::SHQueryRecycleBin(rootPath.c_str(), //__in_opt LPCTSTR pszRootPath, - &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo - return rv == S_OK ? STATUS_REC_EXISTS : STATUS_REC_MISSING; - } - return STATUS_REC_UNKNOWN; -} -#elif defined FFS_LINUX -/* -We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not! - -The following function looks perfect, alas it is restricted to local files and to the implementation of GIO only: - - gboolean _g_local_file_has_trash_dir(const char* dirname, dev_t dir_dev); - See: http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.h - - Just checking for "G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH" is not correct, since we find in - http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.c - - g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, - writable && parent_info->has_trash_dir); - - => We're NOT interested in whether the specified folder can be trashed, but whether it supports thrashing its child elements! (Only support, not actual write access!) - This renders G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH useless for this purpose. -*/ -#endif diff --git a/lib/recycler.h b/lib/recycler.h deleted file mode 100644 index 8eab5b21..00000000 --- a/lib/recycler.h +++ /dev/null @@ -1,48 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef RECYCLER_H_INCLUDED -#define RECYCLER_H_INCLUDED - -#include <zen/file_error.h> -#include <zen/zstring.h> - -namespace zen -{ -/* --------------------- -|Recycle Bin Access| --------------------- - -Windows -------- -Recycler API always available: during runtime either SHFileOperation or IFileOperation (since Vista) will be dynamically selected - -Linux ------ -Compiler flags: `pkg-config --cflags gio-2.0` -Linker flags: `pkg-config --libs gio-2.0` - -Already included in package "gtk+-2.0"! -*/ - -//move a file or folder to Recycle Bin (deletes permanently if recycle is not available) -bool moveToRecycleBin(const Zstring& filename); //return "true" if file/dir was actually deleted; throw (FileError) - - -#ifdef FFS_WIN -enum StatusRecycler -{ - STATUS_REC_EXISTS, - STATUS_REC_MISSING, - STATUS_REC_UNKNOWN -}; - -StatusRecycler recycleBinStatus(const Zstring& pathName); //test existence of Recycle Bin API for certain path -#endif -} - -#endif // RECYCLER_H_INCLUDED diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp index 1b241956..f5a342d2 100644 --- a/lib/resolve_path.cpp +++ b/lib/resolve_path.cpp @@ -37,8 +37,7 @@ Zstring resolveRelativePath(Zstring relativeName) //note: ::GetFullPathName() is &buffer[0], //__out LPTSTR lpBuffer, nullptr); //__out LPTSTR *lpFilePart if (charsWritten == 0 || charsWritten >= bufferSize) //theoretically, charsWritten can never be == bufferSize - //ERROR! Don't do anything - return relativeName; + return relativeName; //ERROR! Don't do anything return Zstring(&buffer[0], charsWritten); } @@ -53,13 +52,7 @@ Zstring resolveRelativePath(const Zstring& relativeName) { std::vector<char> buffer(10000); if (::getcwd(&buffer[0], buffer.size()) != nullptr) - { - Zstring workingDir = &buffer[0]; - if (!endsWith(workingDir, FILE_NAME_SEPARATOR)) - workingDir += FILE_NAME_SEPARATOR; - - return workingDir + relativeName; - } + return appendSeparator(&buffer[0]) + relativeName; } return relativeName; @@ -177,13 +170,13 @@ bool replaceMacro(wxString& macro) //macro without %-characters, return true if return false; //there are equally named environment variables %TIME%, %DATE% existing, so replace these first! - if (macro.CmpNoCase(wxT("time")) == 0) + if (macro.CmpNoCase(L"time") == 0) { macro = formatTime<wxString>(L"%H%M%S"); return true; } - if (macro.CmpNoCase(wxT("date")) == 0) + if (macro.CmpNoCase(L"date") == 0) { macro = formatTime<wxString>(FORMAT_ISO_DATE); return true; @@ -234,32 +227,27 @@ bool replaceMacro(wxString& macro) //macro without %-characters, return true if } -void expandMacros(wxString& text) +//returns expanded or original string +wxString expandMacros(const wxString& text) { - const wxChar SEPARATOR = '%'; + const wxChar SEPARATOR = L'%'; - if (text.Find(SEPARATOR) != wxNOT_FOUND) + if (contains(text, SEPARATOR)) { - wxString prefix = text.BeforeFirst(SEPARATOR); - wxString postfix = text.AfterFirst(SEPARATOR); - if (postfix.Find(SEPARATOR) != wxNOT_FOUND) + wxString prefix = text.BeforeFirst(SEPARATOR); + wxString rest = text.AfterFirst(SEPARATOR); + if (contains(rest, SEPARATOR)) { - wxString potentialMacro = postfix.BeforeFirst(SEPARATOR); - wxString rest = postfix.AfterFirst(SEPARATOR); //text == prefix + SEPARATOR + potentialMacro + SEPARATOR + rest + wxString potentialMacro = beforeFirst(rest, SEPARATOR); + wxString postfix = afterFirst (rest, SEPARATOR); //text == prefix + SEPARATOR + potentialMacro + SEPARATOR + postfix if (replaceMacro(potentialMacro)) - { - expandMacros(rest); - text = prefix + potentialMacro + rest; - } + return prefix + potentialMacro + expandMacros(postfix); else - { - rest = SEPARATOR + rest; - expandMacros(rest); - text = prefix + SEPARATOR + potentialMacro + rest; - } + return prefix + SEPARATOR + potentialMacro + expandMacros(SEPARATOR + postfix); } } + return text; } @@ -286,59 +274,52 @@ private: #endif +//networks and cdrom excluded - this should not block Zstring volumenNameToPath(const Zstring& volumeName) //return empty string on error { #ifdef FFS_WIN - std::vector<wchar_t> volGuid(10000); + //FindFirstVolume(): traverses volumes on local hard disks only! + //GetLogicalDriveStrings(): traverses all *logical* volumes, including CD-ROM, FreeOTFE virtual volumes + + const DWORD bufferSize = ::GetLogicalDriveStrings(0, nullptr); + std::vector<wchar_t> buffer(bufferSize); - HANDLE hVol = ::FindFirstVolume(&volGuid[0], static_cast<DWORD>(volGuid.size())); - if (hVol != INVALID_HANDLE_VALUE) + const DWORD rv = ::GetLogicalDriveStrings(bufferSize, //__in DWORD nBufferLength, + &buffer[0]); //__out LPTSTR lpBuffer + if (0 < rv && rv < bufferSize) { - ZEN_ON_SCOPE_EXIT(::FindVolumeClose(hVol)); + //search for matching path in parallel until first hit + RunUntilFirstHit<Zstring> findFirstMatch; - do + for (const wchar_t* iter = &buffer[0]; *iter != 0; iter += strLength(iter) + 1) //list terminated by empty c-string { - std::vector<wchar_t> volName(MAX_PATH + 1); - - if (::GetVolumeInformation(&volGuid[0], //__in_opt LPCTSTR lpRootPathName, - &volName[0], //__out LPTSTR lpVolumeNameBuffer, - static_cast<DWORD>(volName.size()), //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize + const Zstring path = iter; + + findFirstMatch.addJob([path, volumeName]() -> std::unique_ptr<Zstring> { - if (EqualFilename()(volumeName, Zstring(&volName[0]))) - { - //GetVolumePathNamesForVolumeName is not available for Windows 2000! - typedef BOOL (WINAPI* GetVolumePathNamesForVolumeNameWFunc)(LPCWSTR lpszVolumeName, - LPWCH lpszVolumePathNames, - DWORD cchBufferLength, - PDWORD lpcchReturnLength); - - const SysDllFun<GetVolumePathNamesForVolumeNameWFunc> getVolumePathNamesForVolumeName(L"kernel32.dll", "GetVolumePathNamesForVolumeNameW"); - if (getVolumePathNamesForVolumeName) - { - std::vector<wchar_t> buffer(10000); - DWORD returnedLen = 0; - if (getVolumePathNamesForVolumeName(&volGuid[0], //__in LPCTSTR lpszVolumeName, - &buffer[0], //__out LPTSTR lpszVolumePathNames, - static_cast<DWORD>(buffer.size()), //__in DWORD cchBufferLength, - &returnedLen)) //__out PDWORD lpcchReturnLength - { - //Attention: in contrast to documentation, this function may write a *single* 0 into - //buffer if volGuid does not have any associated volume paths (e.g. a hidden volume) - const Zstring volPath(&buffer[0]); - if (!volPath.empty()) - return volPath; //return first path name in double-null terminated list! - } - } - return &volGuid[0]; //GUID looks ugly, but should be working correctly - } - } + UINT type = ::GetDriveType(path.c_str()); //non-blocking call! + if (type == DRIVE_REMOTE || + type == DRIVE_CDROM) + return nullptr; + + //next call seriously blocks for non-existing network drives! + std::vector<wchar_t> volName(MAX_PATH + 1); //docu says so + + if (::GetVolumeInformation(path.c_str(), //__in_opt LPCTSTR lpRootPathName, + &volName[0], //__out LPTSTR lpVolumeNameBuffer, + static_cast<DWORD>(volName.size()), //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize + if (EqualFilename()(volumeName, Zstring(&volName[0]))) + return zen::make_unique<Zstring>(path); + return nullptr; + }); } - while (::FindNextVolume(hVol, &volGuid[0], static_cast<DWORD>(volGuid.size()))); + if (auto result = findFirstMatch.get()) //blocks until ready + return *result; } #elif defined FFS_LINUX @@ -358,62 +339,58 @@ Zstring volumenNameToPath(const Zstring& volumeName) //return empty string on er #ifdef FFS_WIN -//attention: this call may seriously block if network volume is not available!!! +//networks and cdrom excluded - this should not block Zstring volumePathToName(const Zstring& volumePath) //return empty string on error { - const DWORD bufferSize = MAX_PATH + 1; - std::vector<wchar_t> volName(bufferSize); - - if (::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, - &volName[0], //__out LPTSTR lpVolumeNameBuffer, - bufferSize, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize + UINT rv = ::GetDriveType(volumePath.c_str()); //non-blocking call! + if (rv != DRIVE_REMOTE && + rv != DRIVE_CDROM) { - return &volName[0]; + std::vector<wchar_t> buffer(MAX_PATH + 1); + + if (::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, + &buffer[0], //__out LPTSTR lpVolumeNameBuffer, + static_cast<DWORD>(buffer.size()), //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize + return &buffer[0]; } return Zstring(); } #endif -void expandVolumeName(Zstring& text) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder +//expand volume name if possible, return original input otherwise +Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder { //this would be a nice job for a C++11 regex... - //we only expect the [.*] pattern at the beginning => do not touch dir names like "C:\somedir\[stuff]" - trim(text, true, false); + Zstring textTmp = text; + trim(textTmp, true, false); - if (startsWith(text, Zstr("["))) + if (startsWith(textTmp, Zstr("["))) { - size_t posEnd = text.find(Zstr("]")); + size_t posEnd = textTmp.find(Zstr("]")); if (posEnd != Zstring::npos) { - Zstring volname = Zstring(text.c_str() + 1, posEnd - 1); - Zstring after = Zstring(text.c_str() + posEnd + 1); + Zstring volname = Zstring(textTmp.c_str() + 1, posEnd - 1); + Zstring rest = Zstring(textTmp.c_str() + posEnd + 1); - if (startsWith(after, Zstr(':'))) - after = afterFirst(after, Zstr(':')); - if (startsWith(after, FILE_NAME_SEPARATOR)) - after = afterFirst(after, FILE_NAME_SEPARATOR); + if (startsWith(rest, Zstr(':'))) + rest = afterFirst(rest, Zstr(':')); + if (startsWith(rest, FILE_NAME_SEPARATOR)) + rest = afterFirst(rest, FILE_NAME_SEPARATOR); //[.*] pattern was found... if (!volname.empty()) { - Zstring volPath = volumenNameToPath(volname); //return empty string on error + Zstring volPath = volumenNameToPath(volname); //should not block?! if (!volPath.empty()) - { - if (!endsWith(volPath, FILE_NAME_SEPARATOR)) - volPath += FILE_NAME_SEPARATOR; - - text = volPath + after; - //successfully replaced pattern - return; - } + return appendSeparator(volPath) + rest; //successfully replaced pattern } //error: did not find corresponding volume name: @@ -425,13 +402,13 @@ void expandVolumeName(Zstring& text) // [volname]:\folder [volname]\folde C:\Program Files\FreeFileSync\[FFS USB]\FreeFileSync\ */ #ifdef FFS_WIN - text = L"?:\\[" + volname + L"]\\" + after; + return L"?:\\[" + volname + L"]\\" + rest; #elif defined FFS_LINUX - text = "/.../[" + volname + "]/" + after; + return "/.../[" + volname + "]/" + rest; #endif - return; } } + return text; } } @@ -445,20 +422,14 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less dirname[1] == L':' && dirname[2] == L'\\') { - //attention: "volumePathToName()" will seriously block if network volume is not available!!! - boost::unique_future<Zstring> futVolName = zen::async([=] { return volumePathToName(Zstring(dirname.c_str(), 3)); }); - if (futVolName.timed_wait(boost::posix_time::seconds(1))) - { - Zstring volname = futVolName.get(); - if (!volname.empty()) - output.insert(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2)); - } + Zstring volname = volumePathToName(Zstring(dirname.c_str(), 3)); //should not block + if (!volname.empty()) + output.insert(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2)); } //2. replace volume name by volume path: [SYSTEM]\dirname -> c:\dirname { - Zstring testVolname = dirname; - expandVolumeName(testVolname); + Zstring testVolname = expandVolumeName(dirname); //should not block if (testVolname != dirname) if (output.insert(testVolname).second) getDirectoryAliasesRecursive(testVolname, output); //recurse! @@ -504,11 +475,10 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less //4. replace (all) macros: %USERPROFILE% -> C:\Users\username { - wxString testMacros = toWx(dirname); - expandMacros(testMacros); - if (toZ(testMacros) != dirname) - if (output.insert(toZ(testMacros)).second) - getDirectoryAliasesRecursive(toZ(testMacros), output); //recurse! + Zstring testMacros = toZ(expandMacros(toWx(dirname))); + if (testMacros != dirname) + if (output.insert(testMacros).second) + getDirectoryAliasesRecursive(testMacros, output); //recurse! } } @@ -535,19 +505,16 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw() { //Formatting is needed since functions expect the directory to end with '\' to be able to split the relative names. - wxString dirnameTmp = toWx(dirString); - expandMacros(dirnameTmp); - - Zstring output = toZ(dirnameTmp); + Zstring dirname = toZ(expandMacros(toWx(dirString))); - expandVolumeName(output); + dirname = expandVolumeName(dirname); //should not block //remove leading/trailing whitespace - trim(output, true, false); - while (endsWith(output, " ")) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name - output.resize(output.size() - 1); + trim(dirname, true, false); + while (endsWith(dirname, " ")) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name + dirname.resize(dirname.size() - 1); - if (output.empty()) //an empty string would later be resolved as "\"; this is not desired + if (dirname.empty()) //an empty string would later be resolved as "\"; this is not desired return Zstring(); /* @@ -560,12 +527,9 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw() WINDOWS/LINUX: - detection of dependent directories, e.g. "\" and "C:\test" */ - output = resolveRelativePath(output); - - if (!endsWith(output, FILE_NAME_SEPARATOR)) - output += FILE_NAME_SEPARATOR; + dirname = resolveRelativePath(dirname); - return output; + return appendSeparator(dirname); } @@ -585,7 +549,8 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio //if (::GetFileAttributes((driveLetter + L'\\').c_str()) == INVALID_FILE_ATTRIBUTES) <- this will seriously block if network is not reachable!!! - const Zstring dirname = removeLongPathPrefix(dirnameOrig); + Zstring dirname = removeLongPathPrefix(dirnameOrig); + trim(dirname, true, false); //1. local path if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':') diff --git a/lib/resources.cpp b/lib/resources.cpp index f5def2c6..3ae24d1d 100644 --- a/lib/resources.cpp +++ b/lib/resources.cpp @@ -64,21 +64,21 @@ GlobalResources::GlobalResources() const wxString name = entry->GetName(); //generic image loading - if (name.EndsWith(wxT(".png"))) + if (name.EndsWith(L".png")) bitmaps.insert(std::make_pair(name, wxImage(resourceFile, wxBITMAP_TYPE_PNG))); - //else if (name == wxT("money.gif")) + //else if (name == L"money.gif") // loadAnimFromZip(resourceFile, animationMoney); - else if (name == wxT("working.gif")) + else if (name == L"working.gif") loadAnimFromZip(resourceFile, animationSync); } } #ifdef FFS_WIN //for compatibility it seems we need to stick with a "real" icon - programIcon = wxIcon(wxT("A_PROGRAM_ICON")); + programIcon = wxIcon(L"A_PROGRAM_ICON"); #else //use big logo bitmap for better quality - programIcon.CopyFromBitmap(getImageInt(wxT("FreeFileSync.png"))); + programIcon.CopyFromBitmap(getImageInt(L"FreeFileSync.png")); //attention: this is the reason we need a member getImage -> it must not implicitly create static object instance!!! //erroneously calling static object constructor twice will deadlock on Linux!! #endif @@ -87,8 +87,8 @@ GlobalResources::GlobalResources() const wxBitmap& GlobalResources::getImageInt(const wxString& imageName) const { - auto iter = bitmaps.find(imageName.Find(L'.') == wxNOT_FOUND ? //assume .png ending if nothing else specified - imageName + wxT(".png") : + auto iter = bitmaps.find(!contains(imageName, L'.') ? //assume .png ending if nothing else specified + imageName + L".png" : imageName); if (iter != bitmaps.end()) return iter->second; diff --git a/lib/shadow.cpp b/lib/shadow.cpp index 0a34ae5a..f4d7f3af 100644 --- a/lib/shadow.cpp +++ b/lib/shadow.cpp @@ -40,53 +40,49 @@ bool runningWOW64() //test if process is running under WOW64 (reference http://m class ShadowCopy::ShadowVolume { public: - ShadowVolume(const Zstring& volumeNameFormatted) : //throw(FileError) - createShadowCopy (getDllName(), createShadowCopyFctName), - releaseShadowCopy(getDllName(), releaseShadowCopyFctName), - getShadowVolume (getDllName(), getShadowVolumeFctName), + ShadowVolume(const Zstring& volumeNamePf) : //throw(FileError) + createShadowCopy (getDllName(), funName_createShadowCopy), + releaseShadowCopy(getDllName(), funName_releaseShadowCopy), + getShadowVolume (getDllName(), funName_getShadowVolume), + getLastError (getDllName(), funName_getLastError), backupHandle(nullptr) { //VSS does not support running under WOW64 except for Windows XP and Windows Server 2003 //(Reference: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx) if (runningWOW64()) - throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" + - _("Making shadow copies on WOW64 is not supported. Please use FreeFileSync 64-bit version.")); + throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" + + _("Please use FreeFileSync 64-bit version to create shadow copies on this system.")); //check if shadow copy dll was loaded correctly - if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume) - throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" + - replaceCpy(_("Cannot load file %x."), L"%x", std::wstring(L"\"") + getDllName() + L"\"")); + if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume || !getLastError) + throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" + + replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); //--------------------------------------------------------------------------------------------------------- //start shadow volume copy service: - const unsigned int BUFFER_SIZE = 10000; - std::vector<wchar_t> msgBuffer(BUFFER_SIZE); - backupHandle = createShadowCopy(volumeNameFormatted.c_str(), - &msgBuffer[0], BUFFER_SIZE); + backupHandle = createShadowCopy(volumeNamePf.c_str()); if (!backupHandle) - throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" + - &msgBuffer[0] + L" Volume: \"" + volumeNameFormatted + L"\""); + throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" + + getLastError() + L" Volume: " + fmtFileName(volumeNamePf)); - std::vector<wchar_t> volBuffer(BUFFER_SIZE); - getShadowVolume(backupHandle, &volBuffer[0], BUFFER_SIZE); - shadowVol = Zstring(&volBuffer[0]) + FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash + shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash } ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary - Zstring getShadowVolumeName() const { return shadowVol; } //with trailing path separator + Zstring geNamePf() const { return shadowVolPf; } //with trailing path separator private: ShadowVolume(const ShadowVolume&); ShadowVolume& operator=(const ShadowVolume&); - const DllFun<CreateShadowCopyFct> createShadowCopy; - const DllFun<ReleaseShadowCopyFct> releaseShadowCopy; - const DllFun<GetShadowVolumeFct> getShadowVolume; - - Zstring shadowVol; + const DllFun<FunType_createShadowCopy> createShadowCopy; + const DllFun<FunType_releaseShadowCopy> releaseShadowCopy; + const DllFun<FunType_getShadowVolume> getShadowVolume; + const DllFun<FunType_getLastError> getLastError; + Zstring shadowVolPf; ShadowHandle backupHandle; }; //############################################################################################################# @@ -94,34 +90,33 @@ private: Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) { - std::vector<wchar_t> volBuffer(1000); + DWORD bufferSize = 10000; + std::vector<wchar_t> volBuffer(bufferSize); if (!::GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName, &volBuffer[0], //__out LPTSTR lpszVolumePathName, - static_cast<DWORD>(volBuffer.size()))) //__in DWORD cchBufferLength - throw FileError(_("Cannot determine volume name for file:") + L"\n\"" + inputFile + L"\""); + bufferSize)) //__in DWORD cchBufferLength + throw FileError(replaceCpy(_("Path %x does not contain a volume name."), L"%x", fmtFileName(inputFile))); - Zstring volumeNameFormatted = &volBuffer[0]; - if (!endsWith(volumeNameFormatted, FILE_NAME_SEPARATOR)) - volumeNameFormatted += FILE_NAME_SEPARATOR; + const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found. - const size_t pos = inputFile.find(volumeNameFormatted); //inputFile needs NOT to begin with volumeNameFormatted: consider for example \\?\ prefix! + const size_t pos = inputFile.find(volumeNamePf); //inputFile needs NOT to begin with volumeNamePf: consider for example \\?\ prefix! if (pos == Zstring::npos) { std::wstring msg = _("Volume name %x not part of file name %y!"); - replace(msg, L"%x", std::wstring(L"\"") + volumeNameFormatted + L"\"", false); - replace(msg, L"%y", std::wstring(L"\"") + inputFile + L"\"", false); + replace(msg, L"%x", fmtFileName(volumeNamePf), false); + replace(msg, L"%y", fmtFileName(inputFile), false); throw FileError(msg); } //get or create instance of shadow volume - VolNameShadowMap::const_iterator iter = shadowVol.find(volumeNameFormatted); + VolNameShadowMap::const_iterator iter = shadowVol.find(volumeNamePf); if (iter == shadowVol.end()) { - auto newEntry = std::make_shared<ShadowVolume>(volumeNameFormatted); - iter = shadowVol.insert(std::make_pair(volumeNameFormatted, newEntry)).first; + auto newEntry = std::make_shared<ShadowVolume>(volumeNamePf); + iter = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first; } //return filename alias on shadow copy volume - return iter->second->getShadowVolumeName() + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length()); + return iter->second->geNamePf() + Zstring(inputFile.c_str() + pos + volumeNamePf.length()); } diff --git a/lib/shadow.h b/lib/shadow.h index 2d933840..5335e95d 100644 --- a/lib/shadow.h +++ b/lib/shadow.h @@ -20,7 +20,7 @@ class ShadowCopy //buffer access to Windows Volume Shadow Copy Service public: ShadowCopy() {} - Zstring makeShadowCopy(const Zstring& inputFile); //throw(FileError); returns filename on shadow copy + Zstring makeShadowCopy(const Zstring& inputFile); //throw FileError; returns filename on shadow copy private: ShadowCopy(const ShadowCopy&); diff --git a/lib/statistics.h b/lib/statistics.h deleted file mode 100644 index bddf129d..00000000 --- a/lib/statistics.h +++ /dev/null @@ -1,73 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef STATISTICS_H_INCLUDED -#define STATISTICS_H_INCLUDED - -#include <vector> -#include <map> -#include <memory> -#include <wx/defs.h> -#include <wx/string.h> -#include <wx/stopwatch.h> -#include <zen/deprecate.h> - -class RetrieveStatistics -{ -public: - ZEN_DEPRECATE ~RetrieveStatistics(); //remove code after measurements! - void writeEntry(double value, int objects); - -private: - struct StatEntry - { - long time; - int objects; - double value; - }; - std::vector<StatEntry> data; - wxStopWatch timer; -}; - - -class Statistics -{ -public: - Statistics(int totalObjectCount, - double totalDataAmount, - unsigned windowSizeRemainingTime, //time in ms - unsigned windowSizeBytesPerSecond); //time in ms - - void addMeasurement(int objectsCurrent, double dataCurrent); - void setNewTotal(int totalObjectCount, double totalDataAmount); //may change during sync! - - wxString getRemainingTime() const; //returns the remaining time in milliseconds - wxString getBytesPerSecond() const; - - void pauseTimer(); - void resumeTimer(); - -private: - int objectsTotal; - double dataTotal; - - const unsigned int windowSizeRemTime; //"window width" of statistics used for calculation of remaining time in ms - const unsigned int windowSizeBPS; // - const unsigned int windowMax; - - struct Record - { - int objects; //object count - double data; //unit: bytes - }; - - typedef std::multimap<long, Record> TimeRecordMap; //time, unit: milliseconds - TimeRecordMap measurements; // - - wxStopWatch timer; -}; - -#endif // STATISTICS_H_INCLUDED diff --git a/lib/status_handler.cpp b/lib/status_handler.cpp index 5e75b60e..d9655c48 100644 --- a/lib/status_handler.cpp +++ b/lib/status_handler.cpp @@ -6,9 +6,12 @@ #include "status_handler.h" #include <wx/app.h> -#include <ctime> +#include <zen/tick_count.h> -void updateUiNow() +using namespace zen; + + +void zen::updateUiNow() { //process UI events and prevent application from "not responding" -> NO performance issue! wxTheApp->Yield(); @@ -17,15 +20,16 @@ void updateUiNow() // wxTheApp->Dispatch(); } - -bool updateUiIsAllowed() +namespace { - const std::clock_t CLOCK_UPDATE_INTERVAL = UI_UPDATE_INTERVAL * CLOCKS_PER_SEC / 1000; +const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000; +TickVal lastExec = getTicks(); +}; - static std::clock_t lastExec = 0; - const std::clock_t now = std::clock(); //this is quite fast: 2 * 10^-5 - - if (now - lastExec >= CLOCK_UPDATE_INTERVAL) //perform ui updates not more often than necessary +bool zen::updateUiIsAllowed() +{ + const TickVal now = getTicks(); //0 on error + if (now - lastExec >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary { lastExec = now; return true; diff --git a/lib/status_handler.h b/lib/status_handler.h index b246e49c..7b7cb3d7 100644 --- a/lib/status_handler.h +++ b/lib/status_handler.h @@ -10,9 +10,10 @@ #include "../process_callback.h" #include <string> #include <zen/int64.h> +#include <zen/i18n.h> -const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss - +namespace zen +{ bool updateUiIsAllowed(); //test if a specific amount of time is over void updateUiNow(); //do the updating @@ -31,11 +32,42 @@ struct AbortCallback }; -//actual callback implementation will have to satisfy "process" and "gui" -class StatusHandler : public ProcessCallback, public AbortCallback +//common statistics "everybody" needs +struct Statistics +{ + virtual ~Statistics() {} + + virtual ProcessCallback::Phase currentPhase() const = 0; + + virtual int getObjectsCurrent(ProcessCallback::Phase phaseId) const = 0; + virtual int getObjectsTotal (ProcessCallback::Phase phaseId) const = 0; + + virtual Int64 getDataCurrent(ProcessCallback::Phase phaseId) const = 0; + virtual Int64 getDataTotal (ProcessCallback::Phase phaseId) const = 0; + + virtual const std::wstring& currentStatusText() const = 0; +}; + + +//partial callback implementation with common functionality for "batch", "GUI/Compare" and "GUI/Sync" +class StatusHandler : public ProcessCallback, public AbortCallback, public Statistics { public: - StatusHandler() : abortRequested(false) {} + StatusHandler() : currentPhase_(PHASE_NONE), + numbersCurrent_(4), //init with phase count + numbersTotal_ (4), // + abortRequested(false) {} + +protected: + //implement parts of ProcessCallback + virtual void initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseId) + { + currentPhase_ = phaseId; + refNumbers(numbersTotal_, currentPhase_) = std::make_pair(objectsTotal, dataTotal); + } + + virtual void updateProcessedData(int objectsDelta, Int64 dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods should NOT throw in order + virtual void updateTotalData (int objectsDelta, Int64 dataDelta) { updateData(numbersTotal_ , objectsDelta, dataDelta); } //to properly allow undoing setting of statistics! virtual void requestUiRefresh() { @@ -46,12 +78,68 @@ public: abortThisProcess(); //abort can be triggered by requestAbortion() } + virtual void reportStatus(const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw AbortThisProcess */ } + virtual void reportInfo (const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw AbortThisProcess */ } //log text in derived class + + //implement AbortCallback + virtual void requestAbortion() + { + abortRequested = true; + statusText_ =_("Abort requested: Waiting for current operation to finish..."); + } //this does NOT call abortThisProcess immediately, but when we're out of the C GUI call stack + + //implement Statistics + virtual Phase currentPhase() const { return currentPhase_; } + + virtual int getObjectsCurrent(Phase phaseId) const { return refNumbers(numbersCurrent_, phaseId).first; } + virtual int getObjectsTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).first; } + + virtual Int64 getDataCurrent(Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersCurrent_, phaseId).second; } + virtual Int64 getDataTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).second; } + + virtual const std::wstring& currentStatusText() const { return statusText_; } + + //enrich this interface virtual void abortThisProcess() = 0; - virtual void requestAbortion() { abortRequested = true; } //this does NOT call abortThisProcess immediately, but when appropriate (e.g. async. processes finished) - bool abortIsRequested() { return abortRequested; } + + bool abortIsRequested() const { return abortRequested; } private: + typedef std::vector<std::pair<int, Int64>> StatNumbers; + + void updateData(StatNumbers& num, int objectsDelta, Int64 dataDelta) + { + auto& st = refNumbers(num, currentPhase_); + st.first += objectsDelta; + st.second += dataDelta; + } + + static const std::pair<int, Int64>& refNumbers(const StatNumbers& num, Phase phaseId) + { + switch (phaseId) + { + case PHASE_SCANNING: + return num[0]; + case PHASE_COMPARING_CONTENT: + return num[1]; + case PHASE_SYNCHRONIZING: + return num[2]; + case PHASE_NONE: + break; + } + assert(false); + return num[3]; //dummy entry! + } + + static std::pair<int, Int64>& refNumbers(StatNumbers& num, Phase phaseId) { return const_cast<std::pair<int, Int64>&>(refNumbers(static_cast<const StatNumbers&>(num), phaseId)); } + + Phase currentPhase_; + StatNumbers numbersCurrent_; + StatNumbers numbersTotal_; + std::wstring statusText_; + bool abortRequested; }; +} #endif // STATUSHANDLER_H_INCLUDED diff --git a/lib/status_handler_impl.h b/lib/status_handler_impl.h index 7141d75c..10890dbe 100644 --- a/lib/status_handler_impl.h +++ b/lib/status_handler_impl.h @@ -8,7 +8,7 @@ #define STATUSHANDLER_IMPL_H_INCLUDED #include <zen/file_error.h> -#include "status_handler.h" +#include "process_callback.h" template <typename Function> inline bool tryReportingError(Function cmd, ProcessCallback& handler) //return "true" on success, "false" if error was ignored diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp index 10bb698a..3f6cc0be 100644 --- a/lib/xml_base.cpp +++ b/lib/xml_base.cpp @@ -31,7 +31,7 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff if (!startsWith(fileBegin, xmlBegin) && !startsWith(fileBegin, zen::BYTE_ORDER_MARK_UTF8 + xmlBegin)) //respect BOM! - throw FfsXmlError(_("Error parsing configuration file:") + L"\n\"" + filename + L"\""); + throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); } const zen::UInt64 fs = zen::getFilesize(filename); //throw FileError @@ -40,12 +40,12 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff FileInput inputFile(filename); //throw FileError const size_t bytesRead = inputFile.read(&stream[0], stream.size()); //throw FileError if (bytesRead < to<size_t>(fs)) - throw FfsXmlError(_("Error reading file:") + L"\n\"" + filename + L"\""); + throw FfsXmlError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename))); } catch (const FileError& error) { if (!fileExists(filename)) - throw FfsXmlError(_("File does not exist:") + L"\n\"" + filename+ L"\""); + throw FfsXmlError(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename))); throw FfsXmlError(error.toString()); } @@ -54,9 +54,13 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff { zen::parse(stream, doc); //throw XmlParsingError } - catch (const XmlParsingError&) + catch (const XmlParsingError& e) { - throw FfsXmlError(_("Error parsing configuration file:") + L"\n\"" + filename + L"\""); + throw FfsXmlError( + replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), + L"%x", fmtFileName(filename)), + L"%y", numberTo<std::wstring>(e.row)), + L"%z", numberTo<std::wstring>(e.col))); } } @@ -77,7 +81,7 @@ const std::wstring xmlAccess::getErrorMessageFormatted(const XmlIn& in) } -void xmlAccess::saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename) //throw (FfsXmlError) +void xmlAccess::saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename) //throw FfsXmlError { std::string stream = serialize(doc); //throw () @@ -98,7 +102,7 @@ void xmlAccess::saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename) try { FileOutput outputFile(filename, FileOutput::ACC_OVERWRITE); //throw FileError - outputFile.write(stream.c_str(), stream.length()); // + outputFile.write(stream.c_str(), stream.length()); // } catch (const FileError& error) //more detailed error messages than with wxWidgets { |