summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:19:49 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:19:49 +0200
commitc8e0e909b4a8d18319fc65434a10dc446434817c (patch)
treeeee91e7d2ce229dd043811eae8f1e2bd78061916 /lib
parent5.2 (diff)
downloadFreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.tar.gz
FreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.tar.bz2
FreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.zip
5.3
Diffstat (limited to 'lib')
-rw-r--r--lib/IFileOperation/FileOperation_Vista.vcxproj240
-rw-r--r--lib/IFileOperation/dll_main.cpp25
-rw-r--r--lib/IFileOperation/file_op.cpp190
-rw-r--r--lib/IFileOperation/file_op.h62
-rw-r--r--lib/ShadowCopy/LockFile.cpp5
-rw-r--r--lib/ShadowCopy/Shadow_Server2003.vcxproj12
-rw-r--r--lib/ShadowCopy/Shadow_Windows7.vcxproj12
-rw-r--r--lib/ShadowCopy/Shadow_XP.vcxproj12
-rw-r--r--lib/ShadowCopy/shadow.cpp36
-rw-r--r--lib/ShadowCopy/shadow.h35
-rw-r--r--lib/Thumbnail/thumbnail.cpp28
-rw-r--r--lib/Thumbnail/thumbnail.h8
-rw-r--r--lib/binary.cpp4
-rw-r--r--lib/binary.h2
-rw-r--r--lib/db_file.cpp34
-rw-r--r--lib/dir_exist_async.h5
-rw-r--r--lib/dir_lock.cpp407
-rw-r--r--lib/dir_lock.h8
-rw-r--r--lib/ffs_paths.h14
-rw-r--r--lib/icon_buffer.cpp27
-rw-r--r--lib/localization.cpp16
-rw-r--r--lib/parallel_scan.cpp21
-rw-r--r--lib/parse_plural.h2
-rw-r--r--lib/perf_check.cpp (renamed from lib/statistics.cpp)140
-rw-r--r--lib/perf_check.h40
-rw-r--r--lib/process_xml.cpp104
-rw-r--r--lib/process_xml.h26
-rw-r--r--lib/recycler.cpp210
-rw-r--r--lib/recycler.h48
-rw-r--r--lib/resolve_path.cpp241
-rw-r--r--lib/resources.cpp14
-rw-r--r--lib/shadow.cpp69
-rw-r--r--lib/shadow.h2
-rw-r--r--lib/statistics.h73
-rw-r--r--lib/status_handler.cpp22
-rw-r--r--lib/status_handler.h102
-rw-r--r--lib/status_handler_impl.h2
-rw-r--r--lib/xml_base.cpp18
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
{
bgstack15