summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Batch.icobin103260 -> 0 bytes
-rw-r--r--lib/FreeFileSync.icobin114124 -> 0 bytes
-rw-r--r--lib/ShadowCopy/Shadow_Windows7.vcxproj240
-rw-r--r--lib/ShadowCopy/shadow.cpp199
-rw-r--r--lib/ShadowCopy/shadow.h94
-rw-r--r--lib/SyncDB.icobin111496 -> 0 bytes
-rw-r--r--lib/Thumbnail/Thumbnail.vcxproj236
-rw-r--r--lib/Thumbnail/thumbnail.cpp486
-rw-r--r--lib/Thumbnail/thumbnail.h85
-rw-r--r--lib/binary.cpp134
-rw-r--r--lib/binary.h25
-rw-r--r--lib/cmp_filetime.h57
-rw-r--r--lib/db_file.cpp821
-rw-r--r--lib/db_file.h102
-rw-r--r--lib/dir_exist_async.h78
-rw-r--r--lib/dir_lock.cpp685
-rw-r--r--lib/dir_lock.h45
-rw-r--r--lib/error_log.h43
-rw-r--r--lib/ffs_paths.cpp144
-rw-r--r--lib/ffs_paths.h25
-rw-r--r--lib/generate_logfile.h181
-rw-r--r--lib/hard_filter.cpp404
-rw-r--r--lib/hard_filter.h270
-rw-r--r--lib/help_provider.h98
-rw-r--r--lib/icon_buffer.cpp674
-rw-r--r--lib/icon_buffer.h52
-rw-r--r--lib/localization.cpp485
-rw-r--r--lib/localization.h46
-rw-r--r--lib/lock_holder.h57
-rw-r--r--lib/norm_filter.h85
-rw-r--r--lib/osx_file_icon.h36
-rw-r--r--lib/osx_file_icon.mm179
-rw-r--r--lib/parallel_scan.cpp588
-rw-r--r--lib/parallel_scan.h76
-rw-r--r--lib/parse_lng.h706
-rw-r--r--lib/parse_plural.h478
-rw-r--r--lib/perf_check.cpp262
-rw-r--r--lib/perf_check.h45
-rw-r--r--lib/process_xml.cpp1577
-rw-r--r--lib/process_xml.h298
-rw-r--r--lib/resolve_path.cpp708
-rw-r--r--lib/resolve_path.h36
-rw-r--r--lib/return_codes.h30
-rw-r--r--lib/shadow.cpp131
-rw-r--r--lib/shadow.h36
-rw-r--r--lib/soft_filter.h112
-rw-r--r--lib/status_handler.cpp29
-rw-r--r--lib/status_handler.h147
-rw-r--r--lib/status_handler_impl.h37
-rw-r--r--lib/versioning.cpp439
-rw-r--r--lib/versioning.h89
-rw-r--r--lib/xml_base.cpp108
-rw-r--r--lib/xml_base.h42
53 files changed, 0 insertions, 12040 deletions
diff --git a/lib/Batch.ico b/lib/Batch.ico
deleted file mode 100644
index faa2db64..00000000
--- a/lib/Batch.ico
+++ /dev/null
Binary files differ
diff --git a/lib/FreeFileSync.ico b/lib/FreeFileSync.ico
deleted file mode 100644
index 88f656ee..00000000
--- a/lib/FreeFileSync.ico
+++ /dev/null
Binary files differ
diff --git a/lib/ShadowCopy/Shadow_Windows7.vcxproj b/lib/ShadowCopy/Shadow_Windows7.vcxproj
deleted file mode 100644
index 86df3453..00000000
--- a/lib/ShadowCopy/Shadow_Windows7.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>Windows7</ProjectName>
- <ProjectGuid>{7E217D76-90A5-4B03-A6F8-E7C3ADD22901}</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>
- <UseOfAtl>false</UseOfAtl>
- <PlatformToolset>v120_xp</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>v120_xp</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <CharacterSet>Unicode</CharacterSet>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <PlatformToolset>v120_xp</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>v120_xp</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>
- <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
- <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'">Shadow_$(ProjectName)_$(Platform)</TargetName>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Shadow_$(ProjectName)_$(Platform)</TargetName>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Shadow_$(ProjectName)_$(Platform)</TargetName>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Shadow_$(ProjectName)_$(Platform)</TargetName>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <BuildLog>
- <Path>$(IntDir)Build.html</Path>
- </BuildLog>
- <ClCompile>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MinimalRebuild>false</MinimalRebuild>
- <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
- <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
- <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
- <AdditionalIncludeDirectories>../..;C:\Data\C++\boost</AdditionalIncludeDirectories>
- <SmallerTypeCheck>true</SmallerTypeCheck>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- </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>
- <AdditionalLibraryDirectories>C:\Data\C++\Boost\stage\lib</AdditionalLibraryDirectories>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <BuildLog>
- <Path>$(IntDir)Build.html</Path>
- </BuildLog>
- <Midl>
- <TargetEnvironment>X64</TargetEnvironment>
- </Midl>
- <ClCompile>
- <Optimization>Disabled</Optimization>
- <MinimalRebuild>false</MinimalRebuild>
- <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
- <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
- <AdditionalIncludeDirectories>../..;C:\Data\C++\boost</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SmallerTypeCheck>true</SmallerTypeCheck>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- </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>
- <AdditionalLibraryDirectories>C:\Data\C++\Boost\stage_x64\lib</AdditionalLibraryDirectories>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <BuildLog>
- <Path>$(IntDir)Build.html</Path>
- </BuildLog>
- <ClCompile>
- <Optimization>MaxSpeed</Optimization>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(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>../..;C:\Data\C++\boost</AdditionalIncludeDirectories>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- </ClCompile>
- <Link>
- <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <SubSystem>Windows</SubSystem>
- <OptimizeReferences>true</OptimizeReferences>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
- <ProfileGuidedDatabase>
- </ProfileGuidedDatabase>
- <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
- <TargetMachine>MachineX86</TargetMachine>
- <AdditionalLibraryDirectories>C:\Data\C++\Boost\stage\lib</AdditionalLibraryDirectories>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </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>
- <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>../..;C:\Data\C++\boost</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- </ClCompile>
- <Link>
- <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <SubSystem>Windows</SubSystem>
- <OptimizeReferences>true</OptimizeReferences>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
- <ProfileGuidedDatabase>
- </ProfileGuidedDatabase>
- <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
- <TargetMachine>MachineX64</TargetMachine>
- <AdditionalLibraryDirectories>C:\Data\C++\Boost\stage_x64\lib</AdditionalLibraryDirectories>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="..\..\zen\debug_memory_leaks.cpp" />
- <ClCompile Include="shadow.cpp" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="shadow.h" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project> \ No newline at end of file
diff --git a/lib/ShadowCopy/shadow.cpp b/lib/ShadowCopy/shadow.cpp
deleted file mode 100644
index adc7c5c2..00000000
--- a/lib/ShadowCopy/shadow.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "shadow.h"
-#include <algorithm>
-#include <string>
-#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"
-#include "xp/inc/vswriter.h"
-#include "xp/inc/vsbackup.h"
-
-#elif defined USE_SHADOW_2003
-#include "Server 2003/inc/vss.h"
-#include "Server 2003/inc/vswriter.h"
-#include "Server 2003/inc/vsbackup.h"
-
-#elif defined USE_SHADOW_WINDOWS7
-#include <vss.h> //
-#include <vswriter.h> //part of Windows SDK for Windows 7
-#include <vsbackup.h> //
-#pragma comment(lib, "VssApi.lib")
-
-#else
-#error adapt!
-#endif
-
-using namespace zen;
-
-
-struct shadow::ShadowData
-{
- ShadowData(const ComPtr<IVssBackupComponents>& backupComp,
- const std::wstring& shadowVolume) : backupComp_(backupComp), shadowVolume_(shadowVolume) {}
-
- ComPtr<IVssBackupComponents> backupComp_;
- std::wstring shadowVolume_;
-};
-
-
-namespace
-{
-std::wstring formatVssError(HRESULT hr) //at least the one's from IVssBackupComponents::AddToSnapshotSet; return empty if no format found
-{
- switch (hr)
- {
- case VSS_E_BAD_STATE:
- return L"VSS_E_BAD_STATE";
- case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED:
- return L"VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED";
- case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED:
- return L"VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED";
- case VSS_E_OBJECT_NOT_FOUND:
- return L"VSS_E_OBJECT_NOT_FOUND";
- case VSS_E_PROVIDER_NOT_REGISTERED:
- return L"VSS_E_PROVIDER_NOT_REGISTERED";
- case VSS_E_PROVIDER_VETO:
- return L"VSS_E_PROVIDER_VETO";
- case VSS_E_VOLUME_NOT_SUPPORTED:
- return L"VSS_E_VOLUME_NOT_SUPPORTED";
- case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER:
- return L"VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER";
- case VSS_E_UNEXPECTED_PROVIDER_ERROR:
- return L"VSS_E_UNEXPECTED_PROVIDER_ERROR";
- default:
- return std::wstring();
- }
-}
-
-
-shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw SysError
-{
- ComPtr<IVssBackupComponents> backupComp;
- {
- HRESULT hr = ::CreateVssBackupComponents(backupComp.init());
- if (FAILED(hr))
- {
- if (hr == E_ACCESSDENIED)
- throw SysError(formatComError(L"The caller does not have sufficient backup privileges or is not an administrator.", hr));
- throw SysError(formatComError(L"Error calling \"CreateVssBackupComponents\".", hr));
- }
- }
-
- ZEN_COM_CHECK(backupComp->InitializeForBackup()); //throw SysError
-
- //SetContext() only required if different than the default, VSS_CTX_BACKUP; not implemented on XP!!!
- //ZEN_COM_CHECK(backupComp->SetContext(VSS_CTX_BACKUP)); //throw SysError
-
- ZEN_COM_CHECK(backupComp->SetBackupState(false, false, VSS_BT_FULL)); //throw SysError
-
-
- //the Shadow Copy Optimization Writer removes items it considers non-essential,
- //http://msdn.microsoft.com/en-US/library/bb968827#shadow_copy_optimization_writer
- //like the exclusions in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\BackupRestore\FilesNotToSnapshot
- //http://msdn.microsoft.com/en-us/library/aa819132%28v=vs.85%29.aspx
- //Outlook *.ost files in particular:
- //https://sourceforge.net/p/freefilesync/discussion/help/thread/722dcbfb
- const VSS_ID disabledWriters[] = { { 0x4dc3bdd4, 0xab48, 0x4d07, { 0xad, 0xb0, 0x3b, 0xee, 0x29, 0x26, 0xfd, 0x7f } } }; //Shadow Copy Optimization Writer
- {
- HRESULT hr = backupComp->DisableWriterClasses(disabledWriters, 1);
- if (FAILED(hr) && hr != E_NOTIMPL) //DisableWriterClasses() is not implemented on Windows XP, although MSDN documented otherwise!
- throw SysError(formatComError(L"Error calling \"backupComp->DisableWriterClasses\".", hr));
- }
-
-
- auto waitForComFuture = [](IVssAsync& fut)
- {
- ZEN_COM_CHECK(fut.Wait());
-
- HRESULT hr = S_OK;
- ZEN_COM_CHECK(fut.QueryStatus(&hr, nullptr)); //check if the async operation succeeded...
- if (FAILED(hr))
- throw SysError(formatComError(L"Error calling \"fut->QueryStatus\".", hr));
- };
-
- ComPtr<IVssAsync> gatherAsync;
- ZEN_COM_CHECK(backupComp->GatherWriterMetadata(gatherAsync.init()));
- waitForComFuture(*gatherAsync); //failure can happen if XP-version of VSS is used on Windows Vista (which needs at least VSS-Server2003 build)
-
- VSS_ID snapshotSetId = {};
- ZEN_COM_CHECK(backupComp->StartSnapshotSet(&snapshotSetId));
- ScopeGuard guardSnapShot = makeGuard([&] { backupComp->AbortBackup(); });
- //Quote: "This method must be called if a backup operation terminates after the creation of a
- //shadow copy set with "StartSnapshotSet" and before "DoSnapshotSet" returns."
-
- VSS_ID SnapShotId = {};
- {
- HRESULT hr = backupComp->AddToSnapshotSet(const_cast<wchar_t*>(volumeName), GUID_NULL, &SnapShotId);
- if (FAILED(hr))
- {
- if (hr == VSS_E_VOLUME_NOT_SUPPORTED)
- throw SysError(L"Volume Shadow Copy Service is not supported on this volume!");
- const std::wstring vssError = formatVssError(hr);
- if (!vssError.empty())
- throw SysError(L"Error calling \"backupComp->AddToSnapshotSet\": " + vssError);
- else
- throw SysError(formatComError(L"Error calling \"backupComp->AddToSnapshotSet\".", hr));
- }
- }
-
- ComPtr<IVssAsync> prepareAsync;
- ZEN_COM_CHECK(backupComp->PrepareForBackup(prepareAsync.init()));
- waitForComFuture(*prepareAsync);
-
- ComPtr<IVssAsync> snapshotAsync;
- ZEN_COM_CHECK(backupComp->DoSnapshotSet(snapshotAsync.init()));
- guardSnapShot.dismiss();
- waitForComFuture(*snapshotAsync);
-
- VSS_SNAPSHOT_PROP props = {};
- ZEN_COM_CHECK(backupComp->GetSnapshotProperties(SnapShotId, &props));
- ZEN_ON_SCOPE_EXIT(::VssFreeSnapshotProperties(&props));
-
- //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)
-{
- try
- {
- ShadowData result = ::createShadowCopy(volumeName); //throw SysError
- return new ShadowData(result); //shadow handle owned by caller! std::bad_alloc?
- }
- catch (const zen::SysError& e)
- {
- lastErrorMessage.reset(new std::wstring(e.toString()));
- return nullptr;
- }
-}
-
-
-const wchar_t* shadow::getShadowVolume(shadow::ShadowHandle handle)
-{
- return handle ? handle->shadowVolume_.c_str() : nullptr; //better fail in client code than here!
-}
-
-
-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
deleted file mode 100644
index e68b2655..00000000
--- a/lib/ShadowCopy/shadow.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef SHADOWCOPY_H_14837413434
-#define SHADOWCOPY_H_14837413434
-
-#ifdef SHADOWDLL_EXPORTS
-#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport)
-#else
-#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport)
-#endif
-
-#include <zen/build_info.h>
-#include <zen/win_ver.h>
-
-
-class IVssBackupComponents;
-
-namespace shadow
-{
-/*--------------
- |declarations|
- --------------*/
-
-//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
-struct ShadowData;
-typedef ShadowData* ShadowHandle;
-
-//volumeName *must* end with "\"
-DLL_FUNCTION_DECLARATION
-ShadowHandle createShadowCopy(const wchar_t* volumeName); //returns nullptr on failure!
-
-//release the backupHandle after shadow copy is not needed anymore!
-DLL_FUNCTION_DECLARATION
-void releaseShadowCopy(ShadowHandle handle);
-
-DLL_FUNCTION_DECLARATION
-const wchar_t* getShadowVolume(ShadowHandle handle); //never fails, returns shadowVolName, never ending with "\"
-
-//get last error message if any of the functions above fail
-DLL_FUNCTION_DECLARATION
-const wchar_t* getLastError(); //no nullptr check required!
-//##########################################################################################
-
-
-/*----------
- |typedefs|
- ----------*/
-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 funName_createShadowCopy [] = "createShadowCopy";
-const char funName_releaseShadowCopy[] = "releaseShadowCopy";
-const char funName_getShadowVolume [] = "getShadowVolume";
-const char funName_getLastError [] = "getLastError";
-/*---------------
- |library names|
- ---------------*/
-
-inline
-const wchar_t* getDllName()
-{
- // distinguish a bunch of VSS builds: we use XP, Server 2003 and Server 2008 R2 implementations...
- // VSS version and compatibility overview: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx
-
- if (zen::win7OrLater()) //Windows Server 2008 R2 or Windows 7
- return zen::is64BitBuild ?
- L"Shadow_Windows7_x64.dll" :
- L"Shadow_Windows7_Win32.dll";
- //else if (vistaOrLater()) -> skip Windows Server 2008 and Windows Vista
- // ;
- else if (zen::winServer2003orLater()) //Windows Server 2003 and Windows Server 2003 R2
- return zen::is64BitBuild ?
- L"Shadow_Server2003_x64.dll" :
- L"Shadow_Server2003_Win32.dll";
- else //Windows XP
- return zen::is64BitBuild ?
- L"Shadow_XP_x64.dll" :
- L"Shadow_XP_Win32.dll";
-}
-}
-
-#undef DLL_FUNCTION_DECLARATION
-
-#endif //SHADOWCOPY_H_14837413434
diff --git a/lib/SyncDB.ico b/lib/SyncDB.ico
deleted file mode 100644
index 9740a338..00000000
--- a/lib/SyncDB.ico
+++ /dev/null
Binary files differ
diff --git a/lib/Thumbnail/Thumbnail.vcxproj b/lib/Thumbnail/Thumbnail.vcxproj
deleted file mode 100644
index a98aadab..00000000
--- a/lib/Thumbnail/Thumbnail.vcxproj
+++ /dev/null
@@ -1,236 +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">
- <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>v120_xp</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>v120_xp</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <CharacterSet>Unicode</CharacterSet>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <PlatformToolset>v120_xp</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>v120_xp</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'">.\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
- <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir>
- <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|x64'">Thumbnail_$(Platform)</TargetName>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Thumbnail_$(Platform)</TargetName>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Thumbnail_$(Platform)</TargetName>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Thumbnail_$(Platform)</TargetName>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <BuildLog>
- <Path>$(IntDir)Build.html</Path>
- </BuildLog>
- <ClCompile>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MinimalRebuild>false</MinimalRebuild>
- <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
- <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
- <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
- <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
- <SmallerTypeCheck>true</SmallerTypeCheck>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
- </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>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </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>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MinimalRebuild>false</MinimalRebuild>
- <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
- <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
- <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
- <SmallerTypeCheck>true</SmallerTypeCheck>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- </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>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <BuildLog>
- <Path>$(IntDir)Build.html</Path>
- </BuildLog>
- <ClCompile>
- <Optimization>MaxSpeed</Optimization>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
- <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
- <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
- </ClCompile>
- <Link>
- <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <SubSystem>Windows</SubSystem>
- <OptimizeReferences>true</OptimizeReferences>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
- <ProfileGuidedDatabase>
- </ProfileGuidedDatabase>
- <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
- <TargetMachine>MachineX86</TargetMachine>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </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>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
- <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
- <AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- </ClCompile>
- <Link>
- <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <SubSystem>Windows</SubSystem>
- <OptimizeReferences>true</OptimizeReferences>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
- <ProfileGuidedDatabase>
- </ProfileGuidedDatabase>
- <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
- <TargetMachine>MachineX64</TargetMachine>
- <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="..\..\zen\debug_memory_leaks.cpp" />
- <ClCompile Include="thumbnail.cpp" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="thumbnail.h" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project> \ No newline at end of file
diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp
deleted file mode 100644
index 53c30bc3..00000000
--- a/lib/Thumbnail/thumbnail.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "thumbnail.h"
-#include <algorithm>
-#include <string>
-
-#define WIN32_LEAN_AND_MEAN
-#include <zen/win.h>
-#include <zen/win_ver.h>
-#include <zen/sys_error.h>
-
-#define STRICT_TYPED_ITEMIDS //better type safety for IDLists
-#include <Shlobj.h>
-
-#include <Shellapi.h>
-#include <CommonControls.h>
-
-#include <zen/com_error.h>
-#include <zen/com_ptr.h>
-#include <zen/string_tools.h>
-#include <zen/scope_guard.h>
-#include <zen/basic_math.h>
-//#include <zen/perf.h>
-
-using namespace zen;
-
-
-namespace
-{
-thumb::ImageData* allocImageData(int width, int height) //throw SysError; return value always bound!
-{
- ZEN_COM_ASSERT(width >= 0 && height >= 0); //throw SysError
-
- std::unique_ptr<thumb::ImageData> idata = make_unique<thumb::ImageData>();
-
- idata->width = width;
- idata->height = height;
- idata->rgb = new unsigned char[width * height * 4];
- idata->alpha = idata->rgb + width * height * 3;
-
- return idata.release();
-}
-
-void releaseImageData_impl(const thumb::ImageData* id)
-{
- if (id)
- {
- delete [] id->rgb;
- delete id;
- }
-}
-
-
-//caller takes ownership!
-HICON createIconFromBitmap(HBITMAP bitmap) //throw SysError
-{
- BITMAP bmpInfo = {};
- ZEN_COM_ASSERT(::GetObject(bitmap, //__in HGDIOBJ hgdiobj,
- sizeof(bmpInfo), //__in int cbBuffer,
- &bmpInfo)); //__out LPVOID lpvObject
- //no documented extended error info
-
- HDC hScreenDC = ::GetDC(nullptr);
- ZEN_COM_ASSERT(hScreenDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC));
-
- HBITMAP bitmapMask = ::CreateCompatibleBitmap(hScreenDC, bmpInfo.bmWidth, bmpInfo.bmHeight);
- ZEN_COM_ASSERT(bitmapMask); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmapMask));
-
- ICONINFO iconInfo = {};
- iconInfo.fIcon = true;
- iconInfo.hbmColor = bitmap;
- iconInfo.hbmMask = bitmapMask;
-
- HICON result = ::CreateIconIndirect(&iconInfo);
- if (!result) throw SysError(formatSystemError(L"CreateIconIndirect", getLastError()));
- return result;
-}
-
-
-//caller takes ownership!
-thumb::ImageData* convertToImageData(HBITMAP bmp) //throw SysError
-{
- //GetDIBits ????
-
- BITMAP bmpInfo = {};
- ZEN_COM_ASSERT(::GetObject(bmp, //__in HGDIOBJ hgdiobj,
- sizeof(BITMAP), //__in int cbBuffer,
- &bmpInfo)); //__out LPVOID lpvObject
-
- HDC hScreenDC = ::GetDC(nullptr);
- ZEN_COM_ASSERT(hScreenDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC));
-
- //32-bit RGB with alpha channel support
- BITMAPV5HEADER bi = {};
- bi.bV5Size = sizeof(bi);
- bi.bV5Width = bmpInfo.bmWidth;
- bi.bV5Height = -bmpInfo.bmHeight; //negative for top left origin
- bi.bV5Planes = 1;
- bi.bV5BitCount = 32;
- bi.bV5Compression = BI_BITFIELDS;
- bi.bV5AlphaMask = 0xFF000000;
- bi.bV5RedMask = 0x00FF0000;
- bi.bV5GreenMask = 0x0000FF00;
- bi.bV5BlueMask = 0x000000FF;
- unsigned char* bitsRgbBmp = nullptr;
-
- HBITMAP rgbBmp = ::CreateDIBSection(hScreenDC, //_In_ HDC hdc,
- reinterpret_cast<const BITMAPINFO*>(&bi), //_In_ const BITMAPINFO *pbmi,
- DIB_RGB_COLORS, //_In_ UINT iUsage,
- reinterpret_cast<VOID**>(&bitsRgbBmp), //_Out_ VOID **ppvBits,
- nullptr, //_In_ HANDLE hSection,
- 0); //_In_ DWORD dwOffset
- ZEN_COM_ASSERT(rgbBmp); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(rgbBmp););
- ZEN_COM_ASSERT(bitsRgbBmp); //check after rgbBmp is owned by us
-
- HDC memDCSrc = ::CreateCompatibleDC(hScreenDC);
- ZEN_COM_ASSERT(memDCSrc); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCSrc));
-
- HDC memDCTrg = ::CreateCompatibleDC(hScreenDC);
- ZEN_COM_ASSERT(memDCTrg); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCTrg));
-
- HGDIOBJ hgdiSrcOld = ::SelectObject(memDCSrc, bmp);
- ZEN_COM_ASSERT(hgdiSrcOld); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDCSrc, hgdiSrcOld));
-
- HGDIOBJ hgdiTrgOld = ::SelectObject(memDCTrg, rgbBmp);
- ZEN_COM_ASSERT(hgdiTrgOld); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDCTrg, hgdiTrgOld));
-
- if (!::BitBlt(memDCTrg, //_In_ HDC hdcDest,
- 0, //_In_ int nXDest,
- 0, //_In_ int nYDest,
- bmpInfo.bmWidth, //_In_ int nWidth,
- bmpInfo.bmHeight, //_In_ int nHeight,
- memDCSrc, //_In_ HDC hdcSrc,
- 0, //_In_ int nXSrc,
- 0, //_In_ int nYSrc,
- SRCCOPY)) //_In_ DWORD dwRop
- throw SysError(formatSystemError(L"BitBlt", getLastError()));
-
- //CreateDIBSection: "Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values."
- /*bool rv = */
- ::GdiFlush();
-
- thumb::ImageData* imgOut = allocImageData(bmpInfo.bmWidth, bmpInfo.bmHeight); //throw SysError
- ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); });
-
- unsigned char* rgbPtr = imgOut->rgb;
- unsigned char* alphaPtr = imgOut->alpha;
-
- for (int i = 0; i < bmpInfo.bmWidth * bmpInfo.bmHeight; ++i)
- {
- unsigned char b = *bitsRgbBmp++;
- unsigned char g = *bitsRgbBmp++;
- unsigned char r = *bitsRgbBmp++;
- unsigned char a = *bitsRgbBmp++;
-
- *rgbPtr++ = r;
- *rgbPtr++ = g;
- *rgbPtr++ = b;
- *alphaPtr++ = a;
- }
-
- guardImgData.dismiss();
- return imgOut;
-}
-
-
-//caller takes ownership!
-const thumb::ImageData* getThumbnail_impl(const wchar_t* filename, int requestedSize) //throw SysError
-{
- const std::wstring filenameStr(filename);
-
- ComPtr<IShellFolder> desktopFolder;
- ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError
- ZEN_COM_ASSERT(desktopFolder); //throw SysError -> better safe than sorry?
-
- PIDLIST_RELATIVE pidlFolder = nullptr;
- {
- std::wstring pathName = beforeLast(filenameStr, L'\\');
- ZEN_COM_CHECK(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
- }
- ZEN_COM_ASSERT(pidlFolder);
- ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree
-
- ComPtr<IShellFolder> imageFolder;
- ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl,
- nullptr, // [in] IBindCtx *pbc,
- IID_PPV_ARGS(imageFolder.init())));
- ZEN_COM_ASSERT(imageFolder);
-
- PIDLIST_RELATIVE pidImage = nullptr;
- {
- std::wstring shortName = afterLast(filenameStr, L'\\');
- ZEN_COM_CHECK(imageFolder->ParseDisplayName(nullptr, // [in] HWND hwnd,
- nullptr, // [in] IBindCtx *pbc,
- const_cast<LPWSTR>(shortName.c_str()), // [in] LPWSTR pszDisplayName,
- nullptr, // [out] ULONG *pchEaten,
- &pidImage, // [out] PIDLIST_RELATIVE *ppidl,
- nullptr)); // [in, out] ULONG *pdwAttributes
- }
- ZEN_COM_ASSERT(pidImage);
- ZEN_ON_SCOPE_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree
-
- ComPtr<IExtractImage> extractImage;
- ZEN_COM_CHECK(imageFolder->GetUIObjectOf(nullptr, // [in] HWND hwndOwner,
- 1, // [in] UINT cidl,
- reinterpret_cast<PCUITEMID_CHILD_ARRAY>(&pidImage), // [in] PCUITEMID_CHILD_ARRAY apidl,
- //this is where STRICT_TYPED_ITEMIDS gets us ;)
- IID_IExtractImage, // [in] REFIID riid,
- nullptr, // [in, out] UINT *rgfReserved,
- reinterpret_cast<void**>(extractImage.init()))); // [out] void **ppv
- ZEN_COM_ASSERT(extractImage);
-
- {
- wchar_t pathBuffer[MAX_PATH];
- DWORD priority = 0;
- const SIZE prgSize = { requestedSize, requestedSize };
- DWORD clrDepth = 32; //"recommended color depth"
- DWORD flags = IEIFLAG_SCREEN | IEIFLAG_OFFLINE;
-
- ZEN_COM_CHECK(extractImage->GetLocation(pathBuffer, // [out] LPWSTR pszPathBuffer,
- MAX_PATH, // [in] DWORD cchMax,
- &priority, // [out] DWORD *pdwPriority,
- &prgSize, // [in] const SIZE *prgSize,
- clrDepth, // [in] DWORD dwRecClrDepth,
- &flags)); // [in, out] DWORD *pdwFlags
- }
-
- HBITMAP bitmap = nullptr;
- ZEN_COM_CHECK(extractImage->Extract(&bitmap));
- ZEN_COM_ASSERT(bitmap);
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmap));
-
- return convertToImageData(bitmap); //throw SysError, pass ownership
-}
-
-
-const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup
-
-//caller takes ownership!
-const thumb::ImageData* getIconByIndex_impl(int iconIndex, thumb::IconSizeType st) //throw SysError
-{
- //Note:
- //- using IExtractIcon::Extract is *no* alternative, just as ::SHGetFileInfo(), it only supports small (16x16) and large (32x32) icons
- //- IShellItemImageFactory::GetImage requires Vista or later
-
- using namespace thumb;
- int requestedSize = 16;
- int shilIconType = SHIL_SMALL; //16x16, size can be customized by the user.
- {
- if (!wereVistaOrLater && //XP doesn't have jumbo icons
- (st == ICON_SIZE_128 ||
- st == ICON_SIZE_256))
- st = ICON_SIZE_48;
-
- switch (st)
- {
- case ICON_SIZE_16:
- break;
- case ICON_SIZE_32:
- requestedSize = 32;
- shilIconType = SHIL_LARGE; //32x32, may be 48x48 if "Use large icon" option is set in Display Properties
- break;
- case ICON_SIZE_48:
- requestedSize = 48;
- shilIconType = SHIL_EXTRALARGE; //48x48, size can be customized by the user.
- break;
- case ICON_SIZE_128:
- requestedSize = 128;
- shilIconType = SHIL_JUMBO; //256x256 pixels -> scale down!
- break;
- case ICON_SIZE_256:
- requestedSize = 256;
- shilIconType = SHIL_JUMBO; //256x256 pixels; Vista and later only
- break;
- }
- }
-
- ComPtr<IImageList> imageList; //perf: 0,12 µs only to get the image list
- ZEN_COM_CHECK(::SHGetImageList(shilIconType, //__in int iImageList,
- IID_PPV_ARGS(imageList.init())));
- ZEN_COM_ASSERT(imageList);
-
- int srcWidth = 0;
- int srcHeight = 0;
- ZEN_COM_CHECK(imageList->GetIconSize(&srcWidth, &srcHeight));
-
- int targetWidth = srcWidth;
- int targetHeight = srcHeight;
- bool needDownScale = false; //scale down if required (e.g Vista Jumbo icons/user-customized icon sizes)
-
- ZEN_COM_ASSERT(srcWidth > 0 && srcHeight > 0 && requestedSize > 0);
-
- const int maxExtent = std::max(srcWidth, srcHeight);
- if (requestedSize < maxExtent)
- {
- needDownScale = true;
- targetWidth = srcWidth * requestedSize / maxExtent;
- targetHeight = srcHeight * requestedSize / maxExtent;
- }
-
- HDC hScreenDC = ::GetDC(nullptr);
- ZEN_COM_ASSERT(hScreenDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC));
-
- auto createRGBDib = [&](unsigned char** rawBits) -> HBITMAP
- {
- BITMAPINFO bi = {};
- bi.bmiHeader.biSize = sizeof(bi);
- bi.bmiHeader.biWidth = targetWidth;
- bi.bmiHeader.biHeight = -targetHeight; //negative for top left origin
- bi.bmiHeader.biPlanes = 1;
- bi.bmiHeader.biBitCount = 24; //we don't want an alpha channel
- bi.bmiHeader.biCompression = BI_RGB;
-
- return ::CreateDIBSection(hScreenDC, //_In_ HDC hdc,
- &bi, //_In_ const BITMAPINFO *pbmi,
- DIB_RGB_COLORS, //_In_ UINT iUsage,
- reinterpret_cast<VOID**>(rawBits), //_Out_ VOID **ppvBits,
- nullptr, //_In_ HANDLE hSection,
- 0); //_In_ DWORD dwOffset
- };
-
- unsigned char* bitsBlackBg = nullptr;
- HBITMAP bmpBlackBg = createRGBDib(&bitsBlackBg);
- ZEN_COM_ASSERT(bmpBlackBg); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpBlackBg););
- ZEN_COM_ASSERT(bitsBlackBg); //check after bmpBlackBg is owned by us
-
- HDC memDC = ::CreateCompatibleDC(hScreenDC);
- ZEN_COM_ASSERT(memDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteDC(memDC));
-
- HGDIOBJ hgdiOld = ::SelectObject(memDC, bmpBlackBg);
- ZEN_COM_ASSERT(hgdiOld); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld));
-
- IMAGELISTDRAWPARAMS drawParams = {};
- drawParams.cbSize = sizeof(drawParams);
- drawParams.hdcDst = memDC;
- drawParams.i = iconIndex;
- drawParams.rgbBk = 0x000000; //black
- drawParams.fStyle = ILD_NORMAL;
- //other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx
-
- if (needDownScale)
- {
- drawParams.fStyle |= ILD_SCALE;
- drawParams.cx = targetWidth;
- drawParams.cy = targetHeight;
- }
-
- //IDO_SHGIOI_LINK does not draw properly in some cases:
- //Win7: draws link overlay *twice* if SHIL_JUMBO is requested, but icon does not have this size
- //XP: drawing IDO_SHGIOI_LINK generally draws corrupted icons and links
- //if (addShortcutOverlay)
- //{
- // int linkOverlay = ::SHGetIconOverlayIndex(nullptr, IDO_SHGIOI_LINK); //-1 on error
- // if (linkOverlay != -1)
- // {
- // //int imgIndex = 0;
- // //if (SUCCEEDED(imageList->GetOverlayImage(linkOverlay, &imgIndex)))
- // //{
- // // drawParams.i = imgIndex;
- // // ZEN_COM_CHECK(imageList->Draw(&drawParams));
- // //}
-
- // drawParams.fStyle |= INDEXTOOVERLAYMASK(linkOverlay);
- // }
- //}
-
- ZEN_COM_CHECK(imageList->Draw(&drawParams));
-
- //-----------------------------------------------
- //we draw the icon twice on different backgrounds to extract the alpha channel:
- //- IImageList::GetIcon doesn't properly render SHIL_JUMBO for icons that don't have jumbo sizes, but IImageList::Draw does!
- //- minor: each HICON consumes 3 GDI handles
- //- IImageList::Draw does not reliably support alpha channel on device context (Windows XP)
- //- wxBitmap created from HBITMAP is very unreliable; often drawn incorrectly by wxDC::DrawBitmap => support wxImage instead
-
- unsigned char* bitsWhiteBg = nullptr;
- HBITMAP bmpWhiteBg = createRGBDib(&bitsWhiteBg);
- ZEN_COM_ASSERT(bmpWhiteBg); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpWhiteBg));
- ZEN_COM_ASSERT(bitsWhiteBg); //check after bmpWhiteBg is owned by us
-
- HGDIOBJ hgdiOld2 = ::SelectObject(memDC, bmpWhiteBg);
- ZEN_COM_ASSERT(hgdiOld2); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld2));
-
- drawParams.rgbBk = 0xFFFFFF; //white
-
- ZEN_COM_CHECK(imageList->Draw(&drawParams));
-
- //#####################################################################################
-
- //"Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values."
- /*bool rv = */
- ::GdiFlush();
-
- ImageData* imgOut = allocImageData(targetWidth, targetHeight); //throw SysError
- ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); });
-
- unsigned char* rgbPtr = imgOut->rgb;
- unsigned char* alphaPtr = imgOut->alpha;
-
- for (int i = 0; i < targetWidth * targetHeight; ++i)
- {
- unsigned char b_black = *bitsBlackBg++;
- unsigned char g_black = *bitsBlackBg++;
- unsigned char r_black = *bitsBlackBg++;
-
- unsigned char b_white = *bitsWhiteBg++;
- unsigned char g_white = *bitsWhiteBg++;
- unsigned char r_white = *bitsWhiteBg++;
-
- const int tmp = 255 + r_black - r_white + //mixed mode arithmetics!
- 255 + g_black - g_white +
- 255 + b_black - b_white;
- unsigned char alpha = static_cast<unsigned char>(numeric::confineCpy(tmp / 3, 0, 255));
-
- auto calcColor = [&](unsigned char c_black, unsigned char c_white)
- {
- return static_cast<unsigned char>(tmp == 0 ? 0 : numeric::confineCpy
- (255 * (3 * (-255 + c_white + c_black) + tmp) / (2 * tmp), //mixed mode arithmetics!
- 0, 255));
- };
-
- *rgbPtr++ = calcColor(r_black, r_white);
- *rgbPtr++ = calcColor(g_black, g_white);
- *rgbPtr++ = calcColor(b_black, b_white);
- *alphaPtr++ = alpha;
- }
-
- guardImgData.dismiss();
- return imgOut;
-}
-}
-
-
-const thumb::ImageData* thumb::getThumbnail(const wchar_t* filename, int requestedSize) //return 0 on failure, caller takes ownership!
-{
- try
- {
- return getThumbnail_impl(filename, requestedSize); //throw SysError
- }
- catch (const SysError&)
- {
- return nullptr;
- }
-}
-
-
-const thumb::ImageData* thumb::getIconByIndex(int iconIndex, thumb::IconSizeType st) //return 0 on failure, caller takes ownership!
-{
- try
- {
- return getIconByIndex_impl(iconIndex, st); //throw SysError
- }
- catch (const SysError&)
- {
- return nullptr;
- }
-}
-
-
-void thumb::releaseImageData(const thumb::ImageData* id)
-{
- releaseImageData_impl(id);
-}
diff --git a/lib/Thumbnail/thumbnail.h b/lib/Thumbnail/thumbnail.h
deleted file mode 100644
index 307fc7cc..00000000
--- a/lib/Thumbnail/thumbnail.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef THUMBNAIL_DLL_HEADER_487108471324
-#define THUMBNAIL_DLL_HEADER_487108471324
-
-#ifdef THUMBNAIL_DLL_EXPORTS
-#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport)
-#else
-#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport)
-#endif
-
-#include <zen/build_info.h>
-//#include <WinDef.h>
-
-namespace thumb
-{
-/*
-PREREQUISITES:
-
-1. COM must be initialized for the current thread via ::CoInitialize(nullptr) or ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED),
- but NOT ::CoInitializeEx(nullptr, COINIT_MULTITHREADED) -> internal access violation crash!
-2. call ::FileIconInit() on app start to remedy obscure errors like SHELL_E_WRONG_BITDEPTH (0x80270102)
- for certain file types, e.g. lnk, mpg - required on Windows 7 see http://msdn.microsoft.com/en-us/library/ms683212(v=VS.85).aspx
-*/
-
-/*--------------
- |declarations|
- --------------*/
-struct ImageData //consider alignment!
-{
- unsigned char* rgb; //rgb-byte order for use with wxImage
- unsigned char* alpha;
- int width;
- int height;
-};
-
-DLL_FUNCTION_DECLARATION
-const ImageData* getThumbnail(const wchar_t* filename, int requestedSize); //return nullptr on failure, release after use
-//Note: not all file types support thumbnails! implement fallback to file icon!
-
-enum IconSizeType
-{
- ICON_SIZE_16,
- ICON_SIZE_32,
- ICON_SIZE_48,
- ICON_SIZE_128,
- ICON_SIZE_256,
-};
-//"iconIndex" as returned by ::SHGetFileInfo()
-DLL_FUNCTION_DECLARATION
-const ImageData* getIconByIndex(int iconIndex, IconSizeType st); //return nullptr on failure, release after use
-
-DLL_FUNCTION_DECLARATION
-void releaseImageData(const ImageData* id);
-
-
-/*----------
- |typedefs|
- ----------*/
-typedef const ImageData* (*FunType_getThumbnail )(const wchar_t* filename, int requestedSize);
-typedef const ImageData* (*FunType_getIconByIndex )(int iconIndex, IconSizeType st);
-typedef void (*FunType_releaseImageData)(const ImageData* id);
-
-
-/*--------------
- |symbol names|
- --------------*/
-//(use const pointers to ensure internal linkage)
-const char funName_getThumbnail [] = "getThumbnail";
-const char funName_getIconByIndex [] = "getIconByIndex";
-const char funName_releaseImageData[] = "releaseImageData";
-
-/*---------------
- |library names|
- ---------------*/
-inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"Thumbnail_x64.dll" : L"Thumbnail_Win32.dll"; }
-}
-
-#undef DLL_FUNCTION_DECLARATION
-
-#endif //THUMBNAIL_DLL_HEADER_487108471324
diff --git a/lib/binary.cpp b/lib/binary.cpp
deleted file mode 100644
index 0e41f7a6..00000000
--- a/lib/binary.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "binary.h"
-#include <zen/tick_count.h>
-#include <vector>
-#include <zen/file_io.h>
-#include <zen/int64.h>
-#include <boost/thread/tss.hpp>
-
-using namespace zen;
-
-
-namespace
-{
-inline
-void setMinSize(std::vector<char>& buffer, size_t minSize)
-{
- if (buffer.size() < minSize) //this is similar to reserve(), but we need a "properly initialized" array here
- buffer.resize(minSize);
-}
-
-class BufferSize
-{
-public:
- BufferSize() : bufSize(BUFFER_SIZE_START) {}
-
- void inc()
- {
- if (bufSize < BUFFER_SIZE_MAX)
- bufSize *= 2;
- }
-
- void dec()
- {
- if (bufSize > BUFFER_SIZE_MIN)
- bufSize /= 2;
- }
-
- operator size_t() const { return bufSize; }
-
-private:
- static const size_t BUFFER_SIZE_MIN = 64 * 1024;
- static const size_t BUFFER_SIZE_START = 128 * 1024; //initial buffer size
- static const size_t BUFFER_SIZE_MAX = 16 * 1024 * 1024;
-
- /*Tests on Win7 x64 show that buffer size does NOT matter if files are located on different physical disks!
- Impact of buffer size when files are on same disk:
-
- buffer MB/s
- ------------
- 64 10
- 128 19
- 512 40
- 1024 48
- 2048 56
- 4096 56
- 8192 56
- */
-
- size_t bufSize;
-};
-
-
-const std::int64_t TICKS_PER_SEC = ticksPerSec();
-}
-
-
-bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback)
-{
- static boost::thread_specific_ptr<std::vector<char>> cpyBuf1;
- static boost::thread_specific_ptr<std::vector<char>> cpyBuf2;
- if (!cpyBuf1.get())
- cpyBuf1.reset(new std::vector<char>());
- if (!cpyBuf2.get())
- cpyBuf2.reset(new std::vector<char>());
-
- std::vector<char>& memory1 = *cpyBuf1;
- std::vector<char>& memory2 = *cpyBuf2;
-
- FileInput file1(filename1); //throw FileError
- FileInput file2(filename2); //
-
- BufferSize bufferSize;
-
- TickVal lastDelayViolation = getTicks();
-
- do
- {
- setMinSize(memory1, bufferSize);
- setMinSize(memory2, bufferSize);
-
- const TickVal startTime = getTicks();
-
- const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError
- const size_t length2 = file2.read(&memory2[0], bufferSize); //returns actual number of bytes read
- //send progress updates immediately after reading to reliably allow speed calculations for our clients!
- callback.updateCompareStatus(to<Int64>(std::max(length1, length2)));
-
- if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0)
- return false;
-
- //-------- dynamically set buffer size to keep callback interval between 100 - 500ms ---------------------
- if (TICKS_PER_SEC > 0)
- {
- const TickVal now = getTicks();
-
- const std::int64_t loopTime = dist(startTime, now) * 1000 / TICKS_PER_SEC; //unit: [ms]
- if (loopTime < 100)
- {
- if (dist(lastDelayViolation, now) / TICKS_PER_SEC > 2) //avoid "flipping back": e.g. DVD-Roms read 32MB at once, so first read may be > 500 ms, but second one will be 0ms!
- {
- lastDelayViolation = now;
- bufferSize.inc();
- }
- }
- else if (loopTime > 500)
- {
- lastDelayViolation = now;
- bufferSize.dec();
- }
- }
- //------------------------------------------------------------------------------------------------
- }
- while (!file1.eof());
-
- 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
deleted file mode 100644
index 8a4abe6b..00000000
--- a/lib/binary.h
+++ /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 (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef BINARY_H_INCLUDED
-#define BINARY_H_INCLUDED
-
-#include <zen/zstring.h>
-#include <zen/file_error.h>
-#include <zen/int64.h>
-
-namespace zen
-{
-struct CompareCallback
-{
- virtual ~CompareCallback() {}
- virtual void updateCompareStatus(Int64 bytesDelta) = 0;
-};
-
-bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError
-}
-
-#endif // BINARY_H_INCLUDED
diff --git a/lib/cmp_filetime.h b/lib/cmp_filetime.h
deleted file mode 100644
index 4e75675b..00000000
--- a/lib/cmp_filetime.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef CMP_FILETIME_H_INCLUDED
-#define CMP_FILETIME_H_INCLUDED
-
-#include <wx/stopwatch.h>
-#include <zen/int64.h>
-
-namespace zen
-{
-//---------------------------------------------------------------------------------------------------------------
-inline
-bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance)
-{
- if (a < b)
- return b <= a + static_cast<ptrdiff_t>(tolerance);
- else
- return a <= b + static_cast<ptrdiff_t>(tolerance);
-}
-//---------------------------------------------------------------------------------------------------------------
-
-//number of seconds since Jan 1st 1970 + 1 year (needn't be too precise)
-static const long oneYearFromNow = wxGetUTCTime() + 365 * 24 * 3600; //init at program startup alas in *each* compilation untit -> avoid MT issues
-//refactor when C++11 thread-safe static initialization is availalbe in VS (already in GCC)
-
-class CmpFileTime
-{
-public:
- enum Result
- {
- TIME_EQUAL,
- TIME_LEFT_NEWER,
- TIME_RIGHT_NEWER,
- TIME_LEFT_INVALID,
- TIME_RIGHT_INVALID
- };
-
- static Result getResult(const Int64& lhs, const Int64& rhs, size_t tolerance)
- {
- if (sameFileTime(lhs, rhs, tolerance)) //last write time may differ by up to 2 seconds (NTFS vs FAT32)
- return TIME_EQUAL;
-
- //check for erroneous dates
- if (lhs < 0 || lhs > oneYearFromNow) //earlier than Jan 1st 1970 or more than one year in future
- return TIME_LEFT_INVALID;
-
- if (rhs < 0 || rhs > oneYearFromNow)
- return TIME_RIGHT_INVALID;
-
- //regular time comparison
- if (lhs < rhs)
- return TIME_RIGHT_NEWER;
- else
- return TIME_LEFT_NEWER;
- }
-};
-}
-
-#endif // CMP_FILETIME_H_INCLUDED
diff --git a/lib/db_file.cpp b/lib/db_file.cpp
deleted file mode 100644
index 1c2a34f3..00000000
--- a/lib/db_file.cpp
+++ /dev/null
@@ -1,821 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "db_file.h"
-#include <zen/file_handling.h>
-#include <zen/scope_guard.h>
-#include <zen/guid.h>
-#include <zen/utf.h>
-#include <zen/serialize.h>
-#include <wx+/zlib_wrap.h>
-
-#ifdef ZEN_WIN
-#include <zen/win.h> //includes "windows.h"
-#include <zen/long_path_prefix.h>
-#endif
-
-using namespace zen;
-
-
-namespace
-{
-//-------------------------------------------------------------------------------------------------------------------------------
-const char FILE_FORMAT_DESCR[] = "FreeFileSync";
-const int DB_FORMAT_CONTAINER = 9;
-const int DB_FORMAT_STREAM = 1;
-//-------------------------------------------------------------------------------------------------------------------------------
-
-typedef std::string UniqueId;
-typedef std::map<UniqueId, BinaryStream> DbStreams; //list of streams ordered by session UUID
-
-//-----------------------------------------------------------------------------------
-//| ensure 32/64 bit portability: use fixed size data types only e.g. std::uint32_t |
-//-----------------------------------------------------------------------------------
-
-template <SelectedSide side> inline
-Zstring getDBFilename(const BaseDirPair& baseDirObj, bool tempfile = false)
-{
- //Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity? are UTC file times really compatible?
- //what about endianess!?
- //however 32 and 64 bit db files *are* designed to be binary compatible!
- //Give db files different names.
- //make sure they end with ".ffs_db". These files will be excluded from comparison
-#ifdef ZEN_WIN
- Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING;
-#elif defined ZEN_LINUX || defined ZEN_MAC
- //files beginning with dots are hidden e.g. in Nautilus
- Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING;
-#endif
- return baseDirObj.getBaseDirPf<side>() + dbname;
-}
-
-//#######################################################################################################################################
-
-void saveStreams(const DbStreams& streamList, const Zstring& filename) //throw FileError
-{
- BinStreamOut streamOut;
-
- //write FreeFileSync file identifier
- writeArray(streamOut, FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR));
-
- //save file format version
- writeNumber<std::int32_t>(streamOut, DB_FORMAT_CONTAINER);
-
- //save stream list
- writeNumber<std::uint32_t>(streamOut, static_cast<std::uint32_t>(streamList.size())); //number of streams, one for each sync-pair
-
- for (const auto& stream : streamList)
- {
- writeContainer<std::string >(streamOut, stream.first );
- writeContainer<BinaryStream>(streamOut, stream.second);
- }
-
- assert(!somethingExists(filename)); //orphan tmp files should be cleaned up already at this point!
- saveBinStream(filename, streamOut.get()); //throw FileError
-
-#ifdef ZEN_WIN
- //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp
- ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file
-#endif
-}
-
-
-DbStreams loadStreams(const Zstring& filename) //throw FileError, FileErrorDatabaseNotExisting
-{
- try
- {
- BinStreamIn streamIn = loadBinStream<BinaryStream>(filename); //throw FileError
-
- //read FreeFileSync file identifier
- char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {};
- readArray(streamIn, formatDescr, sizeof(formatDescr)); //throw UnexpectedEndOfStreamError
-
- if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr))
- throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename)));
-
- const int version = readNumber<std::int32_t>(streamIn); //throw UnexpectedEndOfStreamError
- if (version != DB_FORMAT_CONTAINER) //read file format version number
- throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename)));
-
- DbStreams output;
-
- //read stream lists
- size_t dbCount = readNumber<std::uint32_t>(streamIn); //number of streams, one for each sync-pair
- while (dbCount-- != 0)
- {
- //DB id of partner databases
- std::string sessionID = readContainer<std::string >(streamIn); //throw UnexpectedEndOfStreamError
- BinaryStream stream = readContainer<BinaryStream>(streamIn); //
-
- output[sessionID] = std::move(stream);
- }
- return output;
- }
- catch (FileError&)
- {
- if (!somethingExists(filename)) //a benign(?) race condition with FileError
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" +
- replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename)));
- throw;
- }
- catch (UnexpectedEndOfStreamError&)
- {
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename));
- }
- catch (const std::bad_alloc& e) //still required?
- {
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename),
- _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()));
- }
-}
-
-//#######################################################################################################################################
-
-class StreamGenerator //for db-file back-wards compatibility we stick with two output streams until further
-{
-public:
- static void execute(const InSyncDir& dir, //throw FileError
- const Zstring& filenameL, //used for diagnostics only
- const Zstring& filenameR,
- BinaryStream& streamL,
- BinaryStream& streamR)
- {
- StreamGenerator generator;
-
- //PERF_START
- generator.recurse(dir);
- //PERF_STOP
-
- auto compStream = [](const BinaryStream& stream, const Zstring& filename) -> BinaryStream //throw FileError
- {
- try
- {
- /* Zlib: optimal level - testcase 1 million files
- level/size [MB]/time [ms]
- 0 49.54 272 (uncompressed)
- 1 14.53 1013
- 2 14.13 1106
- 3 13.76 1288 - best compromise between speed and compression
- 4 13.20 1526
- 5 12.73 1916
- 6 12.58 2765
- 7 12.54 3633
- 8 12.51 9032
- 9 12.50 19698 (maximal compression) */
- return compress(stream, 3); //throw ZlibInternalError
- }
- catch (ZlibInternalError&)
- {
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)), L"zlib internal error");
- }
- };
-
- const BinaryStream tmpL = compStream(generator.outputLeft .get(), filenameL);
- const BinaryStream tmpR = compStream(generator.outputRight.get(), filenameR);
- const BinaryStream tmpB = compStream(generator.outputBoth .get(), filenameL + Zstr("/") + filenameR);
-
- BinStreamOut outL;
- BinStreamOut outR;
- //save format version
- writeNumber<std::int32_t>(outL, DB_FORMAT_STREAM);
- writeNumber<std::int32_t>(outR, DB_FORMAT_STREAM);
-
- //distribute "outputBoth" over left and right streams:
- writeNumber<std::int8_t>(outL, true); //this side contains first part of "outputBoth"
- writeNumber<std::int8_t>(outR, false);
-
- const size_t size1stPart = tmpB.size() / 2;
- const size_t size2ndPart = tmpB.size() - size1stPart;
-
- writeNumber<std::uint64_t>(outL, size1stPart);
- writeNumber<std::uint64_t>(outR, size2ndPart);
-
- writeArray(outL, &*tmpB.begin(), size1stPart);
- writeArray(outR, &*tmpB.begin() + size1stPart, size2ndPart);
-
- //write streams corresponding to one side only
- writeContainer<BinaryStream>(outL, tmpL);
- writeContainer<BinaryStream>(outR, tmpR);
-
- streamL = outL.get();
- streamR = outR.get();
- }
-
-private:
- void recurse(const InSyncDir& container)
- {
- writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.files.size()));
- for (const auto& dbFile : container.files)
- {
- writeUtf8(outputBoth, dbFile.first);
- writeNumber<std::int32_t>(outputBoth, dbFile.second.cmpVar);
- writeNumber<std::uint64_t>(outputBoth, to<std::uint64_t>(dbFile.second.fileSize));
-
- writeFile(outputLeft, dbFile.second.left);
- writeFile(outputRight, dbFile.second.right);
- }
-
- writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.symlinks.size()));
- for (const auto& dbSymlink : container.symlinks)
- {
- writeUtf8(outputBoth, dbSymlink.first);
- writeNumber<std::int32_t>(outputBoth, dbSymlink.second.cmpVar);
-
- writeLink(outputLeft, dbSymlink.second.left);
- writeLink(outputRight, dbSymlink.second.right);
- }
-
- writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.dirs.size()));
- for (const auto& dbDir : container.dirs)
- {
- writeUtf8(outputBoth, dbDir.first);
- writeNumber<std::int32_t>(outputBoth, dbDir.second.status);
-
- recurse(dbDir.second);
- }
- }
-
- static void writeUtf8(BinStreamOut& output, const Zstring& str) { writeContainer(output, utfCvrtTo<Zbase<char>>(str)); }
-
- static void writeFile(BinStreamOut& output, const InSyncDescrFile& descr)
- {
- writeNumber<std:: int64_t>(output, to<std:: int64_t>(descr.lastWriteTimeRaw));
- writeNumber<std::uint64_t>(output, descr.fileId.first);
- writeNumber<std::uint64_t>(output, descr.fileId.second);
- assert_static(sizeof(descr.fileId.first ) <= sizeof(std::uint64_t));
- assert_static(sizeof(descr.fileId.second) <= sizeof(std::uint64_t));
- }
-
- static void writeLink(BinStreamOut& output, const InSyncDescrLink& descr)
- {
- writeNumber<std::int64_t>(output, to<std:: int64_t>(descr.lastWriteTimeRaw));
- }
-
- BinStreamOut outputLeft; //data related to one side only
- BinStreamOut outputRight; //
- BinStreamOut outputBoth; //data concerning both sides
-};
-
-
-class StreamParser
-{
-public:
- static std::shared_ptr<InSyncDir> execute(const BinaryStream& streamL, //throw FileError
- const BinaryStream& streamR,
- const Zstring& filenameL, //used for diagnostics only
- const Zstring& filenameR)
- {
- auto decompStream = [](const BinaryStream& stream, const Zstring& filename) -> BinaryStream //throw FileError
- {
- try
- {
- return decompress(stream); //throw ZlibInternalError
- }
- catch (ZlibInternalError&)
- {
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)), L"zlib internal error");
- }
- };
-
- try
- {
- BinStreamIn inL(streamL);
- BinStreamIn inR(streamR);
-
- const int streamVersion = readNumber<std::int32_t>(inL); //throw UnexpectedEndOfStreamError
- warn_static("remove this case after migration:")
- bool migrateStreamFromOldFormat = streamVersion != DB_FORMAT_STREAM;
- if (migrateStreamFromOldFormat)
- inL = BinStreamIn(streamL);
- else
- {
- const int streamVersionRef = readNumber<std::int32_t>(inR); //throw UnexpectedEndOfStreamError
- if (streamVersionRef != streamVersion) //throw UnexpectedEndOfStreamError
- throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filenameR), L"stream format mismatch"));
- if (streamVersion != DB_FORMAT_STREAM)
- throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filenameL), L"stream format"));
- }
-
- bool has1stPartL = false;
- bool has1stPartR = false;
- if (migrateStreamFromOldFormat)
- {
- has1stPartL = readNumber<bool>(inL) != 0; //throw UnexpectedEndOfStreamError
- has1stPartR = readNumber<bool>(inR) != 0; //
- }
- else
- {
- has1stPartL = readNumber<std::int8_t>(inL) != 0; //throw UnexpectedEndOfStreamError
- has1stPartR = readNumber<std::int8_t>(inR) != 0; //
- }
-
- if (has1stPartL == has1stPartR)
- throw UnexpectedEndOfStreamError();
-
- BinStreamIn& in1stPart = has1stPartL ? inL : inR;
- BinStreamIn& in2ndPart = has1stPartL ? inR : inL;
-
- const size_t size1stPart = static_cast<size_t>(readNumber<std::uint64_t>(in1stPart));
- const size_t size2ndPart = static_cast<size_t>(readNumber<std::uint64_t>(in2ndPart));
-
- BinaryStream tmpB;
- tmpB.resize(size1stPart + size2ndPart); //throw bad_alloc
- readArray(in1stPart, &*tmpB.begin(), size1stPart);
- readArray(in2ndPart, &*tmpB.begin() + size1stPart, size2ndPart);
-
- const BinaryStream tmpL = readContainer<BinaryStream>(inL);
- const BinaryStream tmpR = readContainer<BinaryStream>(inR);
-
- auto output = std::make_shared<InSyncDir>(InSyncDir::DIR_STATUS_IN_SYNC);
- StreamParser parser(decompStream(tmpL, filenameL),
- decompStream(tmpR, filenameR),
- decompStream(tmpB, filenameL + Zstr("/") + filenameR), migrateStreamFromOldFormat);
- parser.recurse(*output); //throw UnexpectedEndOfStreamError
- return output;
- }
- catch (const UnexpectedEndOfStreamError&)
- {
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR));
- }
- catch (const std::bad_alloc& e)
- {
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR),
- _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()));
- }
- }
-
-private:
- StreamParser(const BinaryStream& bufferL,
- const BinaryStream& bufferR,
- const BinaryStream& bufferB,
- bool migrateStreamFromOldFormat) :
- migrateStreamFromOldFormat_(migrateStreamFromOldFormat),
- inputLeft (bufferL),
- inputRight(bufferR),
- inputBoth (bufferB) {}
-
- void recurse(InSyncDir& container)
- {
- size_t fileCount = readNumber<std::uint32_t>(inputBoth);
- while (fileCount-- != 0)
- {
- const Zstring shortName = readUtf8(inputBoth);
-
- if (migrateStreamFromOldFormat_)
- {
- const auto inSyncType = readNumber<std::int32_t>(inputBoth);
- const CompareVariant cmpVar = inSyncType == 0 ? CMP_BY_CONTENT : CMP_BY_TIME_SIZE;
-
- auto lastWriteTimeRawL = readNumber<std::int64_t>(inputLeft); //throw UnexpectedEndOfStreamError
- const UInt64 fileSize = readNumber<std::uint64_t>(inputLeft);
- auto devIdL = static_cast<DeviceId >(readNumber<std::uint64_t>(inputLeft)); //
- auto fileIdxL = static_cast<FileIndex>(readNumber<std::uint64_t>(inputLeft)); //silence "loss of precision" compiler warnings
- const InSyncDescrFile dataL = InSyncDescrFile(lastWriteTimeRawL, FileId(devIdL, fileIdxL));
-
- auto lastWriteTimeRaw = readNumber<std::int64_t>(inputRight); //throw UnexpectedEndOfStreamError
- readNumber<std::uint64_t>(inputRight);
- auto devId = static_cast<DeviceId >(readNumber<std::uint64_t>(inputRight)); //
- auto fileIdx = static_cast<FileIndex>(readNumber<std::uint64_t>(inputRight)); //silence "loss of precision" compiler warnings
- const InSyncDescrFile dataR = InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx));
-
- container.addFile(shortName, dataL, dataR, cmpVar, fileSize);
- }
- else
- {
- const auto cmpVar = static_cast<CompareVariant>(readNumber<std::int32_t>(inputBoth));
- const UInt64 fileSize = readNumber<std::uint64_t>(inputBoth);
- const InSyncDescrFile dataL = readFile(inputLeft);
- const InSyncDescrFile dataR = readFile(inputRight);
- container.addFile(shortName, dataL, dataR, cmpVar, fileSize);
- }
- }
-
- size_t linkCount = readNumber<std::uint32_t>(inputBoth);
- while (linkCount-- != 0)
- {
- const Zstring shortName = readUtf8(inputBoth);
-
- if (migrateStreamFromOldFormat_)
- {
- const CompareVariant cmpVar = CMP_BY_CONTENT;
- auto lastWriteTimeRaw = readNumber<std::int64_t>(inputLeft);
- readUtf8(inputLeft);//descr.targetPath = readUtf8(input);
- readNumber<std::int32_t>(inputLeft);//descr.type = static_cast<LinkDescriptor::LinkType>(readNumber<std::int32_t>(input));
- InSyncDescrLink dataL = InSyncDescrLink(lastWriteTimeRaw);
-
- auto lastWriteTimeRawR = readNumber<std::int64_t>(inputRight);
- readUtf8(inputRight);//descr.targetPath = readUtf8(input);
- readNumber<std::int32_t>(inputRight);//descr.type = static_cast<LinkDescriptor::LinkType>(readNumber<std::int32_t>(input));
- InSyncDescrLink dataR = InSyncDescrLink(lastWriteTimeRawR);
-
- container.addSymlink(shortName, dataL, dataR, cmpVar);
- }
- else
- {
- const auto cmpVar = static_cast<CompareVariant>(readNumber<std::int32_t>(inputBoth));
- InSyncDescrLink dataL = readLink(inputLeft);
- InSyncDescrLink dataR = readLink(inputRight);
- container.addSymlink(shortName, dataL, dataR, cmpVar);
- }
- }
-
- size_t dirCount = readNumber<std::uint32_t>(inputBoth);
- while (dirCount-- != 0)
- {
- const Zstring shortName = readUtf8(inputBoth);
- auto status = static_cast<InSyncDir::InSyncStatus>(readNumber<std::int32_t>(inputBoth));
-
- InSyncDir& subDir = container.addDir(shortName, status);
- recurse(subDir);
- }
- }
-
- static Zstring readUtf8(BinStreamIn& input) { return utfCvrtTo<Zstring>(readContainer<Zbase<char>>(input)); } //throw UnexpectedEndOfStreamError
-
- static InSyncDescrFile readFile(BinStreamIn& input)
- {
- //attention: order of function argument evaluation is undefined! So do it one after the other...
- auto lastWriteTimeRaw = readNumber<std::int64_t>(input); //throw UnexpectedEndOfStreamError
- auto devId = static_cast<DeviceId >(readNumber<std::uint64_t>(input)); //
- auto fileIdx = static_cast<FileIndex>(readNumber<std::uint64_t>(input)); //silence "loss of precision" compiler warnings
- return InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx));
- }
-
- static InSyncDescrLink readLink(BinStreamIn& input)
- {
- auto lastWriteTimeRaw = readNumber<std::int64_t>(input);
- return InSyncDescrLink(lastWriteTimeRaw);
- }
-
- warn_static("remove after migration")
- bool migrateStreamFromOldFormat_;
-
- BinStreamIn inputLeft; //data related to one side only
- BinStreamIn inputRight; //
- BinStreamIn inputBoth; //data concerning both sides
-};
-
-//#######################################################################################################################################
-
-class UpdateLastSynchronousState
-{
- /*
- 1. filter by file name does *not* create a new hierarchy, but merely gives a different *view* on the existing file hierarchy
- => only update database entries matching this view!
- 2. Symlink handling *does* create a new (asymmetric) hierarchy during comparison
- => update all database entries!
- */
-public:
- static void execute(const BaseDirPair& baseDirObj, InSyncDir& dir)
- {
- UpdateLastSynchronousState updater(baseDirObj.getCompVariant(), baseDirObj.getFilter());
- updater.recurse(baseDirObj, dir);
- }
-
-private:
- UpdateLastSynchronousState(CompareVariant activeCmpVar, const HardFilter& filter) :
- filter_(filter),
- activeCmpVar_(activeCmpVar) {}
-
- void recurse(const HierarchyObject& hierObj, InSyncDir& dir)
- {
- process(hierObj.refSubFiles(), hierObj.getObjRelativeNamePf(), dir.files);
- process(hierObj.refSubLinks(), hierObj.getObjRelativeNamePf(), dir.symlinks);
- process(hierObj.refSubDirs (), hierObj.getObjRelativeNamePf(), dir.dirs);
- }
-
- template <class M, class V>
- static V& updateItem(M& map, const Zstring& key, const V& value)
- {
- auto rv = map.insert(typename M::value_type(key, value));
- if (!rv.second)
- {
-#if defined ZEN_WIN || defined ZEN_MAC //caveat: key must be updated, if there is a change in short name case!!!
- if (rv.first->first != key)
- {
- map.erase(rv.first);
- return map.insert(typename M::value_type(key, value)).first->second;
- }
-#endif
- rv.first->second = value;
- }
- return rv.first->second;
-
- //www.cplusplus.com claims that hint position for map<>::insert(iterator position, const value_type& val) changed with C++11 -> standard is unclear in [map.modifiers]
- // => let's use the more generic and potentially less performant version above!
-
- /*
- //efficient create or update without "default-constructible" requirement (Effective STL, item 24)
-
- //first check if key already exists (if yes, we're saving a value construction/destruction compared to std::map<>::insert
- auto it = map.lower_bound(key);
- if (it != map.end() && !(map.key_comp()(key, it->first)))
- {
- #if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!!
- if (it->first != key)
- {
- map.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly
- return map.insert(typename M::value_type(key, value)).first->second;
- }
- #endif
- it->second = value;
- return it->second;
- }
- return map.insert(it, typename M::value_type(key, value))->second;
- */
- }
-
- void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelativeNamePf, InSyncDir::FileList& dbFiles)
- {
- hash_set<const InSyncFile*> toPreserve; //referencing fixed-in-memory std::map elements
- for (const FilePair& fileObj : currentFiles)
- if (!fileObj.isEmpty())
- {
- if (fileObj.getCategory() == FILE_EQUAL) //data in sync: write current state
- {
- //Caveat: If FILE_EQUAL, we *implicitly* assume equal left and right short names matching case: InSyncDir's mapping tables use short name as a key!
- //This makes us silently dependent from code in algorithm.h!!!
- assert(fileObj.getShortName<LEFT_SIDE>() == fileObj.getShortName<RIGHT_SIDE>());
- //this should be taken for granted:
- assert(fileObj.getFileSize<LEFT_SIDE>() == fileObj.getFileSize<RIGHT_SIDE>());
-
- //create or update new "in-sync" state
- InSyncFile& file = updateItem(dbFiles, fileObj.getObjShortName(),
- InSyncFile(InSyncDescrFile(fileObj.getLastWriteTime<LEFT_SIDE>(),
- fileObj.getFileId <LEFT_SIDE>()),
- InSyncDescrFile(fileObj.getLastWriteTime<RIGHT_SIDE>(),
- fileObj.getFileId <RIGHT_SIDE>()),
- activeCmpVar_,
- fileObj.getFileSize<LEFT_SIDE>()));
- toPreserve.insert(&file);
- }
- else //not in sync: preserve last synchronous state
- {
- auto it = dbFiles.find(fileObj.getObjShortName());
- if (it != dbFiles.end())
- toPreserve.insert(&it->second);
- }
- }
-
- warn_static("consider temporarily excluded items due to traveral error just like a fixed file filter here!?")
- //delete removed items (= "in-sync") from database
- map_remove_if(dbFiles, [&](const InSyncDir::FileList::value_type& v) -> bool
- {
- if (toPreserve.find(&v.second) != toPreserve.end())
- return false;
- //all items not existing in "currentFiles" have either been deleted meanwhile or been excluded via filter:
- const Zstring& shortName = v.first;
- return filter_.passFileFilter(parentRelativeNamePf + shortName);
- });
- }
-
- void process(const HierarchyObject::SubLinkVec& currentLinks, const Zstring& parentRelativeNamePf, InSyncDir::LinkList& dbLinks)
- {
- hash_set<const InSyncSymlink*> toPreserve;
- for (const SymlinkPair& linkObj : currentLinks)
- if (!linkObj.isEmpty())
- {
- if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state
- {
- assert(linkObj.getShortName<LEFT_SIDE>() == linkObj.getShortName<RIGHT_SIDE>());
-
- //create or update new "in-sync" state
- InSyncSymlink& link = updateItem(dbLinks, linkObj.getObjShortName(),
- InSyncSymlink(InSyncDescrLink(linkObj.getLastWriteTime<LEFT_SIDE>()),
- InSyncDescrLink(linkObj.getLastWriteTime<RIGHT_SIDE>()),
- activeCmpVar_));
- toPreserve.insert(&link);
- }
- else //not in sync: preserve last synchronous state
- {
- auto it = dbLinks.find(linkObj.getObjShortName());
- if (it != dbLinks.end())
- toPreserve.insert(&it->second);
- }
- }
-
- //delete removed items (= "in-sync") from database
- map_remove_if(dbLinks, [&](const InSyncDir::LinkList::value_type& v) -> bool
- {
- if (toPreserve.find(&v.second) != toPreserve.end())
- return false;
- //all items not existing in "currentLinks" have either been deleted meanwhile or been excluded via filter:
- const Zstring& shortName = v.first;
- return filter_.passFileFilter(parentRelativeNamePf + shortName);
- });
- }
-
- void process(const HierarchyObject::SubDirVec& currentDirs, const Zstring& parentRelativeNamePf, InSyncDir::DirList& dbDirs)
- {
- hash_set<const InSyncDir*> toPreserve;
- for (const DirPair& dirObj : currentDirs)
- if (!dirObj.isEmpty())
- switch (dirObj.getDirCategory())
- {
- case DIR_EQUAL:
- {
- assert(dirObj.getShortName<LEFT_SIDE>() == dirObj.getShortName<RIGHT_SIDE>());
-
- //update directory entry only (shallow), but do *not touch* exising child elements!!!
- const Zstring& key = dirObj.getObjShortName();
- auto insertResult = dbDirs.insert(std::make_pair(key, InSyncDir(InSyncDir::DIR_STATUS_IN_SYNC))); //get or create
- auto it = insertResult.first;
-
-#if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!!
- const bool alreadyExisting = !insertResult.second;
- if (alreadyExisting && it->first != key)
- {
- auto oldValue = std::move(it->second);
- dbDirs.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly
- it = dbDirs.insert(InSyncDir::DirList::value_type(key, std::move(oldValue))).first;
- }
-#endif
- InSyncDir& dir = it->second;
- dir.status = InSyncDir::DIR_STATUS_IN_SYNC; //update immediate directory entry
- toPreserve.insert(&dir);
- recurse(dirObj, dir);
- }
- break;
-
- case DIR_DIFFERENT_METADATA:
- //if DIR_DIFFERENT_METADATA and no old database entry yet: we have to insert a new (bogus) database entry:
- //we cannot simply skip the whole directory, since sub-items might be in sync!
- //Example: directories on left and right differ in case while sub-files are equal
- {
- //reuse last "in-sync" if available or insert strawman entry (do not try to update thereby removing child elements!!!)
- InSyncDir& dir = dbDirs.insert(std::make_pair(dirObj.getObjShortName(), InSyncDir(InSyncDir::DIR_STATUS_STRAW_MAN))).first->second;
- toPreserve.insert(&dir);
- recurse(dirObj, dir);
- }
- break;
-
- //not in sync: reuse last synchronous state:
- case DIR_LEFT_SIDE_ONLY:
- case DIR_RIGHT_SIDE_ONLY:
- {
- auto it = dbDirs.find(dirObj.getObjShortName());
- if (it != dbDirs.end())
- {
- toPreserve.insert(&it->second);
- recurse(dirObj, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!!
- }
- }
- break;
- }
-
- //delete removed items (= "in-sync") from database
- map_remove_if(dbDirs, [&](const InSyncDir::DirList::value_type& v) -> bool
- {
- if (toPreserve.find(&v.second) != toPreserve.end())
- return false;
- const Zstring& shortName = v.first;
- return filter_.passDirFilter(parentRelativeNamePf + shortName, nullptr);
- //if directory is not included in "currentDirs", it is either not existing anymore, in which case it should be deleted from database
- //or it was excluded via filter, in which case the database entry should be preserved:
- //=> all child db elements are also preserved since they are not recursed in the loop above!!!
- //=> no problem with filter logic of excluding complete directory subtrees, if top folder is excluded directly!
- });
- }
-
- const HardFilter& filter_; //filter used while scanning directory: generates view on actual files!
- const CompareVariant activeCmpVar_;
-};
-}
-
-//#######################################################################################################################################
-
-std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError, FileErrorDatabaseNotExisting -> return value always bound!
-{
- const Zstring fileNameLeft = getDBFilename<LEFT_SIDE >(baseDirObj);
- const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseDirObj);
-
- if (!baseDirObj.isExisting<LEFT_SIDE >() ||
- !baseDirObj.isExisting<RIGHT_SIDE>())
- {
- //avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts!
- //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430
- const Zstring filename = !baseDirObj.isExisting<LEFT_SIDE>() ? fileNameLeft : fileNameRight;
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + //it could be due to a to-be-created target directory not yet existing => FileErrorDatabaseNotExisting
- replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename)));
- }
-
- //read file data: list of session ID + DirInfo-stream
- const DbStreams streamsLeft = ::loadStreams(fileNameLeft); //throw FileError, FileErrorDatabaseNotExisting
- const DbStreams streamsRight = ::loadStreams(fileNameRight); //
-
- //find associated session: there can be at most one session within intersection of left and right ids
- for (const auto& streamLeft : streamsLeft)
- {
- auto itRight = streamsRight.find(streamLeft.first);
- if (itRight != streamsRight.end())
- {
- return StreamParser::execute(streamLeft.second, //throw FileError
- itRight->second,
- fileNameLeft,
- fileNameRight);
- }
- }
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" +
- _("Database files do not share a common session."));
-}
-
-
-void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError
-{
- //transactional behaviour! write to tmp files first
- const Zstring dbNameLeftTmp = getDBFilename<LEFT_SIDE >(baseDirObj, true);
- const Zstring dbNameRightTmp = getDBFilename<RIGHT_SIDE>(baseDirObj, true);
-
- const Zstring dbNameLeft = getDBFilename<LEFT_SIDE >(baseDirObj);
- const Zstring dbNameRight = getDBFilename<RIGHT_SIDE>(baseDirObj);
-
- //delete old tmp file, if necessary -> throws if deletion fails!
- removeFile(dbNameLeftTmp); //
- removeFile(dbNameRightTmp); //throw FileError
-
- //(try to) load old database files...
- DbStreams streamsLeft; //list of session ID + DirInfo-stream
- DbStreams streamsRight;
-
- try { streamsLeft = ::loadStreams(dbNameLeft ); }
- catch (FileError&) {}
- try { streamsRight = ::loadStreams(dbNameRight); }
- catch (FileError&) {}
- //if error occurs: just overwrite old file! User is already informed about issues right after comparing!
-
- //find associated session: there can be at most one session within intersection of left and right ids
- auto itStreamLeftOld = streamsLeft .cend();
- auto itStreamRightOld = streamsRight.cend();
- for (auto iterLeft = streamsLeft.begin(); iterLeft != streamsLeft.end(); ++iterLeft)
- {
- auto iterRight = streamsRight.find(iterLeft->first);
- if (iterRight != streamsRight.end())
- {
- itStreamLeftOld = iterLeft;
- itStreamRightOld = iterRight;
- break;
- }
- }
-
- //load last synchrounous state
- std::shared_ptr<InSyncDir> lastSyncState = std::make_shared<InSyncDir>(InSyncDir::DIR_STATUS_IN_SYNC);
- if (itStreamLeftOld != streamsLeft .end() &&
- itStreamRightOld != streamsRight.end())
- try
- {
- lastSyncState = StreamParser::execute(itStreamLeftOld ->second, //throw FileError
- itStreamRightOld->second,
- dbNameLeft,
- dbNameRight);
- }
- catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing!
-
- //update last synchrounous state
- UpdateLastSynchronousState::execute(baseDirObj, *lastSyncState);
-
- //serialize again
- BinaryStream updatedStreamLeft;
- BinaryStream updatedStreamRight;
- StreamGenerator::execute(*lastSyncState,
- dbNameLeft,
- dbNameRight,
- updatedStreamLeft,
- updatedStreamRight); //throw FileError
-
- //check if there is some work to do at all
- if (itStreamLeftOld != streamsLeft .end() && updatedStreamLeft == itStreamLeftOld ->second &&
- itStreamRightOld != streamsRight.end() && updatedStreamRight == itStreamRightOld->second)
- return; //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed
-
- //erase old session data
- if (itStreamLeftOld != streamsLeft.end())
- streamsLeft.erase(itStreamLeftOld);
- if (itStreamRightOld != streamsRight.end())
- streamsRight.erase(itStreamRightOld);
-
- //create new session data
- const std::string sessionID = zen::generateGUID();
-
- streamsLeft [sessionID] = std::move(updatedStreamLeft);
- streamsRight[sessionID] = std::move(updatedStreamRight);
-
- //write (temp-) files...
- zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); });
- saveStreams(streamsLeft, dbNameLeftTmp); //throw FileError
-
- zen::ScopeGuard guardTempFileRight = zen::makeGuard([&] {zen::removeFile(dbNameRightTmp); });
- saveStreams(streamsRight, dbNameRightTmp); //throw FileError
-
- //operation finished: rename temp files -> this should work transactionally:
- //if there were no write access, creation of temp files would have failed
- removeFile(dbNameLeft); //
- removeFile(dbNameRight); //throw FileError
- renameFile(dbNameLeftTmp, dbNameLeft); //
- renameFile(dbNameRightTmp, dbNameRight); //
-
- guardTempFileLeft. dismiss(); //no need to delete temp files anymore
- guardTempFileRight.dismiss(); //
-}
diff --git a/lib/db_file.h b/lib/db_file.h
deleted file mode 100644
index c432704d..00000000
--- a/lib/db_file.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef DBFILE_H_834275398588021574
-#define DBFILE_H_834275398588021574
-
-#include <zen/file_error.h>
-#include "../file_hierarchy.h"
-
-namespace zen
-{
-const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db");
-
-struct InSyncDescrFile //subset of FileDescriptor
-{
- InSyncDescrFile(const Int64& lastWriteTimeRawIn,
- const FileId& idIn) :
- lastWriteTimeRaw(lastWriteTimeRawIn),
- fileId(idIn) {}
-
- Int64 lastWriteTimeRaw;
- FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows)
-};
-
-struct InSyncDescrLink
-{
- explicit InSyncDescrLink(const Int64& lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {}
- Int64 lastWriteTimeRaw;
-};
-
-
-//artificial hierarchy of last synchronous state:
-struct InSyncFile
-{
- InSyncFile(const InSyncDescrFile& l, const InSyncDescrFile& r, CompareVariant cv, const UInt64& fileSizeIn) : left(l), right(r), cmpVar(cv), fileSize(fileSizeIn) {}
- InSyncDescrFile left;
- InSyncDescrFile right;
- CompareVariant cmpVar; //the one active while finding "file in sync"
- UInt64 fileSize; //file size must be identical on both sides!
-};
-
-struct InSyncSymlink
-{
- InSyncSymlink(const InSyncDescrLink& l, const InSyncDescrLink& r, CompareVariant cv) : left(l), right(r), cmpVar(cv) {}
- InSyncDescrLink left;
- InSyncDescrLink right;
- CompareVariant cmpVar;
-};
-
-struct InSyncDir
-{
- //for directories we have a logical problem: we cannot have "not existent" as an indicator for
- //"no last synchronous state" since this precludes child elements that may be in sync!
- enum InSyncStatus
- {
- DIR_STATUS_IN_SYNC,
- DIR_STATUS_STRAW_MAN //there is no last synchronous state, but used as container only
- };
- InSyncDir(InSyncStatus statusIn) : status(statusIn) {}
-
- InSyncStatus status;
-
- //------------------------------------------------------------------
- typedef std::map<Zstring, InSyncDir, LessFilename> DirList; //
- typedef std::map<Zstring, InSyncFile, LessFilename> FileList; // key: shortName
- typedef std::map<Zstring, InSyncSymlink, LessFilename> LinkList; //
- //------------------------------------------------------------------
-
- DirList dirs;
- FileList files;
- LinkList symlinks; //non-followed symlinks
-
- //convenience
- InSyncDir& addDir(const Zstring& shortName, InSyncStatus st)
- {
- //use C++11 emplace when available
- return dirs.insert(std::make_pair(shortName, InSyncDir(st))).first->second;
- }
-
- void addFile(const Zstring& shortName, const InSyncDescrFile& dataL, const InSyncDescrFile& dataR, CompareVariant cmpVar, const UInt64& fileSize)
- {
- files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, cmpVar, fileSize)));
- }
-
- void addSymlink(const Zstring& shortName, const InSyncDescrLink& dataL, const InSyncDescrLink& dataR, CompareVariant cmpVar)
- {
- symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR, cmpVar)));
- }
-};
-
-
-DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting);
-
-std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound!
-
-void saveLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError
-}
-
-#endif //DBFILE_H_834275398588021574
diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h
deleted file mode 100644
index 19e5f745..00000000
--- a/lib/dir_exist_async.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef DIR_EXIST_HEADER_08173281673432158067342132467183267
-#define DIR_EXIST_HEADER_08173281673432158067342132467183267
-
-#include <zen/thread.h>
-#include <zen/file_handling.h>
-#include <zen/file_error.h>
-#include "process_callback.h"
-#include "resolve_path.h"
-
-namespace zen
-{
-namespace
-{
-//directory existence checking may hang for non-existent network drives => run asynchronously and update UI!
-//- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable)
-//- add reasonable time-out time!
-//- avoid checking duplicate entries by design: set<Zstring, LessFilename>
-std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::set<Zstring, LessFilename>& dirnames,
- std::set<Zstring, LessFilename>& missing,
- bool allowUserInteraction,
- ProcessCallback& procCallback)
-{
- using namespace zen;
-
- missing.clear();
-
- std::list<std::pair<Zstring, boost::unique_future<bool>>> futureInfo;
- for (const Zstring& dirname : dirnames)
- if (!dirname.empty())
- futureInfo.push_back(std::make_pair(dirname, async2<bool>([=]() -> bool
- {
-#ifdef ZEN_WIN
- //1. login to network share, if necessary
- loginNetworkShare(dirname, allowUserInteraction);
-#endif
- //2. check dir existence
- return dirExists(dirname);
- })));
-
- std::set<Zstring, LessFilename> output;
- //don't wait (almost) endlessly like win32 would on not existing network shares:
- const boost::system_time endTime = boost::get_system_time() + boost::posix_time::seconds(20); //consider CD-rom insert or hard disk spin up time from sleep
-
- for (auto& fi : futureInfo)
- {
- procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(fi.first), false)); //may throw!
-
- while (boost::get_system_time() < endTime &&
- !fi.second.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2)))
- procCallback.requestUiRefresh(); //may throw!
-
- if (fi.second.is_ready() && fi.second.get())
- output.insert(fi.first);
- else
- missing.insert(fi.first);
- }
- return output;
-}
-}
-
-inline //also silences Clang "unused function" for compilation units depending from getExistingDirsUpdating() only
-bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, ProcessCallback& procCallback)
-{
- if (dirname.empty()) return false;
- std::set<Zstring, LessFilename> missing;
- std::set<Zstring, LessFilename> dirsEx = getExistingDirsUpdating({ dirname }, missing, allowUserInteraction, procCallback);
- assert(dirsEx.empty() != missing.empty());
- return dirsEx.find(dirname) != dirsEx.end();
-}
-}
-
-#endif //DIR_EXIST_HEADER_08173281673432158067342132467183267
diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp
deleted file mode 100644
index fb016e1e..00000000
--- a/lib/dir_lock.cpp
+++ /dev/null
@@ -1,685 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-#include "dir_lock.h"
-#include <utility>
-#include <wx/log.h>
-#include <memory>
-#include <zen/sys_error.h>
-#include <zen/thread.h> //includes <boost/thread.hpp>
-#include <zen/scope_guard.h>
-#include <zen/guid.h>
-#include <zen/tick_count.h>
-#include <zen/assert_static.h>
-#include <zen/int64.h>
-#include <zen/file_handling.h>
-#include <zen/serialize.h>
-#include <zen/optional.h>
-
-#ifdef ZEN_WIN
-#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 ZEN_LINUX || defined ZEN_MAC
-#include <fcntl.h> //open()
-#include <sys/stat.h> //
-#include <unistd.h> //getsid()
-#include <signal.h> //kill()
-#include <pwd.h> //getpwuid_r()
-#endif
-
-using namespace zen;
-using namespace std::rel_ops;
-
-
-namespace
-{
-const int EMIT_LIFE_SIGN_INTERVAL = 5; //show life sign; unit: [s]
-const int POLL_LIFE_SIGN_INTERVAL = 4; //poll for life sign; unit: [s]
-const int DETECT_ABANDONED_INTERVAL = 30; //assume abandoned lock; unit: [s]
-
-const char LOCK_FORMAT_DESCR[] = "FreeFileSync";
-const int LOCK_FORMAT_VER = 2; //lock file format version
-}
-
-//worker thread
-class LifeSigns
-{
-public:
- LifeSigns(const Zstring& lockfilename) : //throw()!!! siehe SharedDirLock()
- lockfilename_(lockfilename) {} //thread safety: make deep copy!
-
- void operator()() const //thread entry
- {
- try
- {
- while (true)
- {
- boost::this_thread::sleep(boost::posix_time::seconds(EMIT_LIFE_SIGN_INTERVAL)); //interruption point!
-
- //actual work
- emitLifeSign(); //throw ()
- }
- }
- catch (const std::exception& e) //exceptions must be catched per thread
- {
- wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), utfCvrtTo<wxString>(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads
- }
- }
-
- void emitLifeSign() const //try to append one byte...; throw()
- {
- const char buffer[1] = {' '};
-#ifdef ZEN_WIN
- //ATTENTION: setting file pointer IS required! => use CreateFile/GENERIC_WRITE + SetFilePointerEx!
- //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!!
-
- const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_).c_str(),
- GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp
- FILE_SHARE_READ,
- nullptr,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- nullptr);
- if (fileHandle == INVALID_HANDLE_VALUE)
- return;
- ZEN_ON_SCOPE_EXIT(::CloseHandle(fileHandle));
-
- const LARGE_INTEGER moveDist = {};
- if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile,
- moveDist, //__in LARGE_INTEGER liDistanceToMove,
- nullptr, //__out_opt PLARGE_INTEGER lpNewFilePointer,
- FILE_END)) //__in DWORD dwMoveMethod
- return;
-
- DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx
- if (!::WriteFile(fileHandle, //_In_ HANDLE hFile,
- buffer, //_In_ LPCVOID lpBuffer,
- 1, //_In_ DWORD nNumberOfBytesToWrite,
- &bytesWritten, //_Out_opt_ LPDWORD lpNumberOfBytesWritten,
- nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
- return;
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
- const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND);
- if (fileHandle == -1)
- return;
- ZEN_ON_SCOPE_EXIT(::close(fileHandle));
-
- const ssize_t bytesWritten = ::write(fileHandle, buffer, 1);
- (void)bytesWritten;
-#endif
- }
-
-private:
- const Zstring lockfilename_; //thread local! atomic ref-count => binary value-type semantics!
-};
-
-
-namespace
-{
-UInt64 getLockFileSize(const Zstring& filename) //throw FileError
-{
-#ifdef ZEN_WIN
- WIN32_FIND_DATA fileInfo = {};
- const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo);
- if (searchHandle != INVALID_HANDLE_VALUE)
- {
- ::FindClose(searchHandle);
- return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
- }
- const wchar_t functionName[] = L"FindFirstFile";
-#elif defined ZEN_LINUX || defined ZEN_MAC
- struct ::stat fileInfo = {};
- if (::stat(filename.c_str(), &fileInfo) == 0) //follow symbolic links
- return UInt64(fileInfo.st_size);
- const wchar_t functionName[] = L"stat";
-#endif
-
- const ErrorCode lastError = getLastError();
- const std::wstring errorMsg = replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename));
- const std::wstring errorDescr = formatSystemError(functionName, lastError);
- throw FileError(errorMsg, errorDescr);
-}
-
-
-Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT change file ending!
-{
- const size_t pos = lockfilename.rfind(FILE_NAME_SEPARATOR); //search from end
- return pos == Zstring::npos ? Zstr("Del.") + lockfilename :
- Zstring(lockfilename.c_str(), pos + 1) + //include path separator
- Zstr("Del.") +
- afterLast(lockfilename, FILE_NAME_SEPARATOR); //returns the whole string if ch not found
-}
-
-
-#ifdef ZEN_WIN
-Zstring 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."), formatSystemError(L"OpenProcessToken", getLastError()));
- 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."), formatSystemError(L"GetTokenInformation", getLastError()));
-
- 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."), formatSystemError(L"ConvertSidToStringSid", getLastError()));
- ZEN_ON_SCOPE_EXIT(::LocalFree(sidStr));
- return sidStr;
- }
- throw FileError(_("Cannot get process information."), L"no login found"); //shouldn't happen
-}
-#endif
-
-
-#ifdef ZEN_WIN
-typedef DWORD ProcessId;
-typedef DWORD SessionId;
-#elif defined ZEN_LINUX || defined ZEN_MAC
-typedef pid_t ProcessId;
-typedef pid_t SessionId;
-#endif
-
-//return ppid on Windows, sid on Linux/Mac, "no value" if process corresponding to "processId" is not existing
-Opt<SessionId> getSessionId(ProcessId processId) //throw FileError
-{
-#ifdef ZEN_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)
- throw FileError(_("Cannot get process information."), formatSystemError(L"CreateToolhelp32Snapshot", getLastError()));
- ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot));
-
- PROCESSENTRY32 processEntry = {};
- processEntry.dwSize = sizeof(processEntry);
-
- if (!::Process32First(snapshot, //__in HANDLE hSnapshot,
- &processEntry)) //__inout LPPROCESSENTRY32 lppe
- throw FileError(_("Cannot get process information."), formatSystemError(L"Process32First", getLastError())); //ERROR_NO_MORE_FILES not possible
- do
- {
- if (processEntry.th32ProcessID == processId) //yes, MSDN says this is the way: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684868(v=vs.85).aspx
- return processEntry.th32ParentProcessID; //parent id is stable, even if parent process has already terminated!
- }
- while (::Process32Next(snapshot, &processEntry));
- if (::GetLastError() != ERROR_NO_MORE_FILES) //yes, they call it "files"
- throw FileError(_("Cannot get process information."), formatSystemError(L"Process32Next", getLastError()));
-
- return NoValue();
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
- if (::kill(processId, 0) != 0) //sig == 0: no signal sent, just existence check
- return NoValue();
-
- pid_t procSid = ::getsid(processId); //NOT to be confused with "login session", e.g. not stable on OS X!!!
- if (procSid == -1)
- throw FileError(_("Cannot get process information."), formatSystemError(L"getsid", getLastError()));
-
- return procSid;
-#endif
-}
-
-
-class FromCurrentProcess {}; //tag
-
-struct LockInformation //throw FileError
-{
- explicit LockInformation(FromCurrentProcess) :
- lockId(zen::generateGUID()),
- sessionId(), //dummy value
-#ifdef ZEN_WIN
- 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."), formatSystemError(L"GetComputerNameEx", getLastError()));
-
- computerName = "Windows." + utfCvrtTo<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."), formatSystemError(L"GetUserName", getLastError()));
- userId = utfCvrtTo<std::string>(&buffer[0]);
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
- processId(::getpid()) //never fails
- {
- std::vector<char> buffer(10000);
-
- if (::gethostname(&buffer[0], buffer.size()) != 0)
- throw FileError(_("Cannot get process information."), formatSystemError(L"gethostname", getLastError()));
- computerName += "Linux."; //distinguish linux/windows lock files
- computerName += &buffer[0];
-
- if (::getdomainname(&buffer[0], buffer.size()) != 0)
- throw FileError(_("Cannot get process information."), formatSystemError(L"getdomainname", getLastError()));
- computerName += ".";
- computerName += &buffer[0];
-
- const uid_t userIdNo = ::getuid(); //never fails
- userId.assign(reinterpret_cast<const char*>(&userIdNo), sizeof(userIdNo));
-
- //the id alone is not very distinctive, e.g. often 1000 on Ubuntu => add name
- buffer.resize(std::max<long>(buffer.size(), ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1)
- struct passwd buffer2 = {};
- struct passwd* pwsEntry = nullptr;
- if (::getpwuid_r(userIdNo, &buffer2, &buffer[0], buffer.size(), &pwsEntry) != 0) //getlogin() is deprecated and not working on Ubuntu at all!!!
- throw FileError(_("Cannot get process information."), formatSystemError(L"getpwuid_r", getLastError()));
- if (!pwsEntry)
- throw FileError(_("Cannot get process information."), L"no login found"); //should not happen?
- userId += pwsEntry->pw_name;
-#endif
-
- Opt<SessionId> sessionIdTmp = getSessionId(processId); //throw FileError
- if (!sessionIdTmp)
- throw FileError(_("Cannot get process information."), L"no session id found"); //should not happen?
- sessionId = *sessionIdTmp;
- }
-
- explicit LockInformation(BinStreamIn& stream) //throw UnexpectedEndOfStreamError
- {
- char tmp[sizeof(LOCK_FORMAT_DESCR)] = {};
- readArray(stream, &tmp, sizeof(tmp)); //file format header
- const int lockFileVersion = readNumber<boost::int32_t>(stream); //
-
- if (!std::equal(std::begin(tmp), std::end(tmp), std::begin(LOCK_FORMAT_DESCR)) ||
- lockFileVersion != LOCK_FORMAT_VER)
- throw UnexpectedEndOfStreamError(); //well, not really...!?
-
- lockId = readContainer<std::string>(stream); //
- computerName = readContainer<std::string>(stream); //UnexpectedEndOfStreamError
- userId = readContainer<std::string>(stream); //
- sessionId = static_cast<SessionId>(readNumber<std::uint64_t>(stream)); //[!] conversion
- processId = static_cast<ProcessId>(readNumber<std::uint64_t>(stream)); //[!] conversion
- }
-
- void toStream(BinStreamOut& stream) const //throw ()
- {
- writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR));
- writeNumber<boost::int32_t>(stream, LOCK_FORMAT_VER);
-
- assert_static(sizeof(processId) <= sizeof(std::uint64_t)); //ensure cross-platform compatibility!
- assert_static(sizeof(sessionId) <= sizeof(std::uint64_t)); //
-
- writeContainer(stream, lockId);
- writeContainer(stream, computerName);
- writeContainer(stream, userId);
- writeNumber<std::uint64_t>(stream, sessionId);
- writeNumber<std::uint64_t>(stream, processId);
- }
-
- std::string lockId; //16 byte GUID - a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.)
-
- //identify local computer
- std::string computerName; //format: HostName.DomainName
- std::string userId;
-
- //identify running process
- SessionId sessionId; //Windows: parent process id; Linux/OS X: session of the process, NOT the user
- ProcessId processId;
-};
-
-
-//wxGetFullHostName() is a performance killer for some users, so don't touch!
-
-
-LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError
-{
- BinStreamIn streamIn = loadBinStream<BinaryStream>(lockfilename); //throw FileError
- try
- {
- return LockInformation(streamIn); //throw UnexpectedEndOfStreamError
- }
- catch (UnexpectedEndOfStreamError&)
- {
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(lockfilename)), L"unexpected end of stream");
- }
-}
-
-
-inline
-std::string retrieveLockId(const Zstring& lockfilename) //throw FileError
-{
- return retrieveLockInfo(lockfilename).lockId; //throw FileError
-}
-
-
-enum ProcessStatus
-{
- PROC_STATUS_NOT_RUNNING,
- PROC_STATUS_RUNNING,
- PROC_STATUS_ITS_US,
- PROC_STATUS_CANT_TELL
-};
-
-ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileError
-{
- const LockInformation localInfo((FromCurrentProcess())); //throw FileError
-
- if (lockInfo.computerName != localInfo.computerName ||
- lockInfo.userId != localInfo.userId) //another user may run a session right now!
- return PROC_STATUS_CANT_TELL; //lock owned by different computer in this network
-
- if (lockInfo.sessionId == localInfo.sessionId &&
- 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;
-
- if (Opt<SessionId> sessionId = getSessionId(lockInfo.processId)) //throw FileError
- return *sessionId == lockInfo.sessionId ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING;
- return PROC_STATUS_NOT_RUNNING;
-}
-
-
-const std::int64_t TICKS_PER_SEC = ticksPerSec(); //= 0 on error
-
-
-void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
-{
- const std::wstring infoMsg = replaceCpy(_("Waiting while directory is locked (%x)..."), L"%x", fmtFileName(lockfilename));
-
- if (callback)
- callback->reportStatus(infoMsg);
- //---------------------------------------------------------------
- try
- {
- //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
- {
- const LockInformation& lockInfo = retrieveLockInfo(lockfilename); //throw FileError
- 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_CANT_TELL:
- break;
- }
- }
- catch (FileError&) {} //logfile may be only partly written -> this is no error!
-
- UInt64 fileSizeOld;
- TickVal lastLifeSign = getTicks();
-
- while (true)
- {
- const TickVal now = getTicks();
- const UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError
-
- if (TICKS_PER_SEC <= 0 || !lastLifeSign.isValid() || !now.isValid())
- throw FileError(L"System timer failed."); //no i18n: "should" never throw ;)
-
- if (fileSizeNew != fileSizeOld) //received life sign from lock
- {
- fileSizeOld = fileSizeNew;
- lastLifeSign = now;
- }
-
- if (lockOwnderDead || //no need to wait any longer...
- dist(lastLifeSign, now) / TICKS_PER_SEC > 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 (!originalLockId.empty())
- if (retrieveLockId(lockfilename) != originalLockId) //throw FileError -> 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
- continue; //late life sign
-
- removeFile(lockfilename); //throw FileError
- return;
- }
-
- //wait some time...
- assert_static(1000 * POLL_LIFE_SIGN_INTERVAL % GUI_CALLBACK_INTERVAL == 0);
- for (size_t i = 0; i < 1000 * POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i)
- {
- if (callback) callback->requestUiRefresh();
- boost::this_thread::sleep(boost::posix_time::milliseconds(GUI_CALLBACK_INTERVAL));
-
- if (callback)
- {
- //one signal missed: it's likely this is an abandoned lock => show countdown
- if (dist(lastLifeSign, now) / TICKS_PER_SEC > EMIT_LIFE_SIGN_INTERVAL)
- {
- const int remainingSeconds = std::max<int>(0, DETECT_ABANDONED_INTERVAL - dist(lastLifeSign, getTicks()) / TICKS_PER_SEC);
- const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo<std::wstring>(remainingSeconds));
- callback->reportStatus(infoMsg + L" " + remSecMsg);
- }
- else
- callback->reportStatus(infoMsg); //emit a message in any case (might clear other one)
- }
- }
- }
- }
- catch (FileError&)
- {
- if (!somethingExists(lockfilename)) //a benign(?) race condition with FileError
- return; //what we are waiting for...
- throw;
- }
-}
-
-
-void releaseLock(const Zstring& lockfilename) //throw ()
-{
- try
- {
- removeFile(lockfilename); //throw FileError
- }
- catch (FileError&) {}
-}
-
-
-bool tryLock(const Zstring& lockfilename) //throw FileError
-{
-#ifdef ZEN_WIN
- const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(),
- GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- CREATE_NEW,
- FILE_ATTRIBUTE_NORMAL,
- nullptr);
- if (fileHandle == INVALID_HANDLE_VALUE)
- {
- 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(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)), formatSystemError(L"CreateFile", getLastError()));
- }
- ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); });
- FileOutput fileOut(fileHandle, lockfilename); //pass handle ownership
-
- //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp
- //=> we don't need it that badly //::SetFileAttributes(applyLongPathPrefix(lockfilename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
- ::umask(0); //important! -> why?
- //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open
- 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(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)), formatSystemError(L"open", getLastError()));
- }
- ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); });
- FileOutputUnbuffered fileOut(fileHandle, lockfilename); //pass handle ownership
-#endif
-
- //write housekeeping info: user, process info, lock GUID
- BinaryStream binStream;
- {
- BinStreamOut streamOut;
- LockInformation(FromCurrentProcess()).toStream(streamOut);
- binStream = streamOut.get();
- }
- if (!binStream.empty())
- fileOut.write(&*binStream.begin(), binStream.size()); //throw FileError
-
- guardLockFile.dismiss(); //lockfile created successfully
- return true;
-}
-}
-
-
-class DirLock::SharedDirLock
-{
-public:
- SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback) : //throw FileError
- lockfilename_(lockfilename)
- {
- while (!::tryLock(lockfilename)) //throw FileError
- ::waitOnDirLock(lockfilename, callback); //
-
- threadObj = boost::thread(LifeSigns(lockfilename));
- }
-
- ~SharedDirLock()
- {
- threadObj.interrupt(); //thread lifetime is subset of this instances's life
- threadObj.join(); //we assert precondition "threadObj.joinable()"!!!
-
- ::releaseLock(lockfilename_); //throw ()
- }
-
-private:
- SharedDirLock(const DirLock&);
- SharedDirLock& operator=(const DirLock&);
-
- const Zstring lockfilename_;
- boost::thread threadObj;
-};
-
-
-class DirLock::LockAdmin //administrate all locks held by this process to avoid deadlock by recursion
-{
-public:
- static LockAdmin& instance()
- {
- static LockAdmin inst;
- return inst;
- }
-
- //create or retrieve a SharedDirLock
- std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
- {
- 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 //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
- if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(lockId)) //returns null-lock if not found
- {
- 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 files
-
- //lock not owned by us => create a new one
- auto newLock = std::make_shared<SharedDirLock>(lockfilename, callback); //throw FileError
- const std::string& newLockGuid = retrieveLockId(lockfilename); //throw FileError
-
- //update registry
- fileToGuid[lockfilename] = newLockGuid; //throw()
- guidToLock[newLockGuid] = newLock; //
-
- return newLock;
- }
-
-private:
- LockAdmin() {}
- LockAdmin(const LockAdmin&); //=delete
- LockAdmin& operator=(const LockAdmin&); //=delete
-
- 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 = guidToLock.find(lockId);
- return iterLock != guidToLock.end() ? iterLock->second.lock() : nullptr; //try to get shared_ptr; throw()
- }
-
- 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(); });
- }
-
- FileToGuidMap fileToGuid; //lockname |-> GUID; locks can be referenced by a lockfilename or alternatively a GUID
- GuidToLockMap guidToLock; //GUID |-> "shared lock ownership"
-};
-
-
-DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
-{
- if (callback)
- callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtFileName(lockfilename)));
-
-#ifdef ZEN_WIN
- const DWORD bufferSize = 10000;
- std::vector<wchar_t> volName(bufferSize);
- if (::GetVolumePathName(lockfilename.c_str(), //__in LPCTSTR lpszFileName,
- &volName[0], //__out LPTSTR lpszVolumePathName,
- bufferSize)) //__in DWORD cchBufferLength
- {
- DWORD dt = ::GetDriveType(&volName[0]);
- if (dt == DRIVE_CDROM)
- return; //we don't need a lock for a CD ROM
- }
-#endif
-
- sharedLock = LockAdmin::instance().retrieve(lockfilename, callback); //throw FileError
-}
diff --git a/lib/dir_lock.h b/lib/dir_lock.h
deleted file mode 100644
index ec2a431a..00000000
--- a/lib/dir_lock.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-#ifndef DIR_LOCK_H_INCLUDED
-#define DIR_LOCK_H_INCLUDED
-
-#include <memory>
-#include <zen/file_error.h>
-
-namespace zen
-{
-const size_t GUI_CALLBACK_INTERVAL = 100;
-
-struct DirLockCallback //while waiting for the lock
-{
- virtual ~DirLockCallback() {}
- virtual void requestUiRefresh() = 0; //allowed to throw exceptions
- virtual void reportStatus(const std::wstring& text) = 0;
-};
-
-/*
-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(= 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))
- - NOT thread-safe! (1. static LockAdmin 2. directory name aliases must be resolved sequentially!)
-*/
-class DirLock
-{
-public:
- DirLock(const Zstring& lockfilename, DirLockCallback* callback = nullptr); //throw FileError, callback only used during construction
-
-private:
- class LockAdmin;
- class SharedDirLock;
- std::shared_ptr<SharedDirLock> sharedLock;
-};
-}
-
-#endif // DIR_LOCK_H_INCLUDED
diff --git a/lib/error_log.h b/lib/error_log.h
deleted file mode 100644
index 2971f746..00000000
--- a/lib/error_log.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef ERROR_LOG_89734181783491324134
-#define ERROR_LOG_89734181783491324134
-
-#include <cassert>
-#include <zen/serialize.h>
-#include <zen/time.h>
-#include "ffs_paths.h"
-
-
-namespace zen
-{
-//write error message to a file (even with corrupted stack)- call in desperate situations when no other means of error handling is available
-void logError(const std::string& msg); //throw()
-
-
-
-
-
-
-
-
-
-//##################### implementation ############################
-inline
-void logError(const std::string& msg) //throw()
-{
- assert(false); //this is stuff we like to debug
- const std::string logEntry = "[" + formatTime<std::string>(FORMAT_DATE) + " "+ formatTime<std::string>(FORMAT_TIME) + "] " + msg;
- try
- {
- saveBinStream(getConfigDir() + Zstr("LastError.log"), logEntry); //throw FileError
- }
- catch (const FileError&) {}
-}
-}
-
-#endif //ERROR_LOG_89734181783491324134
diff --git a/lib/ffs_paths.cpp b/lib/ffs_paths.cpp
deleted file mode 100644
index 5c775d3e..00000000
--- a/lib/ffs_paths.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "ffs_paths.h"
-#include <zen/file_handling.h>
-#include <wx/stdpaths.h>
-#include <wx/app.h>
-#include <wx+/string_conv.h>
-
-#ifdef ZEN_MAC
-#include <vector>
-#include <zen/scope_guard.h>
-#include <zen/osx_string.h>
-//keep in .cpp file to not pollute global namespace! e.g. with UInt64:
-#include <ApplicationServices/ApplicationServices.h> //LSFindApplicationForInfo
-#endif
-
-using namespace zen;
-
-
-namespace
-{
-#if defined ZEN_WIN || defined ZEN_LINUX
-inline
-Zstring getExecutableDir() //directory containing executable WITH path separator at end
-{
- return appendSeparator(beforeLast(utfCvrtTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR));
-}
-#endif
-
-#ifdef ZEN_WIN
-inline
-Zstring getInstallDir() //root install directory WITH path separator at end
-{
- return appendSeparator(beforeLast(beforeLast(getExecutableDir(), FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR));
-}
-#endif
-
-
-#ifdef ZEN_WIN
-inline
-bool isPortableVersion() { return !fileExists(getInstallDir() + L"uninstall.exe"); } //this check is a bit lame...
-#elif defined ZEN_LINUX
-inline
-bool isPortableVersion() { return !endsWith(getExecutableDir(), "/bin/"); } //this check is a bit lame...
-#endif
-}
-
-
-bool zen::manualProgramUpdateRequired()
-{
-#if defined ZEN_WIN || defined ZEN_MAC
- return true;
-#elif defined ZEN_LINUX
- return isPortableVersion(); //locally installed version is updated by system
-#endif
-}
-
-
-Zstring zen::getResourceDir()
-{
- //make independent from wxWidgets global variable "appname"; support being called by RealtimeSync
- auto appName = wxTheApp->GetAppName();
- wxTheApp->SetAppName(L"FreeFileSync");
- ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName));
-
-#ifdef ZEN_WIN
- return getInstallDir();
-#elif defined ZEN_LINUX
- if (isPortableVersion())
- return getExecutableDir();
- else //use OS' standard paths
- return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir()));
-#elif defined ZEN_MAC
- return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); //if packaged, used "Contents/Resources", else the executable directory
-#endif
-}
-
-
-Zstring zen::getConfigDir()
-{
- //make independent from wxWidgets global variable "appname"; support being called by RealtimeSync
- auto appName = wxTheApp->GetAppName();
- wxTheApp->SetAppName(L"FreeFileSync");
- ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName));
-
-#ifdef ZEN_WIN
- if (isPortableVersion())
- return getInstallDir();
-#elif defined ZEN_LINUX
- if (isPortableVersion())
- return getExecutableDir();
-#elif defined ZEN_MAC
- //portable apps do not seem common on OS - fine with me: http://theocacao.com/document.page/319
-#endif
- //use OS' standard paths
- Zstring userDirectory = toZ(wxStandardPathsBase::Get().GetUserDataDir());
-
- if (!dirExists(userDirectory))
- try
- {
- makeDirectory(userDirectory); //throw FileError
- }
- catch (const FileError&) {}
-
- return appendSeparator(userDirectory);
-}
-
-
-//this function is called by RealtimeSync!!!
-Zstring zen::getFreeFileSyncLauncher()
-{
-#ifdef ZEN_WIN
- return getInstallDir() + Zstr("FreeFileSync.exe");
-
-#elif defined ZEN_LINUX
- return getExecutableDir() + Zstr("FreeFileSync");
-
-#elif defined ZEN_MAC
- CFURLRef appURL = nullptr;
- ZEN_ON_SCOPE_EXIT(if (appURL) ::CFRelease(appURL));
-
- if (::LSFindApplicationForInfo(kLSUnknownCreator, // OSType inCreator,
- CFSTR("net.SourceForge.FreeFileSync"),//CFStringRef inBundleID,
- nullptr, //CFStringRef inName,
- nullptr, //FSRef *outAppRef,
- &appURL) == noErr) //CFURLRef *outAppURL
- if (appURL)
- if (CFURLRef absUrl = ::CFURLCopyAbsoluteURL(appURL))
- {
- ZEN_ON_SCOPE_EXIT(::CFRelease(absUrl));
-
- if (CFStringRef path = ::CFURLCopyFileSystemPath(absUrl, kCFURLPOSIXPathStyle))
- {
- ZEN_ON_SCOPE_EXIT(::CFRelease(path));
- return appendSeparator(osx::cfStringToZstring(path)) + "Contents/MacOS/FreeFileSync";
- }
- }
- return Zstr("./FreeFileSync"); //fallback: at least give some hint...
-#endif
-}
diff --git a/lib/ffs_paths.h b/lib/ffs_paths.h
deleted file mode 100644
index 28516a3f..00000000
--- a/lib/ffs_paths.h
+++ /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 (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef STANDARDPATHS_H_84275908342534253425
-#define STANDARDPATHS_H_84275908342534253425
-
-#include <zen/zstring.h>
-
-namespace zen
-{
-//------------------------------------------------------------------------------
-//global program directories
-//------------------------------------------------------------------------------
-Zstring getResourceDir(); //resource directory WITH path separator at end
-Zstring getConfigDir (); //config directory WITH path separator at end
-//------------------------------------------------------------------------------
-
-Zstring getFreeFileSyncLauncher(); //full path to application launcher C:\...\FreeFileSync.exe
-bool manualProgramUpdateRequired();
-}
-
-#endif //STANDARDPATHS_H_84275908342534253425
diff --git a/lib/generate_logfile.h b/lib/generate_logfile.h
deleted file mode 100644
index ff97b63a..00000000
--- a/lib/generate_logfile.h
+++ /dev/null
@@ -1,181 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef GEN_LOGFILE_H_93172643216748973216458732165415
-#define GEN_LOGFILE_H_93172643216748973216458732165415
-
-#include <zen/error_log.h>
-#include <zen/file_io.h>
-#include <zen/serialize.h>
-#include <zen/format_unit.h>
-#include "ffs_paths.h"
-
-
-namespace zen
-{
-struct SummaryInfo
-{
- std::wstring jobName; //may be empty
- std::wstring finalStatus;
- int itemsSynced;
- Int64 dataSynced;
- int itemsTotal;
- Int64 dataTotal;
- long totalTime; //unit: [sec]
-};
-
-void saveLogToFile(const SummaryInfo& summary, //throw FileError
- const ErrorLog& log,
- FileOutput& fileOut);
-
-void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
- const ErrorLog& log,
- size_t maxBytesToWrite);
-
-
-
-
-
-//####################### implementation #######################
-namespace
-{
-std::wstring generateLogHeader(const SummaryInfo& s)
-{
- assert(s.itemsSynced <= s.itemsTotal);
- assert(s.dataSynced <= s.dataTotal);
-
- std::wstring output;
-
- //write header
- std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE);
- if (!s.jobName.empty())
- headerLine += L" - " + s.jobName;
- headerLine += L": " + s.finalStatus;
-
- //assemble results box
- std::vector<std::wstring> results;
- results.push_back(headerLine);
- results.push_back(L"");
-
- const wchar_t tabSpace[] = L" ";
-
- std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + toGuiString(s.itemsSynced); //show always, even if 0!
- if (s.itemsSynced != 0 || s.dataSynced != 0) //[!] don't show 0 bytes processed if 0 items were processed
- itemsProc += + L" (" + filesizeToShortString(s.dataSynced) + L")";
- results.push_back(itemsProc);
-
- if (s.itemsTotal != 0 || s.dataTotal != 0) //=: sync phase was reached and there were actual items to sync
- {
- if (s.itemsSynced != s.itemsTotal ||
- s.dataSynced != s.dataTotal)
- results.push_back(tabSpace + _("Items remaining:") + L" " + toGuiString(s.itemsTotal - s.itemsSynced) + L" (" + filesizeToShortString(s.dataTotal - s.dataSynced) + L")");
- }
-
- results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(s.totalTime).Format()));
-
- //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway!
- size_t sepLineLen = 0;
- std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { sepLineLen = std::max(sepLineLen, str.size()); });
-
- output.resize(output.size() + sepLineLen + 1, L'_');
- output += L'\n';
-
- std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += L'|'; output += str; output += L'\n'; });
-
- output += L'|';
- output.resize(output.size() + sepLineLen, L'_');
- output += L'\n';
-
- return output;
-}
-}
-
-
-inline
-void saveLogToFile(const SummaryInfo& summary, //throw FileError
- const ErrorLog& log,
- FileOutput& fileOut)
-{
- Utf8String header = utfCvrtTo<Utf8String>(generateLogHeader(summary));
- replace(header, '\n', LINE_BREAK); //don't replace line break any earlier
- header += LINE_BREAK; //make sure string is not empty!
-
- fileOut.write(&*header.begin(), header.size()); //throw FileError
-
- //write log items one after the other instead of creating one big string: memory allocation might fail; think 1 million entries!
- for (auto iter = log.begin(); iter != log.end(); ++iter)
- {
- Utf8String msg = replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(*iter)), '\n', LINE_BREAK);
- msg += LINE_BREAK; //make sure string is not empty!
-
- fileOut.write(&*msg.begin(), msg.size()); //throw FileError
- }
-}
-
-
-inline
-void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
- const ErrorLog& log,
- size_t maxBytesToWrite) //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems!
-{
- const Zstring filename = getConfigDir() + Zstr("LastSyncs.log");
-
- Utf8String newStream = utfCvrtTo<Utf8String>(generateLogHeader(summary));
- replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier
- newStream += LINE_BREAK;
-
- //check size of "newStream": memory allocation might fail - think 1 million entries!
- for (auto iter = log.begin(); iter != log.end(); ++iter)
- {
- newStream += replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(*iter)), '\n', LINE_BREAK);
- newStream += LINE_BREAK;
-
- if (newStream.size() > maxBytesToWrite)
- {
- newStream += "[...]";
- newStream += LINE_BREAK;
- break;
- }
- }
-
- //fill up the rest of permitted space by appending old log
- if (newStream.size() < maxBytesToWrite)
- {
- Utf8String oldStream;
- try
- {
- oldStream = loadBinStream<Utf8String>(filename); //throw FileError
- }
- catch (FileError&) {}
-
- if (!oldStream.empty())
- {
- newStream += LINE_BREAK;
- newStream += LINE_BREAK;
- newStream += oldStream; //impliticly limited by "maxBytesToWrite"!
-
- //truncate size if required
- if (newStream.size() > maxBytesToWrite)
- {
- //but do not cut in the middle of a row
- auto iter = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1);
- if (iter != newStream.cend())
- {
- newStream.resize(iter - newStream.cbegin());
- newStream += LINE_BREAK;
-
- newStream += "[...]";
- newStream += LINE_BREAK;
- }
- }
- }
- }
-
- saveBinStream(filename, newStream); //throw FileError
-}
-}
-
-#endif //GEN_LOGFILE_H_93172643216748973216458732165415
diff --git a/lib/hard_filter.cpp b/lib/hard_filter.cpp
deleted file mode 100644
index 687aecd8..00000000
--- a/lib/hard_filter.cpp
+++ /dev/null
@@ -1,404 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "hard_filter.h"
-#include <set>
-#include <stdexcept>
-#include <vector>
-#include <typeinfo>
-#include <iterator>
-//#include "../structures.h"
-
-using namespace zen;
-
-//inline bool operator<(const std::type_info& lhs, const std::type_info& rhs) { return lhs.before(rhs) != 0; } -> not working on GCC :(
-
-
-//--------------------------------------------------------------------------------------------------
-bool zen::operator<(const HardFilter& lhs, const HardFilter& rhs)
-{
- if (typeid(lhs) != typeid(rhs))
- return typeid(lhs).before(typeid(rhs)) != 0; //note: in worst case, order is guaranteed to be stable only during each program run
-
- //this and other are same type:
- return lhs.cmpLessSameType(rhs);
-}
-
-
-//void HardFilter::saveFilter(ZstreamOut& stream) const //serialize derived object
-//{
-// //save type information
-// writeString(stream, uniqueClassIdentifier());
-//
-// //save actual object
-// save(stream);
-//}
-
-
-//HardFilter::FilterRef HardFilter::loadFilter(ZstreamIn& stream) //throw UnexpectedEndOfStreamError
-//{
-// //read type information
-// const std::string uniqueClassId = readString<std::string>(stream); //throw UnexpectedEndOfStreamError
-//
-// //read actual object
-// if (uniqueClassId == "NullFilter")
-// return NullFilter::load(stream);
-// else if (uniqueClassId == "NameFilter")
-// return NameFilter::load(stream);
-// else if (uniqueClassId == "CombinedFilter")
-// return CombinedFilter::load(stream);
-// else
-// throw std::logic_error("Programming Error: Unknown filter!");
-//}
-
-
-namespace
-{
-//constructing them in addFilterEntry becomes perf issue for large filter lists
-const Zstring asterisk(Zstr('*'));
-const Zstring sepAsterisk = FILE_NAME_SEPARATOR + asterisk;
-const Zstring asteriskSep = asterisk + FILE_NAME_SEPARATOR;
-const Zstring asteriskSepAsterisk = asteriskSep + asterisk;
-}
-
-
-void addFilterEntry(const Zstring& filterPhrase, std::vector<Zstring>& fileFilter, std::vector<Zstring>& directoryFilter)
-{
-#if defined ZEN_WIN || defined ZEN_MAC
- //Windows does NOT distinguish between upper/lower-case
- Zstring filterFormatted = filterPhrase;
- makeUpper(filterFormatted);
-#elif defined ZEN_LINUX
- const Zstring& filterFormatted = filterPhrase;
- //Linux DOES distinguish between upper/lower-case: nothing to do here
-#endif
- /*
- phrase | action
- +---------+--------
- | \blah | remove \
- | \*blah | remove \
- | \*\blah | remove \
- | \*\* | remove \
- +---------+--------
- | *blah |
- | *\blah | -> add blah
- | *\*blah | -> add *blah
- +---------+--------
- | blah\ | remove \; directory only
- | blah*\ | remove \; directory only
- | blah\*\ | remove \; directory only
- +---------+--------
- | blah* |
- | blah\* | add blah for directory only
- | blah*\* | add blah* for directory only
- +---------+--------
- */
- auto processTail = [&fileFilter, &directoryFilter](const Zstring& phrase)
- {
- if (endsWith(phrase, FILE_NAME_SEPARATOR)) //only relevant for directory filtering
- {
- const Zstring dirPhrase = beforeLast(phrase, FILE_NAME_SEPARATOR);
- if (!dirPhrase.empty())
- directoryFilter.push_back(dirPhrase);
- }
- else if (!phrase.empty())
- {
- fileFilter .push_back(phrase);
- directoryFilter.push_back(phrase);
- if (endsWith(phrase, sepAsterisk)) // abc\*
- {
- const Zstring dirPhrase = beforeLast(phrase, sepAsterisk);
- if (!dirPhrase.empty())
- directoryFilter.push_back(dirPhrase);
- }
- }
- };
-
- if (startsWith(filterFormatted, FILE_NAME_SEPARATOR)) // \abc
- processTail(afterFirst(filterFormatted, FILE_NAME_SEPARATOR));
- else
- {
- processTail(filterFormatted);
- if (startsWith(filterFormatted, asteriskSep)) // *\abc
- processTail(afterFirst(filterFormatted, asteriskSep));
- }
-}
-
-
-namespace
-{
-template <class Char> inline
-const Char* cStringFind(const Char* str, Char ch) //strchr()
-{
- for (;;)
- {
- const Char s = *str;
- if (s == ch) //ch is allowed to be 0 by contract! must return end of string in this case
- return str;
-
- if (s == 0)
- return nullptr;
- ++str;
- }
-}
-
-
-bool matchesMask(const Zchar* str, const Zchar* mask)
-{
- for (;; ++mask, ++str)
- {
- Zchar m = *mask;
- if (m == 0)
- return *str == 0;
-
- switch (m)
- {
- case Zstr('?'):
- if (*str == 0)
- return false;
- break;
-
- case Zstr('*'):
- //advance mask to next non-* char
- do
- {
- m = *++mask;
- }
- while (m == Zstr('*'));
-
- if (m == 0) //mask ends with '*':
- return true;
-
- //*? - pattern
- if (m == Zstr('?'))
- {
- ++mask;
- while (*str++ != 0)
- if (matchesMask(str, mask))
- return true;
- return false;
- }
-
- //*[letter] - pattern
- ++mask;
- for (;;)
- {
- str = cStringFind(str, m);
- if (!str)
- return false;
-
- ++str;
- if (matchesMask(str, mask))
- return true;
- }
-
- default:
- if (*str != m)
- return false;
- }
- }
-}
-
-
-//returns true if string matches at least the beginning of mask
-inline
-bool matchesMaskBegin(const Zchar* str, const Zchar* mask)
-{
- for (;; ++mask, ++str)
- {
- const Zchar m = *mask;
- if (m == 0)
- return *str == 0;
-
- switch (m)
- {
- case Zstr('?'):
- if (*str == 0)
- return true;
- break;
-
- case Zstr('*'):
- return true;
-
- default:
- if (*str != m)
- return *str == 0;
- }
- }
-}
-}
-
-
-inline
-bool matchesFilter(const Zstring& name, const std::vector<Zstring>& filter)
-{
- return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMask(name.c_str(), mask.c_str()); });
-}
-
-
-inline
-bool matchesFilterBegin(const Zstring& name, const std::vector<Zstring>& filter)
-{
- return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); });
-}
-
-
-std::vector<Zstring> splitByDelimiter(const Zstring& filterString)
-{
- //delimiters may be ';' or '\n'
- std::vector<Zstring> output;
-
- const std::vector<Zstring> blocks = split(filterString, Zchar(';')); //split by less common delimiter first
- std::for_each(blocks.begin(), blocks.end(),
- [&](const Zstring& item)
- {
- const std::vector<Zstring> blocks2 = split(item, Zchar('\n'));
-
- std::for_each(blocks2.begin(), blocks2.end(),
- [&](Zstring entry)
- {
- trim(entry);
- if (!entry.empty())
- output.push_back(entry);
- });
- });
-
- return output;
-}
-
-//#################################################################################################
-NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter) :
- includeFilterTmp(includeFilter), //save constructor arguments for serialization
- excludeFilterTmp(excludeFilter)
-{
- //no need for regular expressions: In tests wxRegex was by factor of 10 slower than wxString::Matches()
-
- //load filter into vectors of strings
- //delimiters may be ';' or '\n'
- const std::vector<Zstring>& includeList = splitByDelimiter(includeFilter);
- const std::vector<Zstring>& excludeList = splitByDelimiter(excludeFilter);
-
- //setup include/exclude filters for files and directories
- std::for_each(includeList.begin(), includeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileIn, filterFolderIn); });
- std::for_each(excludeList.begin(), excludeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileEx, filterFolderEx); });
-
- auto removeDuplicates = [](std::vector<Zstring>& cont)
- {
- std::vector<Zstring> output;
- std::set<Zstring> used;
- std::copy_if(cont.begin(), cont.end(), std::back_inserter(output), [&](const Zstring& item) { return used.insert(item).second; });
- output.swap(cont);
- };
-
- removeDuplicates(filterFileIn);
- removeDuplicates(filterFolderIn);
- removeDuplicates(filterFileEx);
- removeDuplicates(filterFolderEx);
-}
-
-
-bool NameFilter::passFileFilter(const Zstring& relFilename) const
-{
-#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case
- Zstring nameFormatted = relFilename;
- makeUpper(nameFormatted);
-#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case
- const Zstring& nameFormatted = relFilename; //nothing to do here
-#endif
-
- return matchesFilter(nameFormatted, filterFileIn) && //process include filters
- !matchesFilter(nameFormatted, filterFileEx); //process exclude filters
-}
-
-
-bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const
-{
- assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage
-
-#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case
- Zstring nameFormatted = relDirname;
- makeUpper(nameFormatted);
-#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case
- const Zstring& nameFormatted = relDirname; //nothing to do here
-#endif
-
- if (matchesFilter(nameFormatted, filterFolderEx)) //process exclude filters
- {
- if (subObjMightMatch)
- *subObjMightMatch = false; //exclude subfolders/subfiles as well
- return false;
- }
-
- if (!matchesFilter(nameFormatted, filterFolderIn)) //process include filters
- {
- if (subObjMightMatch)
- {
- const Zstring& subNameBegin = nameFormatted + FILE_NAME_SEPARATOR; //const-ref optimization
-
- *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory
- matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory
- }
- return false;
- }
-
- return true;
-}
-
-
-bool NameFilter::isNull(const Zstring& includeFilter, const Zstring& excludeFilter)
-{
- Zstring include = includeFilter;
- Zstring exclude = excludeFilter;
- trim(include);
- trim(exclude);
-
- return include == Zstr("*") && exclude.empty();
- //return NameFilter(includeFilter, excludeFilter).isNull(); -> very expensive for huge lists
-}
-
-bool NameFilter::isNull() const
-{
- static NameFilter output(Zstr("*"), Zstring());
- return *this == output;
-}
-
-
-bool NameFilter::cmpLessSameType(const HardFilter& other) const
-{
- assert(typeid(*this) == typeid(other)); //always given in this context!
-
- const NameFilter& otherNameFilt = static_cast<const NameFilter&>(other);
-
- if (filterFileIn != otherNameFilt.filterFileIn)
- return filterFileIn < otherNameFilt.filterFileIn;
-
- if (filterFolderIn != otherNameFilt.filterFolderIn)
- return filterFolderIn < otherNameFilt.filterFolderIn;
-
- if (filterFileEx != otherNameFilt.filterFileEx)
- return filterFileEx < otherNameFilt.filterFileEx;
-
- if (filterFolderEx != otherNameFilt.filterFolderEx)
- return filterFolderEx < otherNameFilt.filterFolderEx;
-
- return false; //vectors equal
-}
-
-
-//void NameFilter::save(ZstreamOut& stream) const
-//{
-// writeString(stream, includeFilterTmp);
-// writeString(stream, excludeFilterTmp);
-//}
-//
-//
-//HardFilter::FilterRef NameFilter::load(ZstreamIn& stream) //throw UnexpectedEndOfStreamError
-//{
-// const Zstring include = readString<Zstring>(stream); //throw UnexpectedEndOfStreamError
-// const Zstring exclude = readString<Zstring>(stream); //
-//
-// return FilterRef(new NameFilter(include, exclude));
-//}
diff --git a/lib/hard_filter.h b/lib/hard_filter.h
deleted file mode 100644
index e721fe4f..00000000
--- a/lib/hard_filter.h
+++ /dev/null
@@ -1,270 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef HARD_FILTER_H_825780275842758345
-#define HARD_FILTER_H_825780275842758345
-
-#include <vector>
-#include <memory>
-#include <zen/zstring.h>
-//#include <wx+/serialize.h>
-
-namespace zen
-{
-//------------------------------------------------------------------
-/*
-Semantics of HardFilter:
-1. using it creates a NEW folder hierarchy! -> must be considered by <Automatic>-mode!
-2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder!
-
- class hierarchy:
-
- HardFilter (interface)
- /|\
- _________|_____________
- | | |
-NullFilter NameFilter CombinedFilter
-*/
-
-class HardFilter //interface for filtering
-{
-public:
- virtual ~HardFilter() {}
-
- //filtering
- virtual bool passFileFilter(const Zstring& relFilename) const = 0;
- virtual bool passDirFilter (const Zstring& relDirname, bool* subObjMightMatch) const = 0;
- //subObjMightMatch: file/dir in subdirectories could(!) match
- //note: variable is only set if passDirFilter returns false!
-
- virtual bool isNull() const = 0; //filter is equivalent to NullFilter, but may be technically slower
-
- typedef std::shared_ptr<const HardFilter> FilterRef; //always bound by design!
-
- //serialization
- // void saveFilter(ZstreamOut& stream) const; //serialize derived object
- // static FilterRef loadFilter(ZstreamIn& stream); //throw UnexpectedEndOfStreamError; CAVEAT!!! adapt this method for each new derivation!!!
-
-private:
- friend bool operator<(const HardFilter& lhs, const HardFilter& rhs);
-
- // virtual void save(ZstreamOut& stream) const = 0; //serialization
- virtual std::string uniqueClassIdentifier() const = 0; //get identifier, used for serialization
- virtual bool cmpLessSameType(const HardFilter& other) const = 0; //typeid(*this) == typeid(other) in this context!
-};
-
-inline bool operator==(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs < rhs) && !(rhs < lhs); }
-inline bool operator!=(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs == rhs); }
-
-
-//small helper method: merge two hard filters (thereby remove Null-filters)
-HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first,
- const HardFilter::FilterRef& second);
-
-
-class NullFilter : public HardFilter //no filtering at all
-{
-public:
- virtual bool passFileFilter(const Zstring& relFilename) const;
- virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const;
- virtual bool isNull() const;
-
-private:
- friend class HardFilter;
- // virtual void save(ZstreamOut& stream) const {}
- virtual std::string uniqueClassIdentifier() const { return "NullFilter"; }
- // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError
- virtual bool cmpLessSameType(const HardFilter& other) const;
-};
-
-
-class NameFilter : public HardFilter //standard filter by filename
-{
-public:
- NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter);
-
- virtual bool passFileFilter(const Zstring& relFilename) const;
- virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const;
-
- virtual bool isNull() const;
- static bool isNull(const Zstring& includeFilter, const Zstring& excludeFilter); //*fast* check without expensively constructing NameFilter instance!
-
-private:
- friend class HardFilter;
- // virtual void save(ZstreamOut& stream) const;
- virtual std::string uniqueClassIdentifier() const { return "NameFilter"; }
- // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError
- virtual bool cmpLessSameType(const HardFilter& other) const;
-
- std::vector<Zstring> filterFileIn; //
- std::vector<Zstring> filterFolderIn; //upper case (windows) + unique items by construction
- std::vector<Zstring> filterFileEx; //
- std::vector<Zstring> filterFolderEx; //
-
- const Zstring includeFilterTmp; //save constructor arguments for serialization
- const Zstring excludeFilterTmp; //
-};
-
-
-class CombinedFilter : public HardFilter //combine two filters to match if and only if both match
-{
-public:
- CombinedFilter(const FilterRef& first, const FilterRef& second) : first_(first), second_(second) {}
-
- virtual bool passFileFilter(const Zstring& relFilename) const;
- virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const;
- virtual bool isNull() const;
-
-private:
- friend class HardFilter;
- // virtual void save(ZstreamOut& stream) const;
- virtual std::string uniqueClassIdentifier() const { return "CombinedFilter"; }
- // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError
- virtual bool cmpLessSameType(const HardFilter& other) const;
-
- const FilterRef first_;
- const FilterRef second_;
-};
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//---------------Inline Implementation---------------------------------------------------
-//inline
-//HardFilter::FilterRef NullFilter::load(ZstreamIn& stream)
-//{
-// return FilterRef(new NullFilter);
-//}
-
-
-inline
-bool NullFilter::passFileFilter(const Zstring& relFilename) const
-{
- return true;
-}
-
-
-inline
-bool NullFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const
-{
- assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage
- return true;
-}
-
-
-inline
-bool NullFilter::isNull() const
-{
- return true;
-}
-
-
-inline
-bool NullFilter::cmpLessSameType(const HardFilter& other) const
-{
- assert(typeid(*this) == typeid(other)); //always given in this context!
- return false;
-}
-
-
-inline
-bool CombinedFilter::passFileFilter(const Zstring& relFilename) const
-{
- return first_ ->passFileFilter(relFilename) && //short-circuit behavior
- second_->passFileFilter(relFilename);
-}
-
-
-inline
-bool CombinedFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const
-{
- if (first_->passDirFilter(relDirname, subObjMightMatch))
- return second_->passDirFilter(relDirname, subObjMightMatch);
- else
- {
- if (subObjMightMatch && *subObjMightMatch)
- second_->passDirFilter(relDirname, subObjMightMatch);
- return false;
- }
-}
-
-
-inline
-bool CombinedFilter::isNull() const
-{
- return first_->isNull() && second_->isNull();
-}
-
-
-inline
-bool CombinedFilter::cmpLessSameType(const HardFilter& other) const
-{
- assert(typeid(*this) == typeid(other)); //always given in this context!
-
- const CombinedFilter& otherCombFilt = static_cast<const CombinedFilter&>(other);
-
- if (*first_ != *otherCombFilt.first_)
- return *first_ < *otherCombFilt.first_;
-
- return *second_ < *otherCombFilt.second_;
-}
-
-
-//inline
-//void CombinedFilter::save(ZstreamOut& stream) const
-//{
-// first_ ->saveFilter(stream);
-// second_->saveFilter(stream);
-//}
-
-
-//inline
-//HardFilter::FilterRef CombinedFilter::load(ZstreamIn& stream) //throw UnexpectedEndOfStreamError
-//{
-// FilterRef first = loadFilter(stream); //throw UnexpectedEndOfStreamError
-// FilterRef second = loadFilter(stream); //
-//
-// return combineFilters(first, second);
-//}
-
-
-inline
-HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first,
- const HardFilter::FilterRef& second)
-{
- if (first->isNull())
- {
- if (second->isNull())
- return std::make_shared<NullFilter>();
- else
- return second;
- }
- else
- {
- if (second->isNull())
- return first;
- else
- return std::make_shared<CombinedFilter>(first, second);
- }
-}
-}
-
-
-#endif //HARD_FILTER_H_825780275842758345
diff --git a/lib/help_provider.h b/lib/help_provider.h
deleted file mode 100644
index 8ddc34c7..00000000
--- a/lib/help_provider.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef HELPPROVIDER_H_INCLUDED
-#define HELPPROVIDER_H_INCLUDED
-
-#ifdef ZEN_WIN
-#include <zen/zstring.h>
-#include <wx/msw/helpchm.h>
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
-#include <wx/html/helpctrl.h>
-#endif
-
-#include "ffs_paths.h"
-
-namespace zen
-{
-//use '/' as path separator!
-void displayHelpEntry(wxWindow* parent);
-void displayHelpEntry(const wxString& section, wxWindow* parent);
-
-
-
-
-
-
-
-
-
-//######################## implementation ########################
-namespace impl
-{
-//finish wxWidgets' job
-#ifdef ZEN_WIN
-class FfsHelpController
-{
-public:
- FfsHelpController()
- {
- chmHlp.Initialize(utfCvrtTo<wxString>(zen::getResourceDir()) + L"FreeFileSync.chm");
- }
-
- void openSection(const wxString& section, wxWindow* parent)
- {
- if (section.empty())
- chmHlp.DisplayContents();
- else
- chmHlp.DisplaySection(replaceCpy(section, L'/', utfCvrtTo<wxString>(FILE_NAME_SEPARATOR)));
- }
-private:
- wxCHMHelpController chmHlp;
-};
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
-class FfsHelpController
-{
-public:
- void openSection(const wxString& section, wxWindow* parent)
- {
- wxHtmlModalHelp dlg(parent, utfCvrtTo<wxString>(zen::getResourceDir()) + L"Help/FreeFileSync.hhp", section,
- wxHF_DEFAULT_STYLE | wxHF_DIALOG | wxHF_MODAL | wxHF_MERGE_BOOKS);
- (void)dlg;
- //-> solves modal help craziness on OSX!
- //-> Suse Linux: avoids program hang on exit if user closed help parent dialog before the help dialog itself was closed (why is this even possible???)
- // avoids ESC key not being recognized by help dialog (but by parent dialog instead)
- }
-};
-#endif
-
-
-inline
-FfsHelpController& getHelpCtrl()
-{
- static FfsHelpController ctrl; //external linkage, despite inline definition!
- return ctrl;
-}
-}
-
-
-inline
-void displayHelpEntry(const wxString& section, wxWindow* parent)
-{
- impl::getHelpCtrl().openSection(section, parent);
-}
-
-
-inline
-void displayHelpEntry(wxWindow* parent)
-{
- impl::getHelpCtrl().openSection(wxString(), parent);
-}
-}
-
-#endif //HELPPROVIDER_H_INCLUDED
diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp
deleted file mode 100644
index e78b308e..00000000
--- a/lib/icon_buffer.cpp
+++ /dev/null
@@ -1,674 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "icon_buffer.h"
-#include <queue>
-#include <set>
-#include <zen/thread.h> //includes <boost/thread.hpp>
-#include <zen/scope_guard.h>
-
-#ifdef ZEN_WIN
-#include <zen/dll.h>
-#include <zen/win_ver.h>
-#include <wx/image.h>
-#include "Thumbnail/thumbnail.h"
-
-#elif defined ZEN_LINUX
-#include <gtk/gtk.h>
-
-#elif defined ZEN_MAC
-#include "osx_file_icon.h"
-#endif
-
-using namespace zen;
-
-
-namespace
-{
-const size_t BUFFER_SIZE_MAX = 600; //maximum number of icons to hold in buffer
-
-#ifndef NDEBUG
-boost::thread::id mainThreadId = boost::this_thread::get_id();
-#endif
-
-#ifdef ZEN_WIN
-const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found"
-
-#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<thumb::FunType_##name>(thumb::getDllName(), thumb::funName_##name) : DllFun<thumb::FunType_##name>();
-DEF_DLL_FUN(getIconByIndex); //
-DEF_DLL_FUN(getThumbnail); //let's spare the boost::call_once hustle and allocate statically
-DEF_DLL_FUN(releaseImageData); //
-#endif
-
-class IconHolder //handle HICON/GdkPixbuf ownership supporting thread-safe usage (in contrast to wxIcon/wxBitmap)
-{
-public:
-#ifdef ZEN_WIN
- typedef const thumb::ImageData* HandleType;
-#elif defined ZEN_LINUX
- typedef GdkPixbuf* HandleType;
-#elif defined ZEN_MAC
- typedef osx::ImageData* HandleType;
-#endif
-
- explicit IconHolder(HandleType handle = nullptr) : handle_(handle) {} //take ownership!
-
- IconHolder(IconHolder&& other) : handle_(other.release()) {}
-
- IconHolder& operator=(IconHolder other) //unifying assignment
- {
- other.swap(*this);
- return *this;
- }
-
- ~IconHolder()
- {
- if (handle_ != nullptr)
-#ifdef ZEN_WIN
- releaseImageData(handle_); //should be checked already before creating IconHolder!
-#elif defined ZEN_LINUX
- ::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"!
-#elif defined ZEN_MAC
- delete handle_;
-#endif
- }
-
- HandleType release()
- {
- ZEN_ON_SCOPE_EXIT(handle_ = nullptr);
- return handle_;
- }
-
- void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw()
-
- //destroys raw icon! Call from GUI thread only!
- wxBitmap extractWxBitmap()
- {
- ZEN_ON_SCOPE_EXIT(assert(!*this));
- assert(boost::this_thread::get_id() == mainThreadId );
-
- if (!handle_)
- return wxNullBitmap;
-
-#ifdef ZEN_WIN
- ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction
-
- //let wxImage reference data without taking ownership:
- wxImage fileIcon(handle_->width, handle_->height, handle_->rgb, true);
- fileIcon.SetAlpha(handle_->alpha, true);
- return wxBitmap(fileIcon);
-
-#elif defined ZEN_LINUX
- return wxBitmap(release()); //ownership passed!
-
-#elif defined ZEN_MAC
- ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction
-
- //let wxImage reference data without taking ownership:
- if (!handle_->rgb.empty())
- {
- wxImage fileIcon(handle_->width, handle_->height, &handle_->rgb[0], true);
- if (!handle_->alpha.empty())
- fileIcon.SetAlpha(&handle_->alpha[0], true);
- return wxBitmap(fileIcon);
- }
- assert(false); //rgb and alpha should never be empty
- return wxBitmap();
-#endif
- }
-
-private:
- HandleType handle_;
-
- IconHolder(const IconHolder& other); //move semantics!
- struct ConversionToBool { int dummy; };
-public:
- //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20)
- operator int ConversionToBool::* () const { return handle_ != nullptr ? &ConversionToBool::dummy : nullptr; }
-};
-
-
-#ifdef ZEN_WIN
-Zstring getFileExtension(const Zstring& filename)
-{
- const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator!
-
- return contains(shortName, Zchar('.')) ?
- afterLast(filename, Zchar('.')) :
- Zstring();
-}
-
-
-std::set<Zstring, LessFilename> priceyExtensions; //thread-safe!
-boost::once_flag initExtensionsOnce = BOOST_ONCE_INIT; //
-
-//test for extension for non-thumbnail icons that physically have to be retrieved from disc
-bool isCheapExtension(const Zstring& extension)
-{
- boost::call_once(initExtensionsOnce, []()
- {
- priceyExtensions.insert(L"exe");
- priceyExtensions.insert(L"ico");
- priceyExtensions.insert(L"ani");
- priceyExtensions.insert(L"cur");
- priceyExtensions.insert(L"msc");
- priceyExtensions.insert(L"scr");
-
- priceyExtensions.insert(L"lnk"); //
- priceyExtensions.insert(L"url"); //make sure shortcuts are pricey to get them to be detected by SHGetFileInfo
- priceyExtensions.insert(L"pif"); //
- priceyExtensions.insert(L"website"); //
-
- });
- return priceyExtensions.find(extension) == priceyExtensions.end();
-}
-
-const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup
-
-
-thumb::IconSizeType getThumbSizeType(IconBuffer::IconSize sz)
-{
- using namespace thumb;
- switch (sz)
- {
- case IconBuffer::SIZE_SMALL:
- return ICON_SIZE_16;
- case IconBuffer::SIZE_MEDIUM:
- if (!wereVistaOrLater) return ICON_SIZE_32; //48x48 doesn't look sharp on XP
- return ICON_SIZE_48;
- case IconBuffer::SIZE_LARGE:
- return ICON_SIZE_128;
- }
- return ICON_SIZE_16;
-}
-
-
-IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffer::IconSize sz)
-{
- //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread!
- SHFILEINFO fileInfo = {}; //initialize hIcon
- DWORD_PTR imgList = ::SHGetFileInfo(pszPath, //Windows 7 doesn't like this parameter to be an empty string
- dwFileAttributes,
- &fileInfo,
- sizeof(fileInfo),
- SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
- if (!imgList) //no need to IUnknown::Release() imgList!
- return IconHolder();
-
- if (getIconByIndex && releaseImageData)
- return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz)));
-
- return IconHolder();
-}
-
-
-IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz)
-{
- //no read-access to disk! determine icon by extension
- return getIconByAttribute((L"dummy." + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz);
-}
-
-#elif defined ZEN_LINUX
-IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz)
-{
- if (gicon)
- if (GtkIconTheme* defaultTheme = ::gtk_icon_theme_get_default()) //not owned!
- if (GtkIconInfo* iconInfo = ::gtk_icon_theme_lookup_by_gicon(defaultTheme, gicon, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN)) //this may fail if icon is not installed on system
- {
- ZEN_ON_SCOPE_EXIT(::gtk_icon_info_free(iconInfo);)
- if (GdkPixbuf* pixBuf = ::gtk_icon_info_load_icon(iconInfo, nullptr))
- return IconHolder(pixBuf); //pass ownership
- }
- return IconHolder();
-}
-#endif
-}
-
-//################################################################################################################################################
-
-IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return 0 on failure
-{
-#ifdef ZEN_WIN
- if (getThumbnail && releaseImageData)
- return IconHolder(getThumbnail(filename.c_str(), requestedSize));
-
-#elif defined ZEN_LINUX
- gint width = 0;
- gint height = 0;
- if (GdkPixbufFormat* fmt = ::gdk_pixbuf_get_file_info(filename.c_str(), &width, &height))
- {
- (void)fmt;
- if (width > 0 && height > 0 && requestedSize > 0)
- {
- int trgWidth = width;
- int trgHeight = height;
-
- const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead!
- if (requestedSize < maxExtent)
- {
- trgWidth = width * requestedSize / maxExtent;
- trgHeight = height * requestedSize / maxExtent;
- }
- if (GdkPixbuf* pixBuf = ::gdk_pixbuf_new_from_file_at_size(filename.c_str(), trgWidth, trgHeight, nullptr))
- return IconHolder(pixBuf); //pass ownership
- }
- }
-
-#elif defined ZEN_MAC
- try
- {
- return IconHolder(new osx::ImageData(osx::getThumbnail(filename.c_str(), requestedSize))); //throw SysError
- }
- catch (zen::SysError&) {}
-#endif
- return IconHolder();
-}
-
-
-IconHolder getGenericFileIcon(IconBuffer::IconSize sz)
-{
- //we're called by getAssociatedIcon()! -> avoid endless recursion!
-#ifdef ZEN_WIN
- return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz);
-
-#elif defined ZEN_LINUX
- const char* mimeFileIcons[] =
- {
- "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes
- "text-x-generic", //http://live.gnome.org/GnomeArt/Tutorials/IconThemes
- "empty", //Ubuntu: /usr/share/icons/Humanity/mimes/48
- GTK_STOCK_FILE, //"gtk-file",
- "gnome-fs-regular", //
- };
-
- if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned!
- for (auto it = std::begin(mimeFileIcons); it != std::end(mimeFileIcons); ++it)
- if (GdkPixbuf* pixBuf = gtk_icon_theme_load_icon(defaultTheme, *it, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN, nullptr))
- return IconHolder(pixBuf); //pass ownership
- return IconHolder();
-
-#elif defined ZEN_MAC
- try
- {
- return IconHolder(new osx::ImageData(osx::getDefaultFileIcon(IconBuffer::getSize(sz)))); //throw SysError
- }
- catch (zen::SysError&) {}
- return IconHolder();
-#endif
-}
-
-
-IconHolder getGenericDirectoryIcon(IconBuffer::IconSize sz)
-{
-#ifdef ZEN_WIN
- return getIconByAttribute(L"dummy", //Windows 7 doesn't like this parameter to be an empty string!
- FILE_ATTRIBUTE_DIRECTORY, sz);
-#elif defined ZEN_LINUX
- if (GIcon* dirIcon = ::g_content_type_get_icon("inode/directory")) //should contain fallback to GTK_STOCK_DIRECTORY ("gtk-directory")
- return iconHolderFromGicon(dirIcon, sz);
- return IconHolder();
-
-#elif defined ZEN_MAC
- try
- {
- return IconHolder(new osx::ImageData(osx::getDefaultFolderIcon(IconBuffer::getSize(sz)))); //throw SysError
- }
- catch (zen::SysError&) { return IconHolder(); }
-#endif
-}
-
-
-IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz)
-{
- //1. try to load thumbnails
- switch (sz)
- {
- case IconBuffer::SIZE_SMALL:
- break;
- case IconBuffer::SIZE_MEDIUM:
- case IconBuffer::SIZE_LARGE:
- if (IconHolder ico = getThumbnailIcon(filename, IconBuffer::getSize(sz)))
- return ico;
- //else: fallback to non-thumbnail icon
- break;
- }
-
- warn_static("problem: für folder links ist getThumbnail erfolgreich => SFGAO_LINK nicht gecheckt!")
-
- //2. retrieve file icons
-#ifdef ZEN_WIN
- //perf: optimize fallback case for SIZE_MEDIUM and SIZE_LARGE:
- const Zstring& extension = getFileExtension(filename);
- if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension
- return getAssociatedIconByExt(extension, sz);
- //result will not be buffered under extension name, but full filename; this is okay, since we're in SIZE_MEDIUM or SIZE_LARGE context,
- //which means the access to get thumbnail failed: thumbnail failure is not dependent from extension in general!
-
- SHFILEINFO fileInfo = {};
- if (DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //_In_ LPCTSTR pszPath, -> note: ::SHGetFileInfo() can't handle \\?\-prefix!
- 0, //DWORD dwFileAttributes,
- &fileInfo, //_Inout_ SHFILEINFO *psfi,
- sizeof(fileInfo), //UINT cbFileInfo,
- SHGFI_SYSICONINDEX /*| SHGFI_ATTRIBUTES*/)) //UINT uFlags
- {
- (void)imgList;
- //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay
- //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP
-
- //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as needed;
- // for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList."
- //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx
-
-#ifdef __MINGW32__ //Shobjidl.h
-#define SFGAO_LINK 0x00010000L // Shortcut (link) or symlinks
-#endif
-
- warn_static("support SFGAO_GHOSTED or hidden?")
- //requires SHGFI_ATTRIBUTES
- //const bool isLink = (fileInfo.dwAttributes & SFGAO_LINK) != 0;
-
- if (getIconByIndex && releaseImageData)
- if (const thumb::ImageData* imgData = getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz)))
- return IconHolder(imgData);
- }
-
-#elif defined ZEN_LINUX
- GFile* file = ::g_file_new_for_path(filename.c_str()); //documented to "never fail"
- ZEN_ON_SCOPE_EXIT(::g_object_unref(file);)
-
- if (GFileInfo* fileInfo = ::g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr))
- {
- ZEN_ON_SCOPE_EXIT(::g_object_unref(fileInfo);)
- if (GIcon* gicon = ::g_file_info_get_icon(fileInfo)) //not owned!
- return iconHolderFromGicon(gicon, sz);
- }
- //need fallback: icon lookup may fail because some icons are currently not present on system
-
-#elif defined ZEN_MAC
- try
- {
- return IconHolder(new osx::ImageData(osx::getFileIcon(filename.c_str(), IconBuffer::getSize(sz)))); //throw SysError
- }
- catch (zen::SysError&) { assert(false); }
-#endif
- return ::getGenericFileIcon(sz); //make sure this does not internally call getAssociatedIcon("someDefaultFile.txt")!!! => endless recursion!
-}
-
-//################################################################################################################################################
-
-//---------------------- Shared Data -------------------------
-class WorkLoad
-{
-public:
- Zstring extractNextFile() //context of worker thread, blocking
- {
- assert(boost::this_thread::get_id() != mainThreadId );
- boost::unique_lock<boost::mutex> dummy(lockFiles);
-
- while (filesToLoad.empty())
- conditionNewFiles.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point!
-
- Zstring fileName = filesToLoad.back();
- filesToLoad.pop_back();
- return fileName;
- }
-
- void setWorkload(const std::list<Zstring>& newLoad) //context of main thread
- {
- assert(boost::this_thread::get_id() == mainThreadId );
- {
- boost::lock_guard<boost::mutex> dummy(lockFiles);
- filesToLoad = newLoad;
- }
- conditionNewFiles.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
- //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
- }
-
- void addToWorkload(const Zstring& newEntry) //context of main thread
- {
- assert(boost::this_thread::get_id() == mainThreadId );
- {
- boost::lock_guard<boost::mutex> dummy(lockFiles);
- filesToLoad.push_back(newEntry); //set as next item to retrieve
- }
- conditionNewFiles.notify_all();
- }
-
-private:
- std::list<Zstring> filesToLoad; //processes last elements of vector first!
- boost::mutex lockFiles;
- boost::condition_variable conditionNewFiles; //signal event: data for processing available
-};
-
-
-class Buffer
-{
-public:
- //called by main and worker thread:
- bool hasFileIcon(const Zstring& fileName) const
- {
- boost::lock_guard<boost::mutex> dummy(lockIconList);
- return iconList.find(fileName) != iconList.end();
- }
-
- //must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!)
- Opt<wxBitmap> retrieveFileIcon(const Zstring& fileName)
- {
- assert(boost::this_thread::get_id() == mainThreadId);
- boost::lock_guard<boost::mutex> dummy(lockIconList);
- auto it = iconList.find(fileName);
- if (it == iconList.end())
- return NoValue();
-
- IconData& idata = it->second;
- if (idata.iconRaw) //if not yet converted...
- {
- idata.iconFmt = make_unique<wxBitmap>(idata.iconRaw.extractWxBitmap()); //convert in main thread!
- assert(!idata.iconRaw);
- }
- return idata.iconFmt ? *idata.iconFmt : wxNullBitmap; //idata.iconRaw may be inserted as empty from worker thread!
- }
-
- //must be called by main thread only! => ~wxBitmap() is NOT thread-safe!
- //call at an appropriate time, e.g. after Workload::setWorkload()
- void limitBufferSize() //critical because GDI resources are limited (e.g. 10000 on XP per process)
- {
- assert(boost::this_thread::get_id() == mainThreadId);
- boost::lock_guard<boost::mutex> dummy(lockIconList);
- while (iconList.size() > BUFFER_SIZE_MAX)
- {
- iconList.erase(iconSequence.front()); //remove oldest element
- iconSequence.pop();
- }
- }
-
- //called by main and worker thread:
- void moveIntoBuffer(const Zstring& entryName, IconHolder&& icon)
- {
- boost::lock_guard<boost::mutex> dummy(lockIconList);
-
- //thread safety: moving IconHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here!
- auto rc = iconList.insert(std::make_pair(entryName, IconData(std::move(icon))));
- if (rc.second) //if insertion took place
- iconSequence.push(entryName); //note: sharing Zstring with IconDB!!!
-
- assert(iconList.size() == iconSequence.size());
- }
-
-private:
- struct IconData
- {
- IconData(IconHolder&& tmp) : iconRaw(std::move(tmp)) {}
- IconData(IconData&& tmp) : iconRaw(std::move(tmp.iconRaw)), iconFmt(std::move(tmp.iconFmt)) {}
-
- IconHolder iconRaw; //native icon representation: may be used by any thread
-
- std::unique_ptr<wxBitmap> iconFmt; //use ONLY from main thread!
- //wxBitmap is NOT thread-safe: non-atomic ref-count just to begin with...
- //- prohibit implicit calls to wxBitmap(const wxBitmap&)
- //- prohibit calls to ~wxBitmap() and transitively ~IconData()
- //- prohibit even wxBitmap() default constructor - better be safe than sorry!
- };
-
- mutable boost::mutex lockIconList;
- std::map<Zstring, IconData, LessFilename> iconList; //shared resource; Zstring is thread-safe like an int
- std::queue<Zstring> iconSequence; //save sequence of buffer entry to delete oldest elements
-};
-
-//################################################################################################################################################
-
-class WorkerThread //lifetime is part of icon buffer
-{
-public:
- WorkerThread(const std::shared_ptr<WorkLoad>& workload,
- const std::shared_ptr<Buffer>& buffer,
- IconBuffer::IconSize st) :
- workload_(workload),
- buffer_(buffer),
- iconSizeType(st) {}
-
- void operator()(); //thread entry
-
-private:
- std::shared_ptr<WorkLoad> workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!)
- std::shared_ptr<Buffer> buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety
- const IconBuffer::IconSize iconSizeType;
-};
-
-
-void WorkerThread::operator()() //thread entry
-{
- //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080
-#ifdef ZEN_WIN
- //Prerequisites, see thumbnail.h
-
- //1. Initialize COM
- ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
- ZEN_ON_SCOPE_EXIT(::CoUninitialize());
-
- //2. Initialize system image list
- typedef BOOL (WINAPI* FileIconInitFun)(BOOL fRestoreCache);
- const SysDllFun<FileIconInitFun> fileIconInit(L"Shell32.dll", reinterpret_cast<LPCSTR>(660)); //MS requires and documents this magic number
- assert(fileIconInit);
- if (fileIconInit)
- fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise.
-#endif
-
- while (true)
- {
- boost::this_thread::interruption_point();
-
- const Zstring fileName = workload_->extractNextFile(); //start work: blocks until next icon to load is retrieved
-
- if (!buffer_->hasFileIcon(fileName)) //perf: workload may contain duplicate entries?
- buffer_->moveIntoBuffer(fileName, getAssociatedIcon(fileName, iconSizeType));
- }
-}
-
-//######################### redirect to impl #####################################################
-
-struct IconBuffer::Pimpl
-{
- Pimpl() :
- workload(std::make_shared<WorkLoad>()),
- buffer (std::make_shared<Buffer>()) {}
-
- std::shared_ptr<WorkLoad> workload;
- std::shared_ptr<Buffer> buffer;
-
- boost::thread worker;
-};
-
-
-IconBuffer::IconBuffer(IconSize sz) : pimpl(make_unique<Pimpl>()),
- iconSizeType(sz),
- genDirIcon (::getGenericDirectoryIcon(sz).extractWxBitmap()),
- genFileIcon(::getGenericFileIcon (sz).extractWxBitmap())
-{
- pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer, sz));
-}
-
-
-IconBuffer::~IconBuffer()
-{
- setWorkload(std::list<Zstring>()); //make sure interruption point is always reached!
- pimpl->worker.interrupt();
- pimpl->worker.join(); //we assume precondition "worker.joinable()"!!!
-}
-
-
-int IconBuffer::getSize(IconSize icoSize)
-{
- switch (icoSize)
- {
- case IconBuffer::SIZE_SMALL:
-#if defined ZEN_WIN || defined ZEN_MAC
- return 16;
-#elif defined ZEN_LINUX
- return 24;
-#endif
- case IconBuffer::SIZE_MEDIUM:
-#ifdef ZEN_WIN
- if (!wereVistaOrLater) return 32; //48x48 doesn't look sharp on XP
-#endif
- return 48;
-
- case IconBuffer::SIZE_LARGE:
- return 128;
- }
- assert(false);
- return 0;
-}
-
-
-bool IconBuffer::readyForRetrieval(const Zstring& filename)
-{
-#ifdef ZEN_WIN
- if (iconSizeType == IconBuffer::SIZE_SMALL)
- if (isCheapExtension(getFileExtension(filename)))
- return true;
-#endif
- return pimpl->buffer->hasFileIcon(filename);
-}
-
-
-Opt<wxBitmap> IconBuffer::retrieveFileIcon(const Zstring& filename)
-{
-#ifdef ZEN_WIN
- //perf: let's read icons which don't need file access right away! No async delay justified!
- if (iconSizeType == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only!
- {
- const Zstring& extension = getFileExtension(filename);
- if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension
- {
- if (Opt<wxBitmap> ico = pimpl->buffer->retrieveFileIcon(extension))
- return ico;
-
- //make sure icon is in buffer, even if icon needs not be retrieved!
- pimpl->buffer->moveIntoBuffer(extension, getAssociatedIconByExt(extension, iconSizeType));
-
- Opt<wxBitmap> ico = pimpl->buffer->retrieveFileIcon(extension);
- assert(ico);
- return ico;
- }
- }
-#endif
-
- if (Opt<wxBitmap> ico = pimpl->buffer->retrieveFileIcon(filename))
- return ico;
-
- //since this icon seems important right now, we don't want to wait until next setWorkload() to start retrieving
- pimpl->workload->addToWorkload(filename);
- pimpl->buffer->limitBufferSize();
- return NoValue();
-}
-
-
-void IconBuffer::setWorkload(const std::list<Zstring>& load)
-{
- pimpl->workload->setWorkload(load); //since buffer can only increase due to new workload,
- pimpl->buffer->limitBufferSize(); //this is the place to impose the limit from main thread!
-}
diff --git a/lib/icon_buffer.h b/lib/icon_buffer.h
deleted file mode 100644
index efa5179f..00000000
--- a/lib/icon_buffer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef ICONBUFFER_H_INCLUDED
-#define ICONBUFFER_H_INCLUDED
-
-#include <list>
-#include <memory>
-#include <zen/zstring.h>
-#include <zen/optional.h>
-#include <wx/bitmap.h>
-
-namespace zen
-{
-class IconBuffer
-{
-public:
- enum IconSize
- {
- SIZE_SMALL,
- SIZE_MEDIUM,
- SIZE_LARGE
- };
-
- IconBuffer(IconSize sz);
- ~IconBuffer();
-
- static int getSize(IconSize icoSizeType); //expected and *maximum* icon size in pixel
- int getSize() const { return getSize(iconSizeType); } //
-
- const wxBitmap& genericFileIcon() { return genFileIcon; }
- const wxBitmap& genericDirIcon () { return genDirIcon; }
-
- bool readyForRetrieval(const Zstring& filename);
- Opt<wxBitmap> retrieveFileIcon(const Zstring& filename);
-
- void setWorkload(const std::list<Zstring>& load); //(re-)set new workload of icons to be retrieved;
-
-private:
- struct Pimpl;
- std::unique_ptr<Pimpl> pimpl;
-
- const IconSize iconSizeType;
- const wxBitmap genDirIcon;
- const wxBitmap genFileIcon;
-};
-}
-
-#endif // ICONBUFFER_H_INCLUDED
diff --git a/lib/localization.cpp b/lib/localization.cpp
deleted file mode 100644
index 3536ba70..00000000
--- a/lib/localization.cpp
+++ /dev/null
@@ -1,485 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "localization.h"
-#include <map>
-#include <list>
-#include <iterator>
-#include <zen/string_tools.h>
-#include <zen/file_traverser.h>
-#include <zen/serialize.h>
-#include <zen/i18n.h>
-#include <zen/format_unit.h>
-#include <wx/intl.h>
-#include <wx/log.h>
-#include "parse_plural.h"
-#include "parse_lng.h"
-#include "ffs_paths.h"
-
-#ifdef ZEN_LINUX
-#include <wchar.h> //wcscasecmp
-
-#elif defined ZEN_MAC
-#include <CoreServices/CoreServices.h>
-#endif
-
-using namespace zen;
-
-
-namespace
-{
-class FFSTranslation : public TranslationHandler
-{
-public:
- FFSTranslation(const Zstring& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError
-
- wxLanguage langId() const { return langId_; }
-
- virtual std::wstring translate(const std::wstring& text) override
- {
- //look for translation in buffer table
- auto it = transMapping.find(text);
- if (it != transMapping.end() && !it->second.empty())
- return it->second;
- return text; //fallback
- }
-
- virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) override
- {
- auto it = transMappingPl.find(std::make_pair(singular, plural));
- if (it != transMappingPl.end())
- {
- const size_t formNo = pluralParser->getForm(n);
- if (formNo < it->second.size())
- return replaceCpy(it->second[formNo], L"%x", toGuiString(n));
- }
- return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); //fallback
- }
-
-private:
- typedef hash_map<std::wstring, std::wstring> Translation; //hash_map is 15% faster than std::map on GCC
- typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring>> TranslationPlural;
-
- Translation transMapping; //map original text |-> translation
- TranslationPlural transMappingPl;
- std::unique_ptr<parse_plural::PluralForm> pluralParser; //bound!
- wxLanguage langId_;
-};
-
-
-FFSTranslation::FFSTranslation(const Zstring& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError
-{
- std::string inputStream;
- try
- {
- inputStream = loadBinStream<std::string>(filename); //throw FileError
- }
- catch (const FileError& e)
- {
- throw lngfile::ParsingError(e.toString(), 0, 0); //passing FileError is too high a level for Parsing error, OTOH user is unlikely to see this since file I/O issues are sorted out by ExistingTranslations()!
- }
-
- lngfile::TransHeader header;
- lngfile::TranslationMap transInput;
- lngfile::TranslationPluralMap transPluralInput;
- lngfile::parseLng(inputStream, header, transInput, transPluralInput); //throw ParsingError
-
- for (const auto& item : transInput)
- {
- const std::wstring original = utfCvrtTo<std::wstring>(item.first);
- const std::wstring translation = utfCvrtTo<std::wstring>(item.second);
- transMapping.insert(std::make_pair(original, translation));
- }
-
- for (const auto& item : transPluralInput)
- {
- const std::wstring engSingular = utfCvrtTo<std::wstring>(item.first.first);
- const std::wstring engPlural = utfCvrtTo<std::wstring>(item.first.second);
-
- std::vector<std::wstring> plFormsWide;
- for (const std::string& pf : item.second)
- plFormsWide.push_back(utfCvrtTo<std::wstring>(pf));
-
- transMappingPl.insert(std::make_pair(std::make_pair(engSingular, engPlural), plFormsWide));
- }
-
- pluralParser = make_unique<parse_plural::PluralForm>(header.pluralDefinition); //throw parse_plural::ParsingError
-}
-
-
-class FindLngfiles : public zen::TraverseCallback
-{
-public:
- FindLngfiles(std::vector<Zstring>& lngFiles) : lngFiles_(lngFiles) {}
-
- virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details)
- {
- if (endsWith(fullName, Zstr(".lng")))
- lngFiles_.push_back(fullName);
- }
-
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; }
- virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) { return nullptr; }
- virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context
- virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } //
-
-private:
- std::vector<Zstring>& lngFiles_;
-};
-
-
-struct LessTranslation
-{
- bool operator()(const ExistingTranslations::Entry& lhs, const ExistingTranslations::Entry& rhs) const
- {
- //use a more "natural" sort: ignore case and diacritics
-#ifdef ZEN_WIN
- const int rv = ::CompareString(LOCALE_USER_DEFAULT, //__in LCID Locale,
- NORM_IGNORECASE, //__in DWORD dwCmpFlags,
- lhs.languageName.c_str(), //__in LPCTSTR lpString1,
- static_cast<int>(lhs.languageName.size()), //__in int cchCount1,
- rhs.languageName.c_str(), //__in LPCTSTR lpString2,
- static_cast<int>(rhs.languageName.size())); //__in int cchCount2
- if (rv == 0)
- throw std::runtime_error("Error comparing strings.");
- else
- return rv == CSTR_LESS_THAN; //convert to C-style string compare result
-
-#elif defined ZEN_LINUX
- return ::wcscasecmp(lhs.languageName.c_str(), rhs.languageName.c_str()) < 0; //ignores case; locale-dependent!
- //return lhs.languageName.CmpNoCase(rhs.languageName) < 0;
-
-#elif defined ZEN_MAC
- auto allocCFStringRef = [](const std::wstring& str) -> CFStringRef //output not owned!
- {
- return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc,
- utfCvrtTo<std::string>(str).c_str(), //const char *cStr,
- kCFStringEncodingUTF8); //CFStringEncoding encoding
- };
-
- CFStringRef langL = allocCFStringRef(lhs.languageName);
- ZEN_ON_SCOPE_EXIT(::CFRelease(langL));
-
- CFStringRef langR = allocCFStringRef(rhs.languageName);
- ZEN_ON_SCOPE_EXIT(::CFRelease(langR));
-
- return::CFStringCompare(langL, langR, kCFCompareLocalized | kCFCompareCaseInsensitive) == kCFCompareLessThan; //no-fail
-#endif
- }
-};
-}
-
-
-ExistingTranslations::ExistingTranslations()
-{
- {
- //default entry:
- ExistingTranslations::Entry newEntry;
- newEntry.languageID = wxLANGUAGE_ENGLISH_US;
- newEntry.languageName = L"English (US)";
- newEntry.languageFile = L"";
- newEntry.translatorName = L"Zenju";
- newEntry.languageFlag = L"flag_usa.png";
- locMapping.push_back(newEntry);
- }
-
- //search language files available
- std::vector<Zstring> lngFiles;
- FindLngfiles traverseCallback(lngFiles);
-
- traverseFolder(zen::getResourceDir() + Zstr("Languages"), //throw();
- traverseCallback);
-
- for (const Zstring& filename : lngFiles)
- {
- try
- {
- const std::string stream = loadBinStream<std::string>(filename); //throw FileError
-
- lngfile::TransHeader lngHeader;
- lngfile::parseHeader(stream, lngHeader); //throw ParsingError
-
- assert(!lngHeader.languageName .empty());
- assert(!lngHeader.translatorName.empty());
- assert(!lngHeader.localeName .empty());
- assert(!lngHeader.flagFile .empty());
- /*
- There is some buggy behavior in wxWidgets which maps "zh_TW" to simplified chinese.
- Fortunately locales can be also entered as description. I changed to "Chinese (Traditional)" which works fine.
- */
- if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utfCvrtTo<wxString>(lngHeader.localeName)))
- {
- ExistingTranslations::Entry newEntry;
- newEntry.languageID = locInfo->Language;
- newEntry.languageName = utfCvrtTo<std::wstring>(lngHeader.languageName);
- newEntry.languageFile = utfCvrtTo<std::wstring>(filename);
- newEntry.translatorName = utfCvrtTo<std::wstring>(lngHeader.translatorName);
- newEntry.languageFlag = utfCvrtTo<std::wstring>(lngHeader.flagFile);
- locMapping.push_back(newEntry);
- }
- else assert(false);
- }
- catch (FileError&) { assert(false); }
- catch (lngfile::ParsingError&) { assert(false); } //better not show an error message here; scenario: batch jobs
- }
-
- std::sort(locMapping.begin(), locMapping.end(), LessTranslation());
-}
-
-
-const std::vector<ExistingTranslations::Entry>& ExistingTranslations::get()
-{
- static ExistingTranslations instance;
- return instance.locMapping;
-}
-
-
-namespace
-{
-wxLanguage mapLanguageDialect(wxLanguage language)
-{
- switch (static_cast<int>(language)) //avoid enumeration value wxLANGUAGE_*' not handled in switch [-Wswitch-enum]
- {
- //variants of wxLANGUAGE_ARABIC
- case wxLANGUAGE_ARABIC_ALGERIA:
- case wxLANGUAGE_ARABIC_BAHRAIN:
- case wxLANGUAGE_ARABIC_EGYPT:
- case wxLANGUAGE_ARABIC_IRAQ:
- case wxLANGUAGE_ARABIC_JORDAN:
- case wxLANGUAGE_ARABIC_KUWAIT:
- case wxLANGUAGE_ARABIC_LEBANON:
- case wxLANGUAGE_ARABIC_LIBYA:
- case wxLANGUAGE_ARABIC_MOROCCO:
- case wxLANGUAGE_ARABIC_OMAN:
- case wxLANGUAGE_ARABIC_QATAR:
- case wxLANGUAGE_ARABIC_SAUDI_ARABIA:
- case wxLANGUAGE_ARABIC_SUDAN:
- case wxLANGUAGE_ARABIC_SYRIA:
- case wxLANGUAGE_ARABIC_TUNISIA:
- case wxLANGUAGE_ARABIC_UAE:
- case wxLANGUAGE_ARABIC_YEMEN:
- return wxLANGUAGE_ARABIC;
-
- //variants of wxLANGUAGE_CHINESE_SIMPLIFIED
- case wxLANGUAGE_CHINESE:
- case wxLANGUAGE_CHINESE_SINGAPORE:
- return wxLANGUAGE_CHINESE_SIMPLIFIED;
-
- //variants of wxLANGUAGE_CHINESE_TRADITIONAL
- case wxLANGUAGE_CHINESE_TAIWAN:
- case wxLANGUAGE_CHINESE_HONGKONG:
- case wxLANGUAGE_CHINESE_MACAU:
- return wxLANGUAGE_CHINESE_TRADITIONAL;
-
- //variants of wxLANGUAGE_DUTCH
- case wxLANGUAGE_DUTCH_BELGIAN:
- return wxLANGUAGE_DUTCH;
-
- //variants of wxLANGUAGE_ENGLISH_UK
- case wxLANGUAGE_ENGLISH_AUSTRALIA:
- case wxLANGUAGE_ENGLISH_NEW_ZEALAND:
- case wxLANGUAGE_ENGLISH_TRINIDAD:
- case wxLANGUAGE_ENGLISH_CARIBBEAN:
- case wxLANGUAGE_ENGLISH_JAMAICA:
- case wxLANGUAGE_ENGLISH_BELIZE:
- case wxLANGUAGE_ENGLISH_EIRE:
- case wxLANGUAGE_ENGLISH_SOUTH_AFRICA:
- case wxLANGUAGE_ENGLISH_ZIMBABWE:
- case wxLANGUAGE_ENGLISH_BOTSWANA:
- case wxLANGUAGE_ENGLISH_DENMARK:
- return wxLANGUAGE_ENGLISH_UK;
-
- //variants of wxLANGUAGE_ENGLISH_US
- case wxLANGUAGE_ENGLISH:
- case wxLANGUAGE_ENGLISH_CANADA:
- case wxLANGUAGE_ENGLISH_PHILIPPINES:
- return wxLANGUAGE_ENGLISH_US;
-
- //variants of wxLANGUAGE_FRENCH
- case wxLANGUAGE_FRENCH_BELGIAN:
- case wxLANGUAGE_FRENCH_CANADIAN:
- case wxLANGUAGE_FRENCH_LUXEMBOURG:
- case wxLANGUAGE_FRENCH_MONACO:
- case wxLANGUAGE_FRENCH_SWISS:
- return wxLANGUAGE_FRENCH;
-
- //variants of wxLANGUAGE_GERMAN
- case wxLANGUAGE_GERMAN_AUSTRIAN:
- case wxLANGUAGE_GERMAN_BELGIUM:
- case wxLANGUAGE_GERMAN_LIECHTENSTEIN:
- case wxLANGUAGE_GERMAN_LUXEMBOURG:
- case wxLANGUAGE_GERMAN_SWISS:
- return wxLANGUAGE_GERMAN;
-
- //variants of wxLANGUAGE_ITALIAN
- case wxLANGUAGE_ITALIAN_SWISS:
- return wxLANGUAGE_ITALIAN;
-
- //variants of wxLANGUAGE_NORWEGIAN_BOKMAL
- case wxLANGUAGE_NORWEGIAN_NYNORSK:
- return wxLANGUAGE_NORWEGIAN_BOKMAL;
-
- //variants of wxLANGUAGE_ROMANIAN
- case wxLANGUAGE_MOLDAVIAN:
- return wxLANGUAGE_ROMANIAN;
-
- //variants of wxLANGUAGE_RUSSIAN
- case wxLANGUAGE_RUSSIAN_UKRAINE:
- return wxLANGUAGE_RUSSIAN;
-
- //variants of wxLANGUAGE_SERBIAN
- case wxLANGUAGE_SERBIAN_CYRILLIC:
- case wxLANGUAGE_SERBIAN_LATIN:
- case wxLANGUAGE_SERBO_CROATIAN:
- return wxLANGUAGE_SERBIAN;
-
- //variants of wxLANGUAGE_SPANISH
- case wxLANGUAGE_SPANISH_ARGENTINA:
- case wxLANGUAGE_SPANISH_BOLIVIA:
- case wxLANGUAGE_SPANISH_CHILE:
- case wxLANGUAGE_SPANISH_COLOMBIA:
- case wxLANGUAGE_SPANISH_COSTA_RICA:
- case wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC:
- case wxLANGUAGE_SPANISH_ECUADOR:
- case wxLANGUAGE_SPANISH_EL_SALVADOR:
- case wxLANGUAGE_SPANISH_GUATEMALA:
- case wxLANGUAGE_SPANISH_HONDURAS:
- case wxLANGUAGE_SPANISH_MEXICAN:
- case wxLANGUAGE_SPANISH_MODERN:
- case wxLANGUAGE_SPANISH_NICARAGUA:
- case wxLANGUAGE_SPANISH_PANAMA:
- case wxLANGUAGE_SPANISH_PARAGUAY:
- case wxLANGUAGE_SPANISH_PERU:
- case wxLANGUAGE_SPANISH_PUERTO_RICO:
- case wxLANGUAGE_SPANISH_URUGUAY:
- case wxLANGUAGE_SPANISH_US:
- case wxLANGUAGE_SPANISH_VENEZUELA:
- return wxLANGUAGE_SPANISH;
-
- //variants of wxLANGUAGE_SWEDISH
- case wxLANGUAGE_SWEDISH_FINLAND:
- return wxLANGUAGE_SWEDISH;
-
- //languages without variants:
- //case wxLANGUAGE_CROATIAN:
- //case wxLANGUAGE_CZECH:
- //case wxLANGUAGE_DANISH:
- //case wxLANGUAGE_FINNISH:
- //case wxLANGUAGE_GREEK:
- //case wxLANGUAGE_HEBREW:
- //case wxLANGUAGE_HUNGARIAN:
- //case wxLANGUAGE_JAPANESE:
- //case wxLANGUAGE_KOREAN:
- //case wxLANGUAGE_LITHUANIAN:
- //case wxLANGUAGE_POLISH:
- //case wxLANGUAGE_PORTUGUESE:
- //case wxLANGUAGE_PORTUGUESE_BRAZILIAN:
- //case wxLANGUAGE_SCOTS_GAELIC:
- //case wxLANGUAGE_SLOVENIAN:
- //case wxLANGUAGE_TURKISH:
- //case wxLANGUAGE_UKRAINIAN:
- default:
- return language;
- }
-}
-
-
-//global wxWidgets localization: sets up C localization runtime as well!
-class wxWidgetsLocale
-{
-public:
- static void init(wxLanguage lng)
- {
- locale.reset(); //avoid global locale lifetime overlap! wxWidgets cannot handle this and will crash!
- locale.reset(new wxLocale);
-
- const wxLanguageInfo* sysLngInfo = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage());
- const wxLanguageInfo* selLngInfo = wxLocale::GetLanguageInfo(lng);
-
- const bool sysLangIsRTL = sysLngInfo ? sysLngInfo->LayoutDirection == wxLayout_RightToLeft : false;
- const bool selectedLangIsRTL = selLngInfo ? selLngInfo->LayoutDirection == wxLayout_RightToLeft : false;
-
-#ifdef NDEBUG
- wxLogNull dummy; //rather than implementing a reasonable error handling wxWidgets decides to shows a modal dialog in wxLocale::Init -> at least we can shut it up!
-#endif
- if (sysLangIsRTL == selectedLangIsRTL)
- locale->Init(wxLANGUAGE_DEFAULT); //use sys-lang to preserve sub-language specific rules (e.g. german swiss number punctation)
- else
- locale->Init(lng); //have to use the supplied language to enable RTL layout different than user settings
- locLng = lng;
- }
-
- static void release() { locale.reset(); locLng = wxLANGUAGE_UNKNOWN; }
-
- static wxLanguage getLanguage() { return locLng; }
-
-private:
- static std::unique_ptr<wxLocale> locale;
- static wxLanguage locLng;
-};
-std::unique_ptr<wxLocale> wxWidgetsLocale::locale;
-wxLanguage wxWidgetsLocale::locLng = wxLANGUAGE_UNKNOWN;
-}
-
-
-void zen::releaseWxLocale()
-{
- wxWidgetsLocale::release();
-}
-
-
-void zen::setLanguage(int language) //throw FileError
-{
- if (language == getLanguage() && wxWidgetsLocale::getLanguage() == language)
- return; //support polling
-
- //(try to) retrieve language file
- std::wstring languageFile;
-
- for (auto it = ExistingTranslations::get().begin(); it != ExistingTranslations::get().end(); ++it)
- if (it->languageID == language)
- {
- languageFile = it->languageFile;
- break;
- }
-
- //load language file into buffer
- if (languageFile.empty()) //if languageFile is empty, texts will be english by default
- zen::setTranslator();
- else
- try
- {
- zen::setTranslator(new FFSTranslation(utfCvrtTo<Zstring>(languageFile), static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError
- }
- catch (lngfile::ParsingError& e)
- {
- throw FileError(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."),
- L"%x", fmtFileName(utfCvrtTo<Zstring>(languageFile))),
- L"%y", numberTo<std::wstring>(e.row_ + 1)),
- L"%z", numberTo<std::wstring>(e.col_ + 1))
- + L"\n\n" + e.msg_);
- }
- catch (parse_plural::ParsingError&)
- {
- throw FileError(L"Invalid plural form definition"); //user should never see this!
- }
-
- //handle RTL swapping: we need wxWidgets to do this
- wxWidgetsLocale::init(languageFile.empty() ? wxLANGUAGE_ENGLISH : static_cast<wxLanguage>(language));
-}
-
-
-int zen::getLanguage()
-{
- const FFSTranslation* loc = dynamic_cast<const FFSTranslation*>(zen::getTranslator());
- return loc ? loc->langId() : wxLANGUAGE_ENGLISH_US;
-}
-
-
-int zen::retrieveSystemLanguage()
-{
- return mapLanguageDialect(static_cast<wxLanguage>(wxLocale::GetSystemLanguage()));
-}
diff --git a/lib/localization.h b/lib/localization.h
deleted file mode 100644
index 2d871dd7..00000000
--- a/lib/localization.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef LOCALIZATION_H_8917342083178321534
-#define LOCALIZATION_H_8917342083178321534
-
-#include <vector>
-#include <zen/file_error.h>
-//#include <wx/string.h>
-
-namespace zen
-{
-class ExistingTranslations
-{
-public:
- struct Entry
- {
- int languageID;
- std::wstring languageName;
- std::wstring languageFile;
- std::wstring translatorName;
- std::wstring languageFlag;
- };
- static const std::vector<Entry>& get();
-
-private:
- ExistingTranslations();
- ExistingTranslations(const ExistingTranslations&);
- ExistingTranslations& operator=(const ExistingTranslations&);
-
- std::vector<Entry> locMapping;
-};
-
-
-void setLanguage(int language); //throw FileError
-int getLanguage();
-int retrieveSystemLanguage();
-
-void releaseWxLocale(); //wxLocale crashes miserably on wxGTK when destructor runs during global cleanup => call in wxApp::OnExit
-//"You should delete all wxWidgets object that you created by the time OnExit finishes. In particular, do not destroy them from application class' destructor!"
-}
-
-#endif //LOCALIZATION_H_8917342083178321534
diff --git a/lib/lock_holder.h b/lib/lock_holder.h
deleted file mode 100644
index 81b5632d..00000000
--- a/lib/lock_holder.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef LOCK_HOLDER_H_489572039485723453425
-#define LOCK_HOLDER_H_489572039485723453425
-
-#include <set>
-#include <zen/zstring.h>
-#include <zen/stl_tools.h>
-#include "dir_lock.h"
-#include "status_handler.h"
-//#include "dir_exist_async.h"
-
-namespace zen
-{
-const Zstring LOCK_FILE_ENDING = Zstr(".ffs_lock"); //intermediate locks created by DirLock use this extension, too!
-
-//hold locks for a number of directories without blocking during lock creation
-//call after having checked directory existence!
-class LockHolder
-{
-public:
- LockHolder(const std::set<Zstring, LessFilename>& dirnamesExisting, //resolved dirname ending with path separator
- bool& warningDirectoryLockFailed,
- ProcessCallback& procCallback)
- {
- for (auto it = dirnamesExisting.begin(); it != dirnamesExisting.end(); ++it)
- {
- const Zstring& dirnameFmt = *it;
- assert(endsWith(dirnameFmt, FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution
-
- class WaitOnLockHandler : public DirLockCallback
- {
- public:
- WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {}
- virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions
- virtual void reportStatus(const std::wstring& text) { pc_.reportStatus(text); }
- private:
- ProcessCallback& pc_;
- } callback(procCallback);
-
- try
- {
- //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages)
- lockHolder.push_back(DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback)); //throw FileError
- }
- catch (const FileError& e)
- {
- const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtFileName(dirnameFmt)) + L"\n\n" + e.toString();
- procCallback.reportWarning(msg, warningDirectoryLockFailed); //may throw!
- }
- }
- }
-
-private:
- std::vector<DirLock> lockHolder;
-};
-}
-
-#endif //LOCK_HOLDER_H_489572039485723453425
diff --git a/lib/norm_filter.h b/lib/norm_filter.h
deleted file mode 100644
index 552931e2..00000000
--- a/lib/norm_filter.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef NORM_FILTER_H_INCLUDED
-#define NORM_FILTER_H_INCLUDED
-
-#include "hard_filter.h"
-#include "soft_filter.h"
-
-namespace zen
-{
-struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use
-{
- NormalizedFilter(const HardFilter::FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {}
-
- //"hard" filter: relevant during comparison, physically skips files
- HardFilter::FilterRef nameFilter;
- //"soft" filter: relevant after comparison; equivalent to user selection
- SoftFilter timeSizeFilter;
-};
-
-
-//combine global and local filters via "logical and"
-NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local);
-
-inline
-bool isNullFilter(const FilterConfig& filterCfg)
-{
- return NameFilter::isNull(filterCfg.includeFilter, filterCfg.excludeFilter) &&
- SoftFilter(filterCfg.timeSpan, filterCfg.unitTimeSpan,
- filterCfg.sizeMin, filterCfg.unitSizeMin,
- filterCfg.sizeMax, filterCfg.unitSizeMax).isNull();
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-// ----------------------- implementation -----------------------
-inline
-NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local)
-{
- HardFilter::FilterRef globalName = std::make_shared<NameFilter>(global.includeFilter, global.excludeFilter);
- HardFilter::FilterRef localName = std::make_shared<NameFilter>(local .includeFilter, local .excludeFilter);
-
- SoftFilter globalTimeSize(global.timeSpan, global.unitTimeSpan,
- global.sizeMin, global.unitSizeMin,
- global.sizeMax, global.unitSizeMax);
-
- SoftFilter localTimeSize(local.timeSpan, local.unitTimeSpan,
- local.sizeMin, local.unitSizeMin,
- local.sizeMax, local.unitSizeMax);
-
- return NormalizedFilter(combineFilters(globalName, localName),
- combineFilters(globalTimeSize, localTimeSize));
-}
-}
-
-#endif //NORM_FILTER_H_INCLUDED
diff --git a/lib/osx_file_icon.h b/lib/osx_file_icon.h
deleted file mode 100644
index 5edfd740..00000000
--- a/lib/osx_file_icon.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef OSX_FILE_ICON_8427508422345342
-#define OSX_FILE_ICON_8427508422345342
-
-#include <vector>
-#include <zen/sys_error.h>
-
-namespace osx
-{
-struct ImageData
-{
- ImageData(int w, int h) : width(w), height(h), rgb(w* h * 3), alpha(w* h) {}
- ImageData(ImageData&& tmp) : width(tmp.width), height(tmp.height) { rgb.swap(tmp.rgb); alpha.swap(tmp.alpha); }
-
- const int width;
- const int height;
- std::vector<unsigned char> rgb; //rgb-byte order for use with wxImage
- std::vector<unsigned char> alpha;
-
-private:
- ImageData(const ImageData&);
- ImageData& operator=(const ImageData&);
-};
-
-ImageData getThumbnail(const char* filename, int requestedSize); //throw SysError
-ImageData getFileIcon (const char* filename, int requestedSize); //throw SysError
-ImageData getDefaultFileIcon (int requestedSize); //throw SysError
-ImageData getDefaultFolderIcon(int requestedSize); //throw SysError
-}
-
-#endif //OSX_FILE_ICON_8427508422345342
diff --git a/lib/osx_file_icon.mm b/lib/osx_file_icon.mm
deleted file mode 100644
index fb3c6490..00000000
--- a/lib/osx_file_icon.mm
+++ /dev/null
@@ -1,179 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "osx_file_icon.h"
-#include <zen/osx_throw_exception.h>
-#include <zen/scope_guard.h>
-#include <zen/basic_math.h>
-
-namespace
-{
-osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw SysError; NSException?
-{
- /*
- wxBitmap(NSImage*) is not good enough: it calls "[NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]"
- => inefficient: TIFFRepresentation converts all contained images of an NSImage
- => lacking: imageRepWithData extracts the first contained image only!
- => wxBitmap(NSImage*) is wxCocoa only, deprecated!
- => wxWidgets generally is not thread-safe so care must be taken to use wxBitmap from main thread only! (e.g. race-condition on non-atomic ref-count!!!)
-
- -> we need only a single bitmap at a specific size => extract raw bytes for use with wxImage in a thread-safe way!
- */
-
- //we choose the Core Graphics solution; for the equivalent App-Kit way see: http://www.cocoabuilder.com/archive/cocoa/193131-is-lockfocus-main-thread-specific.html#193191
-
- ZEN_OSX_ASSERT(requestedSize > 0);
- NSRect rectProposed = NSMakeRect(0, 0, requestedSize, requestedSize); //this is merely a hint!
-
- CGImageRef imgRef = [nsImg CGImageForProposedRect:&rectProposed context:nil hints:nil];
- ZEN_OSX_ASSERT(imgRef != NULL); //can this fail? not documented; ownership?? not documented!
-
- const size_t width = ::CGImageGetWidth (imgRef);
- const size_t height = ::CGImageGetHeight(imgRef);
-
- ZEN_OSX_ASSERT(width > 0 && height > 0 && requestedSize > 0);
-
- int trgWidth = width;
- int trgHeight = height;
-
- const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead!
- if (requestedSize < maxExtent)
- {
- trgWidth = width * requestedSize / maxExtent;
- trgHeight = height * requestedSize / maxExtent;
- }
-
- CGColorSpaceRef colorSpace = ::CGColorSpaceCreateDeviceRGB();
- ZEN_OSX_ASSERT(colorSpace != NULL); //may fail
- ZEN_ON_SCOPE_EXIT(::CGColorSpaceRelease(colorSpace));
-
- std::vector<unsigned char> buf(trgWidth* trgHeight * 4); //32-bit ARGB, little endian byte order -> already initialized with 0 = fully transparent
-
- //supported color spaces: https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
- CGContextRef ctxRef = ::CGBitmapContextCreate(&buf[0], //void *data,
- trgWidth, //size_t width,
- trgHeight, //size_t height,
- 8, //size_t bitsPerComponent,
- 4 * trgWidth, //size_t bytesPerRow,
- colorSpace, //CGColorSpaceRef colorspace,
- kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); //CGBitmapInfo bitmapInfo
- ZEN_OSX_ASSERT(ctxRef != NULL);
- ZEN_ON_SCOPE_EXIT(::CGContextRelease(ctxRef));
-
- ::CGContextDrawImage(ctxRef, CGRectMake(0, 0, trgWidth, trgHeight), imgRef); //can this fail? not documented
-
- //::CGContextFlush(ctxRef); //"If you pass [...] a bitmap context, this function does nothing."
-
- osx::ImageData imgOut(trgWidth, trgHeight);
-
- auto it = buf.begin();
- auto itOutRgb = imgOut.rgb.begin();
- auto itOutAlpha = imgOut.alpha.begin();
- for (int i = 0; i < trgWidth * trgHeight; ++i)
- {
- const unsigned char b = *it++;
- const unsigned char g = *it++;
- const unsigned char r = *it++;
- const unsigned char a = *it++;
-
- //unsigned arithmetics caveat!
- auto demultiplex = [&](unsigned char c) { return static_cast<unsigned char>(numeric::confineCpy(a == 0 ? 0 : (c * 255 + a - 1) / a, 0, 255)); }; //=ceil(c * 255 / a)
-
- *itOutRgb++ = demultiplex(r);
- *itOutRgb++ = demultiplex(g);
- *itOutRgb++ = demultiplex(b);
- *itOutAlpha++ = a;
- }
-
- return imgOut;
-}
-}
-
-
-osx::ImageData osx::getThumbnail(const char* filename, int requestedSize) //throw SysError
-{
- @try
- {
- @autoreleasepool
- {
- NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];
- ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented
- //stringWithCString returns string which is already set to autorelease!
-
- NSImage* nsImg = [[[NSImage alloc] initWithContentsOfFile:nsFile] autorelease];
- ZEN_OSX_ASSERT(nsImg != nil); //may fail
-
- return extractBytes(nsImg, requestedSize); //throw SysError
- }
- }
- @catch(NSException* e)
- {
- throwSysError(e); //throw SysError
- }
-}
-
-
-osx::ImageData osx::getFileIcon(const char* filename, int requestedSize) //throw SysError
-{
- @try
- {
- @autoreleasepool
- {
- NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];
- ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented
- //stringWithCString returns string which is already set to autorelease!
-
- NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFile:nsFile];
- ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented
-
- return extractBytes(nsImg, requestedSize); //throw SysError
- }
- }
- @catch (NSException* e)
- {
- throwSysError(e); //throw SysError
- }
-}
-
-
-osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw SysError
-{
- @try
- {
- @autoreleasepool
- {
- NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)];
- //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:@"dat"];
- ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented
-
- return extractBytes(nsImg, requestedSize); //throw SysError
- }
- }
- @catch (NSException* e)
- {
- throwSysError(e); //throw SysError
- }
-}
-
-
-osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw SysError
-{
- @try
- {
- @autoreleasepool
- {
- NSImage* nsImg = [NSImage imageNamed:NSImageNameFolder];
- //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)];
- ZEN_OSX_ASSERT(nsImg != nil); //may fail
-
- return extractBytes(nsImg, requestedSize); //throw SysError
- }
- }
- @catch (NSException* e)
- {
- throwSysError(e); //throw SysError
- }
-}
diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp
deleted file mode 100644
index 9c6b16a9..00000000
--- a/lib/parallel_scan.cpp
+++ /dev/null
@@ -1,588 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "parallel_scan.h"
-#include <zen/file_traverser.h>
-#include <zen/file_error.h>
-#include <zen/thread.h> //includes <boost/thread.hpp>
-#include <zen/scope_guard.h>
-#include <zen/fixed_list.h>
-#include <boost/detail/atomic_count.hpp>
-#include "db_file.h"
-#include "lock_holder.h"
-
-using namespace zen;
-
-
-namespace
-{
-/*
-#ifdef ZEN_WIN
-
-struct DiskInfo
-{
- DiskInfo() :
- driveType(DRIVE_UNKNOWN),
- diskID(-1) {}
-
- UINT driveType;
- int diskID; // -1 if id could not be determined, this one is filled if driveType == DRIVE_FIXED or DRIVE_REMOVABLE;
-};
-
-inline
-bool operator<(const DiskInfo& lhs, const DiskInfo& rhs)
-{
- if (lhs.driveType != rhs.driveType)
- return lhs.driveType < rhs.driveType;
-
- if (lhs.diskID < 0 || rhs.diskID < 0)
- return false;
- //consider "same", reason: one volume may be uniquely associated with one disk, while the other volume is associated to the same disk AND another one!
- //volume <-> disk is 0..N:1..N
-
- return lhs.diskID < rhs.diskID ;
-}
-
-
-DiskInfo retrieveDiskInfo(const Zstring& pathName)
-{
- std::vector<wchar_t> volName(std::max(pathName.size(), static_cast<size_t>(10000)));
-
- DiskInfo output;
-
- //full pathName need not yet exist!
- if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
- &volName[0], //__out LPTSTR lpszVolumePathName,
- static_cast<DWORD>(volName.size()))) //__in DWORD cchBufferLength
- return output;
-
- const Zstring rootPathName = &volName[0];
-
- output.driveType = ::GetDriveType(rootPathName.c_str());
-
- if (output.driveType == DRIVE_NO_ROOT_DIR) //these two should be the same error category
- output.driveType = DRIVE_UNKNOWN;
-
- if (output.driveType != DRIVE_FIXED && output.driveType != DRIVE_REMOVABLE)
- return output; //no reason to get disk ID
-
- //go and find disk id:
-
- //format into form: "\\.\C:"
- Zstring volnameFmt = rootPathName;
- if (endsWith(volnameFmt, FILE_NAME_SEPARATOR))
- volnameFmt.resize(volnameFmt.size() - 1);
- volnameFmt = L"\\\\.\\" + volnameFmt;
-
- HANDLE hVolume = ::CreateFile(volnameFmt.c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr,
- OPEN_EXISTING,
- 0,
- nullptr);
- if (hVolume == INVALID_HANDLE_VALUE)
- return output;
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hVolume));
-
- std::vector<char> buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks!
-
- DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hVolume, // handle to device
- IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, // dwIoControlCode
- nullptr, // lpInBuffer
- 0, // nInBufferSize
- &buffer[0], // output buffer
- static_cast<DWORD>(buffer.size()), // size of output buffer
- &bytesReturned, // number of bytes returned
- nullptr)) // OVERLAPPED structure
- return output;
-
- const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast<VOLUME_DISK_EXTENTS*>(&buffer[0]);
-
- if (volDisks.NumberOfDiskExtents != 1)
- return output;
-
- output.diskID = volDisks.Extents[0].DiskNumber;
-
- return output;
-}
-#endif
-*/
-
-/*
-PERF NOTE
-
---------------------------------------------
-|Testcase: Reading from two different disks|
---------------------------------------------
-Windows 7:
- 1st(unbuffered) |2nd (OS buffered)
- ----------------------------------
-1 Thread: 57s | 8s
-2 Threads: 39s | 7s
-
---------------------------------------------------
-|Testcase: Reading two directories from same disk|
---------------------------------------------------
-Windows 7: Windows XP:
- 1st(unbuffered) |2nd (OS buffered) 1st(unbuffered) |2nd (OS buffered)
- ---------------------------------- ----------------------------------
-1 Thread: 41s | 13s 1 Thread: 45s | 13s
-2 Threads: 42s | 11s 2 Threads: 38s | 8s
-
-=> Traversing does not take any advantage of file locality so that even multiple threads operating on the same disk impose no performance overhead! (even faster on XP)
-
-std::vector<std::set<DirectoryKey>> separateByDistinctDisk(const std::set<DirectoryKey>& dirkeys)
-{
- //use one thread per physical disk:
- typedef std::map<DiskInfo, std::set<DirectoryKey>> DiskKeyMapping;
- DiskKeyMapping tmp;
- std::for_each(dirkeys.begin(), dirkeys.end(),
- [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); });
-
- std::vector<std::set<DirectoryKey>> buckets;
- std::transform(tmp.begin(), tmp.end(), std::back_inserter(buckets),
- [&](const DiskKeyMapping::value_type& diskToKey) { return diskToKey.second; });
- return buckets;
-}
-*/
-
-//------------------------------------------------------------------------------------------
-typedef Zbase<wchar_t, StorageRefCountThreadSafe> BasicWString; //thread safe string class for UI texts
-
-
-class AsyncCallback //actor pattern
-{
-public:
- AsyncCallback() :
- notifyingThreadID(0),
- textScanning(_("Scanning:")),
- itemsScanned(0),
- activeWorker(0) {}
-
- FillBufferCallback::HandleError reportError(const std::wstring& msg, size_t retryNumber) //blocking call: context of worker thread
- {
- boost::unique_lock<boost::mutex> dummy(lockErrorInfo);
- while (errorInfo.get() || errorResponse.get())
- conditionCanReportError.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point!
-
- errorInfo = make_unique<std::pair<BasicWString, size_t>>(BasicWString(msg), retryNumber);
-
- while (!errorResponse.get())
- conditionGotResponse.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point!
-
- FillBufferCallback::HandleError rv = *errorResponse;
-
- errorInfo.reset();
- errorResponse.reset();
-
- dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionCanReportError.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
-
- return rv;
- }
-
- void processErrors(FillBufferCallback& callback) //context of main thread, call repreatedly
- {
- boost::unique_lock<boost::mutex> dummy(lockErrorInfo);
- if (errorInfo.get() && !errorResponse.get())
- {
- FillBufferCallback::HandleError rv = callback.reportError(copyStringTo<std::wstring>(errorInfo->first), errorInfo->second); //throw!
- errorResponse = make_unique<FillBufferCallback::HandleError>(rv);
-
- dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionGotResponse.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
- }
- }
-
- void incrementNotifyingThreadId() { ++notifyingThreadID; } //context of main thread
-
- void reportCurrentFile(const Zstring& filename, long threadID) //context of worker thread
- {
- if (threadID != notifyingThreadID) return; //only one thread at a time may report status
-
- boost::lock_guard<boost::mutex> dummy(lockCurrentStatus);
- currentFile = filename;
- currentStatus.clear();
- }
-
- void reportCurrentStatus(const std::wstring& status, long threadID) //context of worker thread
- {
- if (threadID != notifyingThreadID) return; //only one thread may report status
-
- boost::lock_guard<boost::mutex> dummy(lockCurrentStatus);
- currentFile.clear();
- currentStatus = BasicWString(status); //we cannot assume std::wstring to be thread safe (yet)!
- }
-
- std::wstring getCurrentStatus() //context of main thread, call repreatedly
- {
- Zstring filename;
- std::wstring statusMsg;
- {
- boost::lock_guard<boost::mutex> dummy(lockCurrentStatus);
- if (!currentFile.empty())
- filename = currentFile;
- else if (!currentStatus.empty())
- statusMsg = copyStringTo<std::wstring>(currentStatus);
- }
-
- if (!filename.empty())
- {
- std::wstring statusText = copyStringTo<std::wstring>(textScanning);
- const long activeCount = activeWorker;
- if (activeCount >= 2)
- statusText += L" [" + replaceCpy(_P("1 thread", "%x threads", activeCount), L"%x", numberTo<std::wstring>(activeCount)) + L"]";
-
- statusText += L" " + fmtFileName(filename);
- return statusText;
- }
- else
- return statusMsg;
- }
-
- void incItemsScanned() { ++itemsScanned; }
- long getItemsScanned() const { return itemsScanned; }
-
- void incActiveWorker() { ++activeWorker; }
- void decActiveWorker() { --activeWorker; }
- long getActiveWorker() const { return activeWorker; }
-
-private:
- //---- error handling ----
- boost::mutex lockErrorInfo;
- boost::condition_variable conditionCanReportError;
- boost::condition_variable conditionGotResponse;
- std::unique_ptr<std::pair<BasicWString, size_t>> errorInfo; //error message + retry number
- std::unique_ptr<FillBufferCallback::HandleError> errorResponse;
-
- //---- status updates ----
- boost::detail::atomic_count notifyingThreadID;
- //CAVEAT: do NOT use boost::thread::id as long as this showstopper exists: https://svn.boost.org/trac/boost/ticket/5754
- boost::mutex lockCurrentStatus; //use a different lock for current file: continue traversing while some thread may process an error
- Zstring currentFile; //only one of these two is filled at a time!
- BasicWString currentStatus; //
-
- const BasicWString textScanning; //this one is (currently) not shared and could be made a std::wstring, but we stay consistent and use thread-safe variables in this class only!
-
- //---- status updates II (lock free) ----
- boost::detail::atomic_count itemsScanned;
- boost::detail::atomic_count activeWorker;
-};
-
-//-------------------------------------------------------------------------------------------------
-
-struct TraverserShared
-{
-public:
- TraverserShared(long threadID,
- SymLinkHandling handleSymlinks,
- const HardFilter::FilterRef& filter,
- std::set<Zstring>& failedDirReads,
- std::set<Zstring>& failedItemReads,
- AsyncCallback& acb) :
- handleSymlinks_(handleSymlinks),
- filterInstance(filter),
- failedDirReads_(failedDirReads),
- failedItemReads_(failedItemReads),
- acb_(acb),
- threadID_(threadID) {}
-
- const SymLinkHandling handleSymlinks_;
- const HardFilter::FilterRef filterInstance; //always bound!
-
- std::set<Zstring>& failedDirReads_;
- std::set<Zstring>& failedItemReads_;
-
- AsyncCallback& acb_;
- const long threadID_;
-};
-
-
-class DirCallback : public zen::TraverseCallback
-{
-public:
- DirCallback(TraverserShared& config,
- const Zstring& relNameParentPf, //postfixed with FILE_NAME_SEPARATOR!
- DirContainer& output) :
- cfg(config),
- relNameParentPf_(relNameParentPf),
- output_(output) {}
-
- virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details);
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details);
- virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName);
- virtual void releaseDirTraverser(TraverseCallback* trav);
-
- virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber);
- virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName);
-
-private:
- TraverserShared& cfg;
- const Zstring relNameParentPf_;
- DirContainer& output_;
-};
-
-
-void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details)
-{
- boost::this_thread::interruption_point();
-
- const Zstring fileNameShort(shortName);
-
- //do not list the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. or lock files
- if (endsWith(fileNameShort, SYNC_DB_FILE_ENDING) ||
- endsWith(fileNameShort, LOCK_FILE_ENDING))
- return;
-
- //update status information no matter whether object is excluded or not!
- cfg.acb_.reportCurrentFile(fullName, cfg.threadID_);
-
- //------------------------------------------------------------------------------------
- //apply filter before processing (use relative name!)
- if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort))
- return;
-
- // std::string fileId = details.fileSize >= 1024 * 1024U ? util::retrieveFileID(fullName) : std::string();
- /*
- Perf test Windows 7, SSD, 350k files, 50k dirs, files > 1MB: 7000
- regular: 6.9s
- ID per file: 43.9s
- ID per file > 1MB: 7.2s
- ID per dir: 8.4s
-
- Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!)
- */
-
- output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTime, details.fileSize, details.id, details.symlinkInfo != nullptr));
-
- cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator
-}
-
-
-DirCallback::HandleLink DirCallback::onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
-{
- boost::this_thread::interruption_point();
-
- //update status information no matter whether object is excluded or not!
- cfg.acb_.reportCurrentFile(fullName, cfg.threadID_);
-
- switch (cfg.handleSymlinks_)
- {
- case SYMLINK_EXCLUDE:
- return LINK_SKIP;
-
- case SYMLINK_USE_DIRECTLY:
- if (cfg.filterInstance->passFileFilter(relNameParentPf_ + shortName)) //always use file filter: Link type may not be "stable" on Linux!
- {
- output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTime));
- cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator
- }
- return LINK_SKIP;
-
- case SYMLINK_FOLLOW_LINK:
- //filter symlinks before trying to follow them: handle user-excluded broken symlinks!
- //since we don't know what the symlink will resolve to, only do this when both variants agree:
- if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + shortName))
- {
- bool subObjMightMatch = true;
- if (!cfg.filterInstance->passDirFilter(relNameParentPf_ + shortName, &subObjMightMatch))
- if (!subObjMightMatch)
- return LINK_SKIP;
- }
- return LINK_FOLLOW;
- }
-
- assert(false);
- return LINK_SKIP;
-}
-
-
-TraverseCallback* DirCallback::onDir(const Zchar* shortName, const Zstring& fullName)
-{
- boost::this_thread::interruption_point();
-
- //update status information no matter whether object is excluded or not!
- cfg.acb_.reportCurrentFile(fullName, cfg.threadID_);
-
- //------------------------------------------------------------------------------------
- const Zstring& relPath = relNameParentPf_ + shortName;
-
- //apply filter before processing (use relative name!)
- bool subObjMightMatch = true;
- const bool passFilter = cfg.filterInstance->passDirFilter(relPath, &subObjMightMatch);
- if (!passFilter && !subObjMightMatch)
- return nullptr; //do NOT traverse subdirs
- //else: attention! ensure directory filtering is applied later to exclude actually filtered directories
-
- DirContainer& subDir = output_.addSubDir(shortName);
- if (passFilter)
- cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator
-
- return new DirCallback(cfg, relPath + FILE_NAME_SEPARATOR, subDir); //releaseDirTraverser() is guaranteed to be called in any case
-}
-
-
-void DirCallback::releaseDirTraverser(TraverseCallback* trav)
-{
- TraverseCallback::releaseDirTraverser(trav); //no-op, introduce compile-time coupling
- delete trav;
-}
-
-
-DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg, size_t retryNumber)
-{
- switch (cfg.acb_.reportError(msg, retryNumber))
- {
- case FillBufferCallback::ON_ERROR_IGNORE:
- cfg.failedDirReads_.insert(relNameParentPf_);
- return ON_ERROR_IGNORE;
-
- case FillBufferCallback::ON_ERROR_RETRY:
- return ON_ERROR_RETRY;
- }
- assert(false);
- return ON_ERROR_IGNORE;
-}
-
-
-DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName)
-{
- switch (cfg.acb_.reportError(msg, retryNumber))
- {
- case FillBufferCallback::ON_ERROR_IGNORE:
- cfg.failedItemReads_.insert(relNameParentPf_ + shortName);
- return ON_ERROR_IGNORE;
-
- case FillBufferCallback::ON_ERROR_RETRY:
- return ON_ERROR_RETRY;
- }
- assert(false);
- return ON_ERROR_IGNORE;
-}
-
-
-#ifdef ZEN_WIN
-class DstHackCallbackImpl : public DstHackCallback
-{
-public:
- DstHackCallbackImpl(AsyncCallback& acb, long threadID) :
- acb_(acb),
- threadID_(threadID),
- 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
- {
- acb_.reportCurrentStatus(replaceCpy(textApplyingDstHack, L"%x", fmtFileName(filename)), threadID_);
- }
-
- AsyncCallback& acb_;
- long threadID_;
- const std::wstring textApplyingDstHack;
-};
-#endif
-
-//------------------------------------------------------------------------------------------
-
-class WorkerThread
-{
-public:
- WorkerThread(long threadID,
- const std::shared_ptr<AsyncCallback>& acb,
- const DirectoryKey& dirKey,
- DirectoryValue& dirOutput) :
- threadID_(threadID),
- acb_(acb),
- dirKey_(dirKey),
- dirOutput_(dirOutput) {}
-
- void operator()() //thread entry
- {
- acb_->incActiveWorker();
- ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker(););
-
- acb_->reportCurrentFile(dirKey_.dirnameFull_, threadID_); //just in case first directory access is blocking
-
- TraverserShared travCfg(threadID_,
- dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy
- dirKey_.filter_,
- dirOutput_.failedDirReads,
- dirOutput_.failedItemReads,
- *acb_);
-
- DirCallback traverser(travCfg,
- Zstring(),
- dirOutput_.dirCont);
-
- DstHackCallback* dstCallbackPtr = nullptr;
-#ifdef ZEN_WIN
- DstHackCallbackImpl dstCallback(*acb_, threadID_);
- dstCallbackPtr = &dstCallback;
-#endif
-
- //get all files and folders from directoryPostfixed (and subdirectories)
- traverseFolder(dirKey_.dirnameFull_, traverser, dstCallbackPtr); //exceptions may be thrown!
- }
-
-private:
- long threadID_;
- std::shared_ptr<AsyncCallback> acb_;
- const DirectoryKey dirKey_;
- DirectoryValue& dirOutput_;
-};
-}
-
-
-void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in
- std::map<DirectoryKey, DirectoryValue>& buf, //out
- FillBufferCallback& callback,
- size_t updateInterval)
-{
- buf.clear();
-
- FixedList<boost::thread> worker; //note: we cannot use std::vector<boost::thread>: compiler error on GCC 4.7, probably a boost screw-up
-
- zen::ScopeGuard guardWorker = zen::makeGuard([&]
- {
- for (boost::thread& wt : worker)
- wt.interrupt(); //interrupt all at once first, then join
- for (boost::thread& wt : worker)
- if (wt.joinable()) //= precondition of thread::join(), which throws an exception if violated!
- wt.join(); //in this context it is possible a thread is *not* joinable anymore due to the thread::timed_join() below!
- });
-
- auto acb = std::make_shared<AsyncCallback>();
-
- //init worker threads
- for (const DirectoryKey& key : keysToRead)
- {
- assert(buf.find(key) == buf.end());
- DirectoryValue& dirOutput = buf[key];
-
- const long threadId = static_cast<long>(worker.size());
- worker.emplace_back(WorkerThread(threadId, acb, key, dirOutput));
- }
-
- //wait until done
- for (boost::thread& wt : worker)
- {
- do
- {
- //update status
- callback.reportStatus(acb->getCurrentStatus(), acb->getItemsScanned()); //throw!
-
- //process errors
- acb->processErrors(callback);
- }
- while (!wt.timed_join(boost::posix_time::milliseconds(updateInterval)));
-
- acb->incrementNotifyingThreadId(); //process info messages of one thread at a time only
- }
-
- guardWorker.dismiss();
-}
diff --git a/lib/parallel_scan.h b/lib/parallel_scan.h
deleted file mode 100644
index 408882bf..00000000
--- a/lib/parallel_scan.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef PARALLEL_SCAN_H_INCLUDED
-#define PARALLEL_SCAN_H_INCLUDED
-
-#include <map>
-#include <set>
-#include "hard_filter.h"
-#include "../structures.h"
-#include "../file_hierarchy.h"
-
-namespace zen
-{
-struct DirectoryKey
-{
- DirectoryKey(const Zstring& dirnameFull,
- const HardFilter::FilterRef& filter,
- SymLinkHandling handleSymlinks) :
- dirnameFull_(dirnameFull),
- filter_(filter),
- handleSymlinks_(handleSymlinks) {}
-
- Zstring dirnameFull_;
- HardFilter::FilterRef filter_; //filter interface: always bound by design!
- SymLinkHandling handleSymlinks_;
-};
-
-inline
-bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs)
-{
- if (lhs.handleSymlinks_ != rhs.handleSymlinks_)
- return lhs.handleSymlinks_ < rhs.handleSymlinks_;
-
- const int cmpName = cmpFileName(lhs.dirnameFull_, rhs.dirnameFull_);
- if (cmpName != 0)
- return cmpName < 0;
-
- return *lhs.filter_ < *rhs.filter_;
-}
-
-
-struct DirectoryValue
-{
- DirContainer dirCont;
- std::set<Zstring> failedDirReads; //relative postfixed names (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop
- std::set<Zstring> failedItemReads; //relative postfixed names (never empty) for failure to read single file/dir/symlink
-};
-
-
-class FillBufferCallback
-{
-public:
- virtual ~FillBufferCallback() {}
-
- enum HandleError
- {
- ON_ERROR_RETRY,
- ON_ERROR_IGNORE
- };
- virtual HandleError reportError (const std::wstring& msg, size_t retryNumber) = 0; //may throw!
- virtual void reportStatus(const std::wstring& msg, int itemsTotal ) = 0; //
-};
-
-//attention: ensure directory filtering is applied later to exclude filtered directories which have been kept as parent folders
-
-void fillBuffer(const std::set<DirectoryKey>& keysToRead, //in
- std::map<DirectoryKey, DirectoryValue>& buf, //out
- FillBufferCallback& callback,
- size_t updateInterval); //unit: [ms]
-}
-
-#endif // PARALLEL_SCAN_H_INCLUDED
diff --git a/lib/parse_lng.h b/lib/parse_lng.h
deleted file mode 100644
index 19a8e751..00000000
--- a/lib/parse_lng.h
+++ /dev/null
@@ -1,706 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef PARSE_LNG_HEADER_INCLUDED
-#define PARSE_LNG_HEADER_INCLUDED
-
-#include <algorithm>
-#include <cctype>
-#include <functional>
-#include <memory>
-#include <map>
-#include <set>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <vector>
-#include <list>
-#include <zen/utf.h>
-#include <zen/string_tools.h>
-#include "parse_plural.h"
-//#include <zen/perf.h>
-
-namespace lngfile
-{
-//singular forms
-typedef std::map <std::string, std::string> TranslationMap; //orig |-> translation
-
-//plural forms
-typedef std::pair<std::string, std::string> SingularPluralPair; //1 house| n houses
-typedef std::vector<std::string> PluralForms; //1 dom | 2 domy | 5 domów
-typedef std::map<SingularPluralPair, PluralForms> TranslationPluralMap; //(sing/plu) |-> pluralforms
-
-struct TransHeader
-{
- TransHeader() : pluralCount(0) {}
- std::string languageName; //display name: "English (UK)"
- std::string translatorName; //"Zenju"
- std::string localeName; //ISO 639 language code + ISO 3166 country code, e.g. "en_GB", or "en_US"
- std::string flagFile; //"england.png"
- int pluralCount; //2
- std::string pluralDefinition; //"n == 1 ? 0 : 1"
-};
-
-
-struct ParsingError
-{
- ParsingError(const std::wstring& msg, size_t row, size_t col) : msg_(msg), row_(row), col_(col) {}
- std::wstring msg_; //parser error message
- size_t row_; //starting with 0
- size_t col_; //
-};
-void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut); //throw ParsingError
-void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError
-
-class TranslationUnorderedList; //unordered list of unique translation items
-std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//--------------------------- implementation ---------------------------
-enum class TranslationNewItemPos
-{
- REL,
- TOP
-};
-
-class TranslationUnorderedList //unordered list of unique translation items
-{
-public:
- TranslationUnorderedList(TranslationNewItemPos newItemPos, TranslationMap&& transOld, TranslationPluralMap&& transPluralOld) :
- newItemPos_(newItemPos), transOld_(std::move(transOld)), transPluralOld_(std::move(transPluralOld)) {}
-
- void addItem(const std::string& orig)
- {
- if (!transUnique.insert(orig).second) return;
- auto it = transOld_.find(orig);
- if (it != transOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing
- sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, it->second)));
- else
- switch (newItemPos_)
- {
- case TranslationNewItemPos::REL:
- sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, std::string())));
- break;
- case TranslationNewItemPos::TOP:
- sequence.push_front(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); //put untranslated items to the front of the .lng filebreak;
- break;
- }
- }
-
- void addItem(const SingularPluralPair& orig)
- {
- if (!pluralUnique.insert(orig).second) return;
- auto it = transPluralOld_.find(orig);
- if (it != transPluralOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing
- sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, it->second)));
- else
- switch (newItemPos_)
- {
- case TranslationNewItemPos::REL:
- sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms())));
- break;
- case TranslationNewItemPos::TOP:
- sequence.push_front(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); //put untranslated items to the front of the .lng file
- break;
- }
- }
-
- bool untranslatedTextExists() const { return std::any_of(sequence.begin(), sequence.end(), [](const std::shared_ptr<Item>& item) { return !item->hasTranslation(); }); }
-
- template <class Function, class Function2>
- void visitItems(Function onTrans, Function2 onPluralTrans) const //onTrans takes (const TranslationMap::value_type&), onPluralTrans takes (const TranslationPluralMap::value_type&)
- {
- for (const auto& item : sequence)
- if (auto regular = dynamic_cast<const RegularItem*>(item.get()))
- onTrans(regular->value);
- else if (auto plural = dynamic_cast<const PluralItem*>(item.get()))
- onPluralTrans(plural->value);
- else assert(false);
- }
-
-private:
- struct Item { virtual ~Item() {} virtual bool hasTranslation() const = 0; };
- struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationMap ::value_type value; };
- struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationPluralMap::value_type value; };
-
- const TranslationNewItemPos newItemPos_;
- std::list<std::shared_ptr<Item>> sequence; //ordered list of translation elements
-
- std::set<TranslationMap ::key_type> transUnique; //check uniqueness
- std::set<TranslationPluralMap::key_type> pluralUnique; //
-
- const TranslationMap transOld_; //reuse existing translation
- const TranslationPluralMap transPluralOld_; //
-};
-
-
-struct Token
-{
- enum Type
- {
- //header information
- TK_HEADER_BEGIN,
- TK_HEADER_END,
- TK_LANG_NAME_BEGIN,
- TK_LANG_NAME_END,
- TK_TRANS_NAME_BEGIN,
- TK_TRANS_NAME_END,
- TK_LOCALE_NAME_BEGIN,
- TK_LOCALE_NAME_END,
- TK_FLAG_FILE_BEGIN,
- TK_FLAG_FILE_END,
- TK_PLURAL_COUNT_BEGIN,
- TK_PLURAL_COUNT_END,
- TK_PLURAL_DEF_BEGIN,
- TK_PLURAL_DEF_END,
-
- //item level
- TK_SRC_BEGIN,
- TK_SRC_END,
- TK_TRG_BEGIN,
- TK_TRG_END,
- TK_TEXT,
- TK_PLURAL_BEGIN,
- TK_PLURAL_END,
- TK_END
- };
-
- Token(Type t) : type(t) {}
- Type type;
-
- std::string text;
-};
-
-
-class KnownTokens
-{
-public:
- typedef std::map<Token::Type, std::string> TokenMap;
-
- static const TokenMap& getList()
- {
- static KnownTokens inst;
- return inst.tokens;
- }
-
- static std::string text(Token::Type t)
- {
- auto it = getList().find(t);
- return it != getList().end() ? it->second : std::string();
- }
-
-private:
- KnownTokens()
- {
- //header information
- tokens.insert(std::make_pair(Token::TK_HEADER_BEGIN, "<header>"));
- tokens.insert(std::make_pair(Token::TK_HEADER_END, "</header>"));
- tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "<language>"));
- tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "</language>"));
- tokens.insert(std::make_pair(Token::TK_TRANS_NAME_BEGIN, "<translator>"));
- tokens.insert(std::make_pair(Token::TK_TRANS_NAME_END, "</translator>"));
- tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_BEGIN, "<locale>"));
- tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "</locale>"));
- tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "<image>"));
- tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "</image>"));
- tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "<plural_count>"));
- tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "</plural_count>"));
- tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "<plural_definition>"));
- tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "</plural_definition>"));
-
- //item level
- tokens.insert(std::make_pair(Token::TK_SRC_BEGIN, "<source>"));
- tokens.insert(std::make_pair(Token::TK_SRC_END, "</source>"));
- tokens.insert(std::make_pair(Token::TK_TRG_BEGIN, "<target>"));
- tokens.insert(std::make_pair(Token::TK_TRG_END, "</target>"));
- tokens.insert(std::make_pair(Token::TK_PLURAL_BEGIN, "<pluralform>"));
- tokens.insert(std::make_pair(Token::TK_PLURAL_END, "</pluralform>"));
- }
- TokenMap tokens;
-};
-
-
-class Scanner
-{
-public:
- Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin())
- {
- if (zen::startsWith(stream, zen::BYTE_ORDER_MARK_UTF8))
- pos += zen::strLength(zen::BYTE_ORDER_MARK_UTF8);
- }
-
- Token nextToken()
- {
- //skip whitespace
- pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); });
-
- if (pos == stream.end())
- return Token(Token::TK_END);
-
- for (const auto& token : KnownTokens::getList())
- if (startsWith(token.second))
- {
- pos += token.second.size();
- return Token(token.first);
- }
-
- //rest must be "text"
- auto itBegin = pos;
- while (pos != stream.end() && !startsWithKnownTag())
- pos = std::find(pos + 1, stream.end(), '<');
-
- std::string text(itBegin, pos);
-
- normalize(text); //remove whitespace from end ect.
-
- if (text.empty() && pos == stream.end())
- return Token(Token::TK_END);
-
- Token out(Token::TK_TEXT);
- out.text = text;
- return out;
- }
-
- size_t posRow() const //current row beginning with 0
- {
- //count line endings
- const size_t crSum = std::count(stream.begin(), pos, '\r'); //carriage returns
- const size_t nlSum = std::count(stream.begin(), pos, '\n'); //new lines
- assert(crSum == 0 || nlSum == 0 || crSum == nlSum);
- return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win
- }
-
- size_t posCol() const //current col beginning with 0
- {
- //seek beginning of line
- for (auto it = pos; it != stream.begin(); )
- {
- --it;
- if (*it == '\r' || *it == '\n')
- return pos - it - 1;
- }
- return pos - stream.begin();
- }
-
-private:
- bool startsWithKnownTag() const
- {
- return std::any_of(KnownTokens::getList().begin(), KnownTokens::getList().end(),
- [&](const KnownTokens::TokenMap::value_type& p) { return startsWith(p.second); });
- }
-
- bool startsWith(const std::string& prefix) const
- {
- if (stream.end() - pos < static_cast<ptrdiff_t>(prefix.size()))
- return false;
- return std::equal(prefix.begin(), prefix.end(), pos);
- }
-
- static void normalize(std::string& text)
- {
- zen::trim(text); //remove whitespace from both ends
-
- //Delimiter:
- //----------
- //Linux: 0xA \n
- //Mac: 0xD \r
- //Win: 0xD 0xA \r\n <- language files are in Windows format
- zen::replace(text, "\r\n", '\n'); //
- zen::replace(text, "\r", '\n'); //ensure c-style line breaks
- }
-
- const std::string stream;
- std::string::const_iterator pos;
-};
-
-
-class LngParser
-{
-public:
- LngParser(const std::string& fileStream) : scn(fileStream), tk(scn.nextToken()) {}
-
- void parse(TranslationMap& out, TranslationPluralMap& pluralOut, TransHeader& header)
- {
- parseHeader(header);
-
- try
- {
- parse_plural::PluralFormInfo pi(header.pluralDefinition, header.pluralCount);
-
- //items
- while (token().type != Token::TK_END)
- parseRegular(out, pluralOut, pi);
- }
- catch (const parse_plural::InvalidPluralForm&)
- {
- throw ParsingError(L"Invalid plural form definition", scn.posRow(), scn.posCol());
- }
- }
-
- void parseHeader(TransHeader& header)
- {
- consumeToken(Token::TK_HEADER_BEGIN);
-
- consumeToken(Token::TK_LANG_NAME_BEGIN);
- header.languageName = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_LANG_NAME_END);
-
- consumeToken(Token::TK_TRANS_NAME_BEGIN);
- header.translatorName = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_TRANS_NAME_END);
-
- consumeToken(Token::TK_LOCALE_NAME_BEGIN);
- header.localeName = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_LOCALE_NAME_END);
-
- consumeToken(Token::TK_FLAG_FILE_BEGIN);
- header.flagFile = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_FLAG_FILE_END);
-
- consumeToken(Token::TK_PLURAL_COUNT_BEGIN);
- header.pluralCount = zen::stringTo<int>(tk.text);
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_PLURAL_COUNT_END);
-
- consumeToken(Token::TK_PLURAL_DEF_BEGIN);
- header.pluralDefinition = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_PLURAL_DEF_END);
-
- consumeToken(Token::TK_HEADER_END);
- }
-
-private:
- void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo)
- {
- consumeToken(Token::TK_SRC_BEGIN);
-
- if (token().type == Token::TK_PLURAL_BEGIN)
- return parsePlural(pluralOut, pluralInfo);
-
- if (token().type != Token::TK_TEXT)
- throw ParsingError(L"Source text empty", scn.posRow(), scn.posCol());
- std::string original = tk.text;
- nextToken();
-
- consumeToken(Token::TK_SRC_END);
-
- consumeToken(Token::TK_TRG_BEGIN);
- std::string translation;
- if (token().type == Token::TK_TEXT)
- {
- translation = token().text;
- nextToken();
- }
- consumeToken(Token::TK_TRG_END);
-
- validateTranslation(original, translation); //throw throw ParsingError
- out.insert(std::make_pair(original, translation));
- }
-
- void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo)
- {
- //Token::TK_SRC_BEGIN already consumed
-
- consumeToken(Token::TK_PLURAL_BEGIN);
- std::string engSingular = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_PLURAL_END);
-
- consumeToken(Token::TK_PLURAL_BEGIN);
- std::string engPlural = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_PLURAL_END);
-
- consumeToken(Token::TK_SRC_END);
-
- consumeToken(Token::TK_TRG_BEGIN);
-
- PluralForms pluralList;
- while (token().type == Token::TK_PLURAL_BEGIN)
- {
- consumeToken(Token::TK_PLURAL_BEGIN);
- std::string pluralForm = tk.text;
- consumeToken(Token::TK_TEXT);
- consumeToken(Token::TK_PLURAL_END);
- pluralList.push_back(pluralForm);
- }
-
- consumeToken(Token::TK_TRG_END);
-
- const SingularPluralPair original(engSingular, engPlural);
- validateTranslation(original, pluralList, pluralInfo);
- pluralOut.insert(std::make_pair(original, pluralList));
- }
-
- void validateTranslation(const std::string& original, const std::string& translation) //throw ParsingError
- {
- if (original.empty())
- throw ParsingError(L"Source translation is empty", scn.posRow(), scn.posCol());
-
- if (!translation.empty())
- {
- //if original contains placeholder, so should translation!
- auto checkPlaceholder = [&](const std::string& placeholder)
- {
- if (zen::contains(original, placeholder) &&
- !zen::contains(translation, placeholder))
- throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol());
- };
- checkPlaceholder("%x");
- checkPlaceholder("%y");
- checkPlaceholder("%z");
-
- //if source contains ampersand to mark menu accellerator key, so must translation
- if (hasSingleAmpersand(original) && !hasSingleAmpersand(translation))
- throw ParsingError(L"Translation is missing the & character to mark an access key for the menu item", scn.posRow(), scn.posCol());
- }
- }
-
- void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError
- {
- using namespace zen;
- //check the primary placeholder is existing at least for the second english text
- if (!contains(original.second, "%x"))
- throw ParsingError(L"Plural form source does not contain %x placeholder", scn.posRow(), scn.posCol());
-
- if (!translation.empty())
- {
- //check for invalid number of plural forms
- if (pluralInfo.getCount() != static_cast<int>(translation.size()))
- throw ParsingError(replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo<std::wstring>(translation.size())), L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn.posRow(), scn.posCol());
-
- //check for duplicate plural form translations (catch copy & paste errors for single-number form translations)
- for (auto it = translation.begin(); it != translation.end(); ++it)
- if (!contains(*it, "%x"))
- {
- auto it2 = std::find(it + 1, translation.end(), *it);
- if (it2 != translation.end())
- throw ParsingError(replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x", L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn.posRow(), scn.posCol());
- }
-
- for (int pos = 0; pos < static_cast<int>(translation.size()); ++pos)
- if (pluralInfo.isSingleNumberForm(pos))
- {
- //translation needs to use decimal number if english source does so (e.g. frequently changing text like statistics)
- if (contains(original.first, "%x") ||
- contains(original.first, "1"))
- {
- const int firstNumber = pluralInfo.getFirstNumber(pos);
- if (!(contains(translation[pos], "%x") ||
- contains(translation[pos], numberTo<std::string>(firstNumber))))
- throw ParsingError(replaceCpy<std::wstring>(replaceCpy<std::wstring>(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder",
- L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn.posRow(), scn.posCol());
- }
- }
- else
- {
- //ensure the placeholder is used when needed
- if (!contains(translation[pos], "%x"))
- throw ParsingError(replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn.posRow(), scn.posCol());
- }
-
- auto checkSecondaryPlaceholder = [&](const std::string& placeholder)
- {
- //make sure secondary placeholder is used in both source texts (or none)
- if (zen::contains(original.first, placeholder) ||
- zen::contains(original.second, placeholder))
- {
- if (!zen::contains(original.first, placeholder) ||
- !zen::contains(original.second, placeholder))
- throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol());
-
- //secondary placeholder is required for all plural forms
- if (!std::all_of(translation.begin(), translation.end(), [&](const std::string& pform) { return zen::contains(pform, placeholder); }))
- throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol());
- }
- };
-
- checkSecondaryPlaceholder("%y");
- checkSecondaryPlaceholder("%z");
- }
- }
-
- static bool hasSingleAmpersand(const std::string& str)
- {
- size_t pos = 0;
- for (;;)
- {
- pos = str.find('&', pos);
- if (pos == std::string::npos)
- return false;
-
- bool freeBefore = pos == 0 || str[pos - 1] != '&';
- bool freeAfter = pos >= str.size() - 1 || str[pos + 1] != '&'; //str.size() > 0 here!
-
- if (freeBefore && freeAfter) //make sure to not catch && which windows resolves as just one & for display!
- return true;
- ++pos;
- }
- }
-
- void nextToken() { tk = scn.nextToken(); }
- const Token& token() const { return tk; }
-
- void consumeToken(Token::Type t) //throw ParsingError
- {
- expectToken(t); //throw ParsingError
- nextToken();
- }
-
- void expectToken(Token::Type t) //throw ParsingError
- {
- if (token().type != t)
- throw ParsingError(L"Unexpected token", scn.posRow(), scn.posCol());
- }
-
- Scanner scn;
- Token tk;
-};
-
-
-inline
-void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut) //throw ParsingError
-{
- out.clear();
- pluralOut.clear();
-
- LngParser(fileStream).parse(out, pluralOut, header);
-}
-
-
-inline
-void parseHeader(const std::string& fileStream, TransHeader& header) //throw ParsingError
-{
- LngParser(fileStream).parseHeader(header);
-}
-
-
-inline
-void formatMultiLineText(std::string& text)
-{
- assert(!zen::contains(text, "\r\n"));
-
- if (text.find('\n') != std::string::npos) //multiple lines
- {
- if (*text.begin() != '\n')
- text = '\n' + text;
- if (*text.rbegin() != '\n')
- text += '\n';
- }
-}
-
-
-std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header)
-{
- std::string out;
- //header
- out += KnownTokens::text(Token::TK_HEADER_BEGIN) + '\n';
-
- out += '\t' + KnownTokens::text(Token::TK_LANG_NAME_BEGIN);
- out += header.languageName;
- out += KnownTokens::text(Token::TK_LANG_NAME_END) + '\n';
-
- out += '\t' + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN);
- out += header.translatorName;
- out += KnownTokens::text(Token::TK_TRANS_NAME_END) + '\n';
-
- out += '\t' + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN);
- out += header.localeName;
- out += KnownTokens::text(Token::TK_LOCALE_NAME_END) + '\n';
-
- out += '\t' + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN);
- out += header.flagFile;
- out += KnownTokens::text(Token::TK_FLAG_FILE_END) + '\n';
-
- out += '\t' + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN);
- out += zen::numberTo<std::string>(header.pluralCount);
- out += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + '\n';
-
- out += '\t' + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN);
- out += header.pluralDefinition;
- out += KnownTokens::text(Token::TK_PLURAL_DEF_END) + '\n';
-
- out += KnownTokens::text(Token::TK_HEADER_END) + '\n';
-
- out += '\n';
-
-
- in.visitItems([&](const TranslationMap::value_type& trans)
- {
- std::string original = trans.first;
- std::string translation = trans.second;
-
- formatMultiLineText(original);
- formatMultiLineText(translation);
-
- out += KnownTokens::text(Token::TK_SRC_BEGIN);
- out += original;
- out += KnownTokens::text(Token::TK_SRC_END) + '\n';
-
- out += KnownTokens::text(Token::TK_TRG_BEGIN);
- out += translation;
- out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n';
- },
- [&](const TranslationPluralMap::value_type& transPlural)
- {
- std::string engSingular = transPlural.first.first;
- std::string engPlural = transPlural.first.second;
- const PluralForms& forms = transPlural.second;
-
- formatMultiLineText(engSingular);
- formatMultiLineText(engPlural);
-
- out += KnownTokens::text(Token::TK_SRC_BEGIN) + '\n';
- out += KnownTokens::text(Token::TK_PLURAL_BEGIN);
- out += engSingular;
- out += KnownTokens::text(Token::TK_PLURAL_END) + '\n';
- out += KnownTokens::text(Token::TK_PLURAL_BEGIN);
- out += engPlural;
- out += KnownTokens::text(Token::TK_PLURAL_END) + '\n';
- out += KnownTokens::text(Token::TK_SRC_END) + '\n';
-
- out += KnownTokens::text(Token::TK_TRG_BEGIN);
- out += '\n';
-
- for (std::string plForm : forms)
- {
- formatMultiLineText(plForm);
-
- out += KnownTokens::text(Token::TK_PLURAL_BEGIN);
- out += plForm;
- out += KnownTokens::text(Token::TK_PLURAL_END) + '\n';
- }
- out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n';
- });
-
- assert(!zen::contains(out, "\r\n") && !zen::contains(out, "\r"));
- return zen::replaceCpy(out, '\n', "\r\n"); //back to win line endings
-}
-}
-
-#endif //PARSE_LNG_HEADER_INCLUDED
diff --git a/lib/parse_plural.h b/lib/parse_plural.h
deleted file mode 100644
index bac933c9..00000000
--- a/lib/parse_plural.h
+++ /dev/null
@@ -1,478 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef PARSE_PLURAL_H_INCLUDED
-#define PARSE_PLURAL_H_INCLUDED
-
-#include <memory>
-#include <cstdint>
-#include <functional>
-#include <zen/string_base.h>
-
-namespace parse_plural
-{
-//expression interface
-struct Expression { virtual ~Expression() {} };
-
-template <class T>
-struct Expr : public Expression
-{
- typedef T ValueType;
- virtual ValueType eval() const = 0;
-};
-
-
-class ParsingError {};
-
-class PluralForm
-{
-public:
- PluralForm(const std::string& stream); //throw ParsingError
- int getForm(std::int64_t n) const { n_ = std::abs(n) ; return static_cast<int>(expr->eval()); }
-
-private:
- std::shared_ptr<Expr<std::int64_t>> expr;
- mutable std::int64_t n_;
-};
-
-
-//validate plural form
-class InvalidPluralForm {};
-
-class PluralFormInfo
-{
-public:
- PluralFormInfo(const std::string& definition, int pluralCount); //throw InvalidPluralForm
-
- int getCount() const { return static_cast<int>(forms.size()); }
- bool isSingleNumberForm(int index) const { return 0 <= index && index < static_cast<int>(forms.size()) ? forms[index].count == 1 : false; }
- int getFirstNumber (int index) const { return 0 <= index && index < static_cast<int>(forms.size()) ? forms[index].firstNumber : -1; }
-
-private:
- struct FormInfo
- {
- FormInfo() : count(0), firstNumber(0) {}
- int count;
- int firstNumber; //which maps to the plural form index position
- };
- std::vector<FormInfo> forms;
-};
-
-
-
-
-
-//--------------------------- implementation ---------------------------
-
-//http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html
-//http://translate.sourceforge.net/wiki/l10n/pluralforms
-/*
-Grammar for Plural forms parser
--------------------------------
-expression:
- conditional-expression
-
-conditional-expression:
- logical-or-expression
- logical-or-expression ? expression : expression
-
-logical-or-expression:
- logical-and-expression
- logical-or-expression || logical-and-expression
-
-logical-and-expression:
- equality-expression
- logical-and-expression && equality-expression
-
-equality-expression:
- relational-expression
- relational-expression == relational-expression
- relational-expression != relational-expression
-
-relational-expression:
- multiplicative-expression
- multiplicative-expression > multiplicative-expression
- multiplicative-expression < multiplicative-expression
- multiplicative-expression >= multiplicative-expression
- multiplicative-expression <= multiplicative-expression
-
-multiplicative-expression:
- pm-expression
- multiplicative-expression % pm-expression
-
-pm-expression:
- variable-number-n-expression
- constant-number-expression
- ( expression )
-
-
-.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)
-*/
-
-namespace implementation
-{
-//specific binary expression based on STL function objects
-template <class StlOp>
-struct BinaryExp : public Expr<typename StlOp::result_type>
-{
- typedef std::shared_ptr<Expr<typename StlOp::first_argument_type >> ExpLhs;
- typedef std::shared_ptr<Expr<typename StlOp::second_argument_type>> ExpRhs;
-
- BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); }
- virtual typename StlOp::result_type eval() const { return biop_(lhs_->eval(), rhs_->eval()); }
-private:
- ExpLhs lhs_;
- ExpRhs rhs_;
- StlOp biop_;
-};
-
-template <class StlOp> inline
-std::shared_ptr<BinaryExp<StlOp>> makeBiExp(const std::shared_ptr<Expression>& lhs, const std::shared_ptr<Expression>& rhs, StlOp biop) //throw ParsingError
-{
- auto exLeft = std::dynamic_pointer_cast<Expr<typename StlOp::first_argument_type >>(lhs);
- auto exRight = std::dynamic_pointer_cast<Expr<typename StlOp::second_argument_type>>(rhs);
- if (!exLeft || !exRight)
- throw ParsingError();
- return std::make_shared<BinaryExp<StlOp>>(exLeft, exRight, biop);
-}
-
-template <class T>
-struct ConditionalExp : public Expr<T>
-{
- ConditionalExp(const std::shared_ptr<Expr<bool>>& ifExp,
- const std::shared_ptr<Expr<T>>& thenExp,
- const std::shared_ptr<Expr<T>>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); }
-
- virtual typename Expr<T>::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); }
-private:
- std::shared_ptr<Expr<bool>> ifExp_;
- std::shared_ptr<Expr<T>> thenExp_;
- std::shared_ptr<Expr<T>> elseExp_;
-};
-
-struct ConstNumberExp : public Expr<std::int64_t>
-{
- ConstNumberExp(std::int64_t n) : n_(n) {}
- virtual std::int64_t eval() const { return n_; }
-private:
- std::int64_t n_;
-};
-
-struct VariableNumberNExp : public Expr<std::int64_t>
-{
- VariableNumberNExp(std::int64_t& n) : n_(n) {}
- virtual std::int64_t eval() const { return n_; }
-private:
- std::int64_t& n_;
-};
-
-//-------------------------------------------------------------------------------
-
-struct Token
-{
- enum Type
- {
- TK_TERNARY_QUEST,
- TK_TERNARY_COLON,
- TK_OR,
- TK_AND,
- TK_EQUAL,
- TK_NOT_EQUAL,
- TK_LESS,
- TK_LESS_EQUAL,
- TK_GREATER,
- TK_GREATER_EQUAL,
- TK_MODULUS,
- TK_VARIABLE_N,
- TK_CONST_NUMBER,
- TK_BRACKET_LEFT,
- TK_BRACKET_RIGHT,
- TK_END
- };
-
- Token(Type t) : type(t), number(0) {}
- Token(std::int64_t num) : type(TK_CONST_NUMBER), number(num) {}
-
- Type type;
- std::int64_t number; //if type == TK_CONST_NUMBER
-};
-
-class Scanner
-{
-public:
- Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin())
- {
- tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST));
- tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON));
- tokens.push_back(std::make_pair("||", Token::TK_OR ));
- tokens.push_back(std::make_pair("&&", Token::TK_AND ));
- tokens.push_back(std::make_pair("==", Token::TK_EQUAL ));
- tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL ));
- tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL ));
- tokens.push_back(std::make_pair("<" , Token::TK_LESS ));
- tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL));
- tokens.push_back(std::make_pair(">" , Token::TK_GREATER ));
- tokens.push_back(std::make_pair("%" , Token::TK_MODULUS ));
- tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N ));
- tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N ));
- tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT ));
- tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT));
- }
-
- Token nextToken()
- {
- //skip whitespace
- pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); });
-
- if (pos == stream_.end())
- return Token::TK_END;
-
- for (auto iter = tokens.begin(); iter != tokens.end(); ++iter)
- if (startsWith(iter->first))
- {
- pos += iter->first.size();
- return Token(iter->second);
- }
-
- auto digitEnd = std::find_if(pos, stream_.end(), [](char c) { return !zen::isDigit(c); });
-
- if (digitEnd != pos)
- {
- auto number = zen::stringTo<std::int64_t>(std::string(pos, digitEnd));
- pos = digitEnd;
- return number;
- }
-
- throw ParsingError(); //unknown token
- }
-
-private:
- bool startsWith(const std::string& prefix) const
- {
- if (stream_.end() - pos < static_cast<ptrdiff_t>(prefix.size()))
- return false;
- return std::equal(prefix.begin(), prefix.end(), pos);
- }
-
- typedef std::vector<std::pair<std::string, Token::Type>> TokenList;
- TokenList tokens;
-
- const std::string stream_;
- std::string::const_iterator pos;
-};
-
-//-------------------------------------------------------------------------------
-
-class Parser
-{
-public:
- Parser(const std::string& stream, std::int64_t& n) :
- scn(stream),
- tk(scn.nextToken()),
- n_(n) {}
-
- std::shared_ptr<Expr<std::int64_t>> parse() //throw ParsingError; return value always bound!
- {
- auto e = std::dynamic_pointer_cast<Expr<std::int64_t>>(parseExpression()); //throw ParsingError
- if (!e)
- throw ParsingError();
- expectToken(Token::TK_END);
- return e;
- }
-
-private:
- std::shared_ptr<Expression> parseExpression() { return parseConditional(); }//throw ParsingError
-
- std::shared_ptr<Expression> parseConditional() //throw ParsingError
- {
- std::shared_ptr<Expression> e = parseLogicalOr();
-
- if (token().type == Token::TK_TERNARY_QUEST)
- {
- nextToken();
-
- auto ifExp = std::dynamic_pointer_cast<Expr<bool>>(e);
- auto thenExp = std::dynamic_pointer_cast<Expr<std::int64_t>>(parseExpression()); //associativity: <-
-
- expectToken(Token::TK_TERNARY_COLON);
- nextToken();
-
- auto elseExp = std::dynamic_pointer_cast<Expr<std::int64_t>>(parseExpression()); //
- if (!ifExp || !thenExp || !elseExp)
- throw ParsingError();
- return std::make_shared<ConditionalExp<std::int64_t>>(ifExp, thenExp, elseExp);
- }
- return e;
- }
-
- std::shared_ptr<Expression> parseLogicalOr()
- {
- std::shared_ptr<Expression> e = parseLogicalAnd();
- while (token().type == Token::TK_OR) //associativity: ->
- {
- nextToken();
-
- std::shared_ptr<Expression> rhs = parseLogicalAnd();
- e = makeBiExp(e, rhs, std::logical_or<bool>()); //throw ParsingError
- }
- return e;
- }
-
- std::shared_ptr<Expression> parseLogicalAnd()
- {
- std::shared_ptr<Expression> e = parseEquality();
- while (token().type == Token::TK_AND) //associativity: ->
- {
- nextToken();
- std::shared_ptr<Expression> rhs = parseEquality();
-
- e = makeBiExp(e, rhs, std::logical_and<bool>()); //throw ParsingError
- }
- return e;
- }
-
- std::shared_ptr<Expression> parseEquality()
- {
- std::shared_ptr<Expression> e = parseRelational();
-
- Token::Type t = token().type;
- if (t == Token::TK_EQUAL || //associativity: n/a
- t == Token::TK_NOT_EQUAL)
- {
- nextToken();
- std::shared_ptr<Expression> rhs = parseRelational();
-
- if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to <std::int64_t>()); //throw ParsingError
- if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to<std::int64_t>()); //
- }
- return e;
- }
-
- std::shared_ptr<Expression> parseRelational()
- {
- std::shared_ptr<Expression> e = parseMultiplicative();
-
- Token::Type t = token().type;
- if (t == Token::TK_LESS || //associativity: n/a
- t == Token::TK_LESS_EQUAL ||
- t == Token::TK_GREATER ||
- t == Token::TK_GREATER_EQUAL)
- {
- nextToken();
- std::shared_ptr<Expression> rhs = parseMultiplicative();
-
- if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less <std::int64_t>()); //
- if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal <std::int64_t>()); //throw ParsingError
- if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater <std::int64_t>()); //
- if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal<std::int64_t>()); //
- }
- return e;
- }
-
- std::shared_ptr<Expression> parseMultiplicative()
- {
- std::shared_ptr<Expression> e = parsePrimary();
-
- while (token().type == Token::TK_MODULUS) //associativity: ->
- {
- nextToken();
- std::shared_ptr<Expression> rhs = parsePrimary();
-
- //"compile-time" check: n % 0
- if (auto literal = std::dynamic_pointer_cast<ConstNumberExp>(rhs))
- if (literal->eval() == 0)
- throw ParsingError();
-
- e = makeBiExp(e, rhs, std::modulus<std::int64_t>()); //throw ParsingError
- }
- return e;
- }
-
- std::shared_ptr<Expression> parsePrimary()
- {
- if (token().type == Token::TK_VARIABLE_N)
- {
- nextToken();
- return std::make_shared<VariableNumberNExp>(n_);
- }
- else if (token().type == Token::TK_CONST_NUMBER)
- {
- const std::int64_t number = token().number;
- nextToken();
- return std::make_shared<ConstNumberExp>(number);
- }
- else if (token().type == Token::TK_BRACKET_LEFT)
- {
- nextToken();
- std::shared_ptr<Expression> e = parseExpression();
-
- expectToken(Token::TK_BRACKET_RIGHT);
- nextToken();
- return e;
- }
- else
- throw ParsingError();
- }
-
- void nextToken() { tk = scn.nextToken(); }
- const Token& token() const { return tk; }
-
- void expectToken(Token::Type t) //throw ParsingError
- {
- if (token().type != t)
- throw ParsingError();
- }
-
- Scanner scn;
- Token tk;
- std::int64_t& n_;
-};
-}
-
-
-inline
-PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) //throw InvalidPluralForm
-{
- if (pluralCount < 1)
- throw InvalidPluralForm();
-
- forms.resize(pluralCount);
- try
- {
- parse_plural::PluralForm pf(definition); //throw parse_plural::ParsingError
- //PERF_START
-
- //perf: 80ns per iteration max (for arabic)
- //=> 1000 iterations should be fast enough and still detect all "single number forms"
- for (int j = 0; j < 1000; ++j)
- {
- int form = pf.getForm(j);
- if (0 <= form && form < static_cast<int>(forms.size()))
- {
- if (forms[form].count == 0)
- forms[form].firstNumber = j;
- ++forms[form].count;
- }
- else
- throw InvalidPluralForm();
- }
- }
- catch (const parse_plural::ParsingError&)
- {
- throw InvalidPluralForm();
- }
-
- //ensure each form is used at least once:
- if (!std::all_of(forms.begin(), forms.end(), [](const FormInfo& fi) { return fi.count >= 1; }))
- throw InvalidPluralForm();
-}
-
-
-inline
-PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError
-}
-
-#endif // PARSE_PLURAL_H_INCLUDED
diff --git a/lib/perf_check.cpp b/lib/perf_check.cpp
deleted file mode 100644
index bf232add..00000000
--- a/lib/perf_check.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "perf_check.h"
-
-#include <limits>
-//#include <wx/ffile.h>
-#include <zen/basic_math.h>
-#include <zen/i18n.h>
-#include <zen/format_unit.h>
-
-using namespace zen;
-
-
-PerfCheck::PerfCheck(unsigned int windowSizeRemainingTime,
- unsigned int windowSizeSpeed) :
- windowSizeRemTime(windowSizeRemainingTime),
- windowSizeSpeed_(windowSizeSpeed),
- windowMax(std::max(windowSizeRemainingTime, windowSizeSpeed)) {}
-
-
-PerfCheck::~PerfCheck()
-{
- /*
- //write samples to a file
- wxFFile outputFile(wxT("statistics.dat"), wxT("w"));
-
- outputFile.Write(wxT("Time(ms);Objects;Data\n"));
-
- for (auto it = samples.begin(); it != samples.end(); ++it)
- {
- outputFile.Write(numberTo<wxString>(it->first));
- outputFile.Write(wxT(";"));
- outputFile.Write(numberTo<wxString>(it->second.objCount_));
- outputFile.Write(wxT(";"));
- outputFile.Write(numberTo<wxString>(it->second.data_));
- outputFile.Write(wxT("\n"));
- }
- */
-}
-
-
-void PerfCheck::addSample(int itemsCurrent, double dataCurrent, long timeMs)
-{
- samples.insert(samples.end(), std::make_pair(timeMs, Record(itemsCurrent, dataCurrent))); //use fact that time is monotonously ascending
-
- //remove all records earlier than "now - windowMax"
- const long newBegin = timeMs - windowMax;
- auto it = samples.upper_bound(newBegin);
- if (it != samples.begin())
- samples.erase(samples.begin(), --it); //keep one point before newBegin in order to handle "measurement holes"
-}
-
-
-inline
-std::pair<const std::multimap<long, PerfCheck::Record>::value_type*, const std::multimap<long, PerfCheck::Record>::value_type*> PerfCheck::getBlockFromEnd(long windowSize) const
-{
- if (!samples.empty())
- {
- auto itBack = samples.rbegin();
- //find start of records "window"
- auto itFront = samples.upper_bound(itBack->first - windowSize);
- if (itFront != samples.begin())
- --itFront; //one point before window begin in order to handle "measurement holes"
- return std::make_pair(&*itFront, &*itBack);
- }
- return std::make_pair(nullptr, nullptr);
-}
-
-
-zen::Opt<std::wstring> PerfCheck::getRemainingTime(double dataRemaining) const
-{
- auto blk = getBlockFromEnd(windowSizeRemTime);
- if (blk.first && blk.second)
- {
- const auto& itemFront = *blk.first;
- const auto& itemBack = *blk.second;
- //-----------------------------------------------------------------------------------------------
- const long timeDelta = itemBack.first - itemFront.first;
- const double dataDelta = itemBack.second.data_ - itemFront.second.data_;
-
- //objects model logical operations *NOT* disk accesses, so we better play safe and use "bytes" only!
- //http://sourceforge.net/p/freefilesync/feature-requests/197/
-
- if (!numeric::isNull(dataDelta)) //sign(dataRemaining) != sign(dataDelta) usually an error, so show it!
- return remainingTimeToString(dataRemaining * timeDelta / (1000.0 * dataDelta));
- }
- return NoValue();
-}
-
-
-zen::Opt<std::wstring> PerfCheck::getBytesPerSecond() const
-{
- auto blk = getBlockFromEnd(windowSizeSpeed_);
- if (blk.first && blk.second)
- {
- const auto& itemFront = *blk.first;
- const auto& itemBack = *blk.second;
- //-----------------------------------------------------------------------------------------------
- const long timeDelta = itemBack.first - itemFront.first;
- const double dataDelta = itemBack.second.data_ - itemFront.second.data_;
-
- if (timeDelta != 0/* && dataDelta > 0*/)
- return filesizeToShortString(zen::Int64(dataDelta * 1000.0 / timeDelta)) + _("/sec");
- }
- return NoValue();
-}
-
-
-zen::Opt<std::wstring> PerfCheck::getItemsPerSecond() const
-{
- auto blk = getBlockFromEnd(windowSizeSpeed_);
- if (blk.first && blk.second)
- {
- const auto& itemFront = *blk.first;
- const auto& itemBack = *blk.second;
- //-----------------------------------------------------------------------------------------------
- const long timeDelta = itemBack.first - itemFront.first;
- const int itemsDelta = itemBack.second.itemCount_ - itemFront.second.itemCount_;
-
- if (timeDelta != 0)
- return replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsDelta * 1000.0 / timeDelta));
- }
- return NoValue();
-}
-
-
-/*
-class for calculation of remaining time:
-----------------------------------------
-"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x
-
-For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by
-
-z_1 = (r - s * q / p) / ((n + 1) - s * s / p)
-z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s
-
-with
-p := x_0^2 + ... + x_n^2
-q := f_0 x_0 + ... + f_n x_n
-r := f_0 + ... + f_n
-s := x_0 + ... + x_n
-
-=> the time to process N files with amount of data D is: N * z_1 + D * z_2
-
-Problem:
---------
-Times f_0, ..., f_n can be very small so that precision of the PC clock is poor.
-=> Times have to be accumulated to enhance precision:
-Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i
-With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...)
-m * z_1 + z_2 * X = F <=>
-z_1 + z_2 * X / m = F / m
-
-=> we obtain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above
-
-
-Statistics::Statistics(int totalObjectCount, double totalDataAmount, unsigned recordCount) :
- objectsTotal(totalObjectCount),
- dataTotal(totalDataAmount),
- recordsMax(recordCount),
- objectsLast(0),
- dataLast(0),
- timeLast(wxGetLocalTimeMillis()),
- z1_current(0),
- z2_current(0),
- dummyRecordPresent(false) {}
-
-
-wxString Statistics::getRemainingTime(int objectsCurrent, double dataCurrent)
-{
- //add new measurement point
- const int m = objectsCurrent - objectsLast;
- if (m != 0)
- {
- objectsLast = objectsCurrent;
-
- const double X = dataCurrent - dataLast;
- dataLast = dataCurrent;
-
- const zen::Int64 timeCurrent = wxGetLocalTimeMillis();
- const double F = (timeCurrent - timeLast).ToDouble();
- timeLast = timeCurrent;
-
- record newEntry;
- newEntry.x_i = X / m;
- newEntry.f_i = F / m;
-
- //remove dummy record
- if (dummyRecordPresent)
- {
- measurements.pop_back();
- dummyRecordPresent = false;
- }
-
- //insert new record
- measurements.push_back(newEntry);
- if (measurements.size() > recordsMax)
- measurements.pop_front();
- }
- else //dataCurrent increased without processing new objects:
- { //modify last measurement until m != 0
- const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead
- if (!isNull(X))
- {
- const zen::Int64 timeCurrent = wxGetLocalTimeMillis();
- const double F = (timeCurrent - timeLast).ToDouble();
-
- record modifyEntry;
- modifyEntry.x_i = X;
- modifyEntry.f_i = F;
-
- //insert dummy record
- if (!dummyRecordPresent)
- {
- measurements.push_back(modifyEntry);
- if (measurements.size() > recordsMax)
- measurements.pop_front();
- dummyRecordPresent = true;
- }
- else //modify dummy record
- measurements.back() = modifyEntry;
- }
- }
-
- //calculate remaining time based on stored measurement points
- double p = 0;
- double q = 0;
- double r = 0;
- double s = 0;
- for (const record& rec : measurements)
- {
- const double x_i = rec.x_i;
- const double f_i = rec.f_i;
- p += x_i * x_i;
- q += f_i * x_i;
- r += f_i;
- s += x_i;
- }
-
- if (!isNull(p))
- {
- const double n = measurements.size();
- const double tmp = (n - s * s / p);
-
- if (!isNull(tmp) && !isNull(s))
- {
- const double z1 = (r - s * q / p) / tmp;
- const double z2 = (r - n * z1) / s; //not (n + 1) here, since n already is the number of measurements
-
- //refresh current values for z1, z2
- z1_current = z1;
- z2_current = z2;
- }
- }
-
- return formatRemainingTime((objectsTotal - objectsCurrent) * z1_current + (dataTotal - dataCurrent) * z2_current);
-}
-*/
diff --git a/lib/perf_check.h b/lib/perf_check.h
deleted file mode 100644
index 9d82be57..00000000
--- a/lib/perf_check.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef STATISTICS_H_INCLUDED
-#define STATISTICS_H_INCLUDED
-
-#include <map>
-#include <string>
-#include <zen/optional.h>
-
-class PerfCheck
-{
-public:
- PerfCheck(unsigned int windowSizeRemainingTime, //unit: [ms]
- unsigned int windowSizeSpeed); //
- ~PerfCheck();
-
- void addSample(int itemsCurrent, double dataCurrent, long timeMs); //timeMs must be ascending!
-
- zen::Opt<std::wstring> getRemainingTime(double dataRemaining) const;
- zen::Opt<std::wstring> getBytesPerSecond() const; //for window
- zen::Opt<std::wstring> getItemsPerSecond() const; //for window
-
-private:
- struct Record
- {
- Record(int itemCount, double data) : itemCount_(itemCount), data_(data) {}
- int itemCount_;
- double data_; //unit: [bytes]
- };
-
- std::pair<const std::multimap<long, Record>::value_type*,
- const std::multimap<long, Record>::value_type*> getBlockFromEnd(long windowSize) const;
-
- const long windowSizeRemTime; //unit: [ms]
- const long windowSizeSpeed_; //
- const long windowMax;
-
- std::map<long, Record> samples; //time, unit: [ms]
-};
-
-#endif // STATISTICS_H_INCLUDED
diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp
deleted file mode 100644
index 3e3e72f1..00000000
--- a/lib/process_xml.cpp
+++ /dev/null
@@ -1,1577 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "process_xml.h"
-#include <utility>
-#include <zenxml/xml.h>
-#include "ffs_paths.h"
-#include <zen/file_handling.h>
-#include <zen/file_io.h>
-//#include <zen/time.h>
-#include "xml_base.h"
-
-using namespace zen;
-using namespace xmlAccess; //functionally needed for correct overload resolution!!!
-using namespace std::rel_ops;
-
-namespace
-{
-//-------------------------------------------------------------------------------------------------------------------------------
-const int XML_FORMAT_VER_GLOBAL = 1;
-const int XML_FORMAT_VER_FFS_GUI = 3; //for FFS 5.22
-const int XML_FORMAT_VER_FFS_BATCH = 3; //
-//-------------------------------------------------------------------------------------------------------------------------------
-}
-
-XmlType getXmlType(const zen::XmlDoc& doc) //throw()
-{
- if (doc.root().getNameAs<std::string>() == "FreeFileSync")
- {
- std::string type;
- if (doc.root().getAttribute("XmlType", type))
- {
- if (type == "GUI")
- return XML_TYPE_GUI;
- else if (type == "BATCH")
- return XML_TYPE_BATCH;
- else if (type == "GLOBAL")
- return XML_TYPE_GLOBAL;
- }
- }
- return XML_TYPE_OTHER;
-}
-
-
-XmlType xmlAccess::getXmlType(const Zstring& filename) //throw()
-{
- try
- {
- //do NOT use zen::loadStream as it will needlessly load even huge files!
- XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError, quick exit if file is not an FFS XML
- return ::getXmlType(doc);
- }
- catch (const FfsXmlError&)
- {
- return XML_TYPE_OTHER;
- }
-}
-
-
-void setXmlType(XmlDoc& doc, XmlType type) //throw()
-{
- switch (type)
- {
- case XML_TYPE_GUI:
- doc.root().setAttribute("XmlType", "GUI");
- break;
- case XML_TYPE_BATCH:
- doc.root().setAttribute("XmlType", "BATCH");
- break;
- case XML_TYPE_GLOBAL:
- doc.root().setAttribute("XmlType", "GLOBAL");
- break;
- case XML_TYPE_OTHER:
- assert(false);
- break;
- }
-}
-
-//################################################################################################################
-
-Zstring xmlAccess::getGlobalConfigFile()
-{
- return zen::getConfigDir() + Zstr("GlobalSettings.xml");
-}
-
-
-void xmlAccess::OptionalDialogs::resetDialogs()
-{
- warningDependentFolders = true;
- warningFolderPairRaceCondition = true;
- warningSignificantDifference = true;
- warningNotEnoughDiskSpace = true;
- warningUnresolvedConflicts = true;
- warningDatabaseError = true;
- warningRecyclerMissing = true;
- warningInputFieldEmpty = true;
- warningDirectoryLockFailed = true;
- popupOnConfigChange = true;
- confirmSyncStart = true;
- confirmExternalCommandMassInvoke = true;
-}
-
-
-xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) //noexcept
-{
- XmlGuiConfig output;
- output.mainCfg = batchCfg.mainCfg;
-
- switch (batchCfg.handleError)
- {
- case ON_ERROR_POPUP:
- case ON_ERROR_STOP:
- output.handleError = ON_GUIERROR_POPUP;
- break;
- case ON_ERROR_IGNORE:
- output.handleError = ON_GUIERROR_IGNORE;
- break;
- }
- return output;
-}
-
-
-xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) //noexcept
-{
- XmlBatchConfig output; //use default batch-settings
- output.mainCfg = guiCfg.mainCfg;
-
- switch (guiCfg.handleError)
- {
- case ON_GUIERROR_POPUP:
- output.handleError = ON_ERROR_POPUP;
- break;
- case ON_GUIERROR_IGNORE:
- output.handleError = ON_ERROR_IGNORE;
- break;
- }
- return output;
-}
-
-
-xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile) //noexcept
-{
- //try to take over batch-specific settings from reference file if possible
- if (!referenceBatchFile.empty())
- try
- {
- XmlBatchConfig batchCfg;
- readConfig(referenceBatchFile, batchCfg); //throw FfsXmlError
-
- batchCfg.mainCfg = guiCfg.mainCfg;
- return batchCfg;
- }
- catch (xmlAccess::FfsXmlError&) {}
-
- return convertGuiToBatch(guiCfg);
-}
-
-
-namespace
-{
-std::vector<Zstring> splitFilterByLines(const Zstring& filterPhrase)
-{
- if (filterPhrase.empty())
- return std::vector<Zstring>();
- return split(filterPhrase, Zstr('\n'));
-}
-
-Zstring mergeFilterLines(const std::vector<Zstring>& filterLines)
-{
- if (filterLines.empty())
- return Zstring();
- Zstring out = filterLines[0];
- std::for_each(filterLines.begin() + 1, filterLines.end(), [&](const Zstring& line) { out += Zstr('\n'); out += line; });
- return out;
-}
-}
-
-
-namespace zen
-{
-template <> inline
-void writeText(const CompareVariant& value, std::string& output)
-{
- switch (value)
- {
- case zen::CMP_BY_TIME_SIZE:
- output = "TimeAndSize";
- break;
- case zen::CMP_BY_CONTENT:
- output = "Content";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, CompareVariant& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- warn_static("remove after migration. 2013.08.20")
- if (tmp == "ByTimeAndSize") //obsolete
- value = zen::CMP_BY_TIME_SIZE;
- else if (tmp == "ByContent") //obsolete
- value = zen::CMP_BY_CONTENT;
- else
-
- if (tmp == "TimeAndSize")
- value = zen::CMP_BY_TIME_SIZE;
- else if (tmp == "Content")
- value = zen::CMP_BY_CONTENT;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const SyncDirection& value, std::string& output)
-{
- switch (value)
- {
- case SyncDirection::LEFT:
- output = "left";
- break;
- case SyncDirection::RIGHT:
- output = "right";
- break;
- case SyncDirection::NONE:
- output = "none";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, SyncDirection& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- if (tmp == "left")
- value = SyncDirection::LEFT;
- else if (tmp == "right")
- value = SyncDirection::RIGHT;
- else if (tmp == "none")
- value = SyncDirection::NONE;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const OnError& value, std::string& output)
-{
- switch (value)
- {
- case ON_ERROR_IGNORE:
- output = "Ignore";
- break;
- case ON_ERROR_POPUP:
- output = "Popup";
- break;
- case ON_ERROR_STOP:
- output = "Stop";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, OnError& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- warn_static("remove after migration. 2013.08.20")
- if (tmp == "Exit") //obsolete
- value = ON_ERROR_STOP;
- else if (tmp == "Abort") //obsolete: 2013.09.04
- value = ON_ERROR_STOP;
- else
-
- if (tmp == "Ignore")
- value = ON_ERROR_IGNORE;
- else if (tmp == "Popup")
- value = ON_ERROR_POPUP;
- else if (tmp == "Stop")
- value = ON_ERROR_STOP;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const OnGuiError& value, std::string& output)
-{
- switch (value)
- {
- case ON_GUIERROR_IGNORE:
- output = "Ignore";
- break;
- case ON_GUIERROR_POPUP:
- output = "Popup";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, OnGuiError& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- if (tmp == "Ignore")
- value = ON_GUIERROR_IGNORE;
- else if (tmp == "Popup")
- value = ON_GUIERROR_POPUP;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const FileIconSize& value, std::string& output)
-{
- switch (value)
- {
- case ICON_SIZE_SMALL:
- output = "Small";
- break;
- case ICON_SIZE_MEDIUM:
- output = "Medium";
- break;
- case ICON_SIZE_LARGE:
- output = "Large";
- break;
- }
-}
-
-
-template <> inline
-bool readText(const std::string& input, FileIconSize& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- if (tmp == "Small")
- value = ICON_SIZE_SMALL;
- else if (tmp == "Medium")
- value = ICON_SIZE_MEDIUM;
- else if (tmp == "Large")
- value = ICON_SIZE_LARGE;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const DeletionPolicy& value, std::string& output)
-{
- switch (value)
- {
- case DELETE_PERMANENTLY:
- output = "Permanent";
- break;
- case DELETE_TO_RECYCLER:
- output = "RecycleBin";
- break;
- case DELETE_TO_VERSIONING:
- output = "Versioning";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, DeletionPolicy& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- //------------------
- warn_static("remove after migration?")
- if (tmp == "DeletePermanently")//obsolete name
- value = DELETE_PERMANENTLY;
- else if (tmp == "MoveToRecycleBin")//obsolete name
- value = DELETE_TO_RECYCLER;
- else if (tmp == "MoveToCustomDirectory")//obsolete name
- value = DELETE_TO_VERSIONING;
- else
-
- if (tmp == "Permanent")
- value = DELETE_PERMANENTLY;
- else if (tmp == "RecycleBin")
- value = DELETE_TO_RECYCLER;
- else if (tmp == "Versioning")
- value = DELETE_TO_VERSIONING;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const SymLinkHandling& value, std::string& output)
-{
- switch (value)
- {
- case SYMLINK_EXCLUDE:
- output = "Exclude";
- break;
- case SYMLINK_USE_DIRECTLY:
- output = "Direct";
- break;
- case SYMLINK_FOLLOW_LINK:
- output = "Follow";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, SymLinkHandling& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- warn_static("remove after migration. 2013.08.20")
- if (tmp == "UseDirectly") //obsolete!
- value = SYMLINK_USE_DIRECTLY;
- else if (tmp == "FollowLink") //obsolete!
- value = SYMLINK_FOLLOW_LINK;
- else if (tmp == "Ignore") //obsolete!
- value = SYMLINK_EXCLUDE;
- else
-
- if (tmp == "Exclude")
- value = SYMLINK_EXCLUDE;
- else if (tmp == "Direct")
- value = SYMLINK_USE_DIRECTLY;
- else if (tmp == "Follow")
- value = SYMLINK_FOLLOW_LINK;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const ColumnTypeRim& value, std::string& output)
-{
- switch (value)
- {
- case COL_TYPE_DIRECTORY:
- output = "Base";
- break;
- case COL_TYPE_FULL_PATH:
- output = "Full";
- break;
- case COL_TYPE_REL_PATH:
- output = "Rel";
- break;
- case COL_TYPE_FILENAME:
- output = "Name";
- break;
- case COL_TYPE_SIZE:
- output = "Size";
- break;
- case COL_TYPE_DATE:
- output = "Date";
- break;
- case COL_TYPE_EXTENSION:
- output = "Ext";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, ColumnTypeRim& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- if (tmp == "Base")
- value = COL_TYPE_DIRECTORY;
- else if (tmp == "Full")
- value = COL_TYPE_FULL_PATH;
- else if (tmp == "Rel")
- value = COL_TYPE_REL_PATH;
- else if (tmp == "Name")
- value = COL_TYPE_FILENAME;
- else if (tmp == "Size")
- value = COL_TYPE_SIZE;
- else if (tmp == "Date")
- value = COL_TYPE_DATE;
- else if (tmp == "Ext")
- value = COL_TYPE_EXTENSION;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const ColumnTypeNavi& value, std::string& output)
-{
- switch (value)
- {
- case COL_TYPE_NAVI_BYTES:
- output = "Bytes";
- break;
- case COL_TYPE_NAVI_DIRECTORY:
- output = "Tree";
- break;
- case COL_TYPE_NAVI_ITEM_COUNT:
- output = "Count";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, ColumnTypeNavi& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- if (tmp == "Bytes")
- value = COL_TYPE_NAVI_BYTES;
- else if (tmp == "Tree")
- value = COL_TYPE_NAVI_DIRECTORY;
- else if (tmp == "Count")
- value = COL_TYPE_NAVI_ITEM_COUNT;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const UnitSize& value, std::string& output)
-{
- switch (value)
- {
- case USIZE_NONE:
- output = "None";
- break;
- case USIZE_BYTE:
- output = "Byte";
- break;
- case USIZE_KB:
- output = "KB";
- break;
- case USIZE_MB:
- output = "MB";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, UnitSize& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- warn_static("remove after migration. 2013.08.20")
- if (tmp == "Inactive") //obsolete!
- value = USIZE_NONE;
- else
-
- if (tmp == "None")
- value = USIZE_NONE;
- else if (tmp == "Byte")
- value = USIZE_BYTE;
- else if (tmp == "KB")
- value = USIZE_KB;
- else if (tmp == "MB")
- value = USIZE_MB;
- else
- return false;
- return true;
-}
-
-template <> inline
-void writeText(const UnitTime& value, std::string& output)
-{
- switch (value)
- {
- case UTIME_NONE:
- output = "None";
- break;
- case UTIME_TODAY:
- output = "Today";
- break;
- case UTIME_THIS_MONTH:
- output = "Month";
- break;
- case UTIME_THIS_YEAR:
- output = "Year";
- break;
- case UTIME_LAST_X_DAYS:
- output = "x-days";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, UnitTime& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- warn_static("remove after migration. 2013.08.20")
- if (tmp == "Inactive") //obsolete!
- value = UTIME_NONE;
- else
-
- if (tmp == "None")
- value = UTIME_NONE;
- else if (tmp == "Today")
- value = UTIME_TODAY;
- else if (tmp == "Month")
- value = UTIME_THIS_MONTH;
- else if (tmp == "Year")
- value = UTIME_THIS_YEAR;
- else if (tmp == "x-days")
- value = UTIME_LAST_X_DAYS;
- else
- return false;
- return true;
-}
-
-template <> inline
-void writeText(const VersioningStyle& value, std::string& output)
-{
- switch (value)
- {
- case VER_STYLE_REPLACE:
- output = "Replace";
- break;
- case VER_STYLE_ADD_TIMESTAMP:
- output = "TimeStamp";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, VersioningStyle& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- warn_static("remove after migration. 2013.08.20")
- if (tmp == "AddTimeStamp") //obsolete
- value = VER_STYLE_ADD_TIMESTAMP;
- else
-
- if (tmp == "Replace")
- value = VER_STYLE_REPLACE;
- else if (tmp == "TimeStamp")
- value = VER_STYLE_ADD_TIMESTAMP;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-void writeText(const DirectionConfig::Variant& value, std::string& output)
-{
- switch (value)
- {
- case DirectionConfig::TWOWAY:
- output = "TwoWay";
- break;
- case DirectionConfig::MIRROR:
- output = "Mirror";
- break;
- case DirectionConfig::UPDATE:
- output = "Update";
- break;
- case DirectionConfig::CUSTOM:
- output = "Custom";
- break;
- }
-}
-
-template <> inline
-bool readText(const std::string& input, DirectionConfig::Variant& value)
-{
- std::string tmp = input;
- zen::trim(tmp);
- warn_static("remove after migration. 2013.08.20")
- if (tmp == "Automatic") //obsolete!
- value = DirectionConfig::TWOWAY;
- else
-
- if (tmp == "TwoWay")
- value = DirectionConfig::TWOWAY;
- else if (tmp == "Mirror")
- value = DirectionConfig::MIRROR;
- else if (tmp == "Update")
- value = DirectionConfig::UPDATE;
- else if (tmp == "Custom")
- value = DirectionConfig::CUSTOM;
- else
- return false;
- return true;
-}
-
-
-template <> inline
-bool readStruc(const XmlElement& input, ColumnAttributeRim& value)
-{
- XmlIn in(input);
- bool rv1 = in.attribute("Type", value.type_);
- bool rv2 = in.attribute("Visible", value.visible_);
- bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0
- bool rv4 = in.attribute("Stretch", value.stretch_);
- return rv1 && rv2 && rv3 && rv4;
-}
-
-template <> inline
-void writeStruc(const ColumnAttributeRim& value, XmlElement& output)
-{
- XmlOut out(output);
- out.attribute("Type", value.type_);
- out.attribute("Visible", value.visible_);
- out.attribute("Width", value.offset_);
- out.attribute("Stretch", value.stretch_);
-}
-
-
-template <> inline
-bool readStruc(const XmlElement& input, ColumnAttributeNavi& value)
-{
- XmlIn in(input);
- bool rv1 = in.attribute("Type", value.type_);
- bool rv2 = in.attribute("Visible", value.visible_);
- bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0
- bool rv4 = in.attribute("Stretch", value.stretch_);
- return rv1 && rv2 && rv3 && rv4;
-}
-
-template <> inline
-void writeStruc(const ColumnAttributeNavi& value, XmlElement& output)
-{
- XmlOut out(output);
- out.attribute("Type", value.type_);
- out.attribute("Visible", value.visible_);
- out.attribute("Width", value.offset_);
- out.attribute("Stretch", value.stretch_);
-}
-
-
-template <> inline
-bool readStruc(const XmlElement& input, ViewFilterDefault& value)
-{
- XmlIn in(input);
-
- bool success = true;
- auto readAttr = [&](XmlIn& elemIn, const char name[], bool& v)
- {
- if (!elemIn.attribute(name, v))
- success = false;
- };
-
- XmlIn catView = in["CategoryView"];
- readAttr(catView, "LeftOnly" , value.leftOnly);
- readAttr(catView, "RightOnly" , value.rightOnly);
- readAttr(catView, "LeftNewer" , value.leftNewer);
- readAttr(catView, "RightNewer", value.rightNewer);
- readAttr(catView, "Different" , value.different);
- readAttr(catView, "Equal" , value.equal);
- readAttr(catView, "Conflict" , value.conflict);
-
- XmlIn actView = in["ActionView"];
- readAttr(actView, "CreateLeft" , value.createLeft);
- readAttr(actView, "CreateRight", value.createRight);
- readAttr(actView, "UpdateLeft" , value.updateLeft);
- readAttr(actView, "UpdateRight", value.updateRight);
- readAttr(actView, "DeleteLeft" , value.deleteLeft);
- readAttr(actView, "DeleteRight", value.deleteRight);
- readAttr(actView, "DoNothing" , value.doNothing);
-
- return success; //[!] avoid short-circuit evaluation above
-}
-
-template <> inline
-void writeStruc(const ViewFilterDefault& value, XmlElement& output)
-{
- XmlOut out(output);
-
- XmlOut catView = out["CategoryView"];
- catView.attribute("LeftOnly" , value.leftOnly);
- catView.attribute("RightOnly" , value.rightOnly);
- catView.attribute("LeftNewer" , value.leftNewer);
- catView.attribute("RightNewer", value.rightNewer);
- catView.attribute("Different" , value.different);
- catView.attribute("Equal" , value.equal);
- catView.attribute("Conflict" , value.conflict);
-
- XmlOut actView = out["ActionView"];
- actView.attribute("CreateLeft" , value.createLeft);
- actView.attribute("CreateRight", value.createRight);
- actView.attribute("UpdateLeft" , value.updateLeft);
- actView.attribute("UpdateRight", value.updateRight);
- actView.attribute("DeleteLeft" , value.deleteLeft);
- actView.attribute("DeleteRight", value.deleteRight);
- actView.attribute("DoNothing" , value.doNothing);
-}
-
-
-template <> inline
-bool readStruc(const XmlElement& input, ConfigHistoryItem& value)
-{
- XmlIn in(input);
- bool rv1 = in(value.configFile);
- //bool rv2 = in.attribute("LastUsed", value.lastUseTime);
- return rv1 /*&& rv2*/;
-}
-
-
-template <> inline
-void writeStruc(const ConfigHistoryItem& value, XmlElement& output)
-{
- XmlOut out(output);
- out(value.configFile);
- //out.attribute("LastUsed", value.lastUseTime);
-}
-}
-
-
-namespace
-{
-void readConfig(const XmlIn& in, CompConfig& cmpConfig)
-{
- in["Variant" ](cmpConfig.compareVar);
-
- warn_static("remove check after migration. 2013.09.7")
- if (in["HandleSymlinks"])//obsolete name
- in["HandleSymlinks"](cmpConfig.handleSymlinks);
- else
- in["Symlinks"](cmpConfig.handleSymlinks);
-}
-
-
-void readConfig(const XmlIn& in, DirectionConfig& directCfg)
-{
- in["Variant"](directCfg.var);
-
- XmlIn inCustDir = in["CustomDirections"];
- inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly);
- inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly);
- inCustDir["LeftNewer" ](directCfg.custom.leftNewer);
- inCustDir["RightNewer"](directCfg.custom.rightNewer);
- inCustDir["Different" ](directCfg.custom.different);
- inCustDir["Conflict" ](directCfg.custom.conflict);
-
- warn_static("remove check after migration. 2013.08.17")
- if (in["DetectMovedFiles"]) //new value: remove check
- in["DetectMovedFiles"](directCfg.detectMovedFiles);
-}
-
-
-void readConfig(const XmlIn& in, SyncConfig& syncCfg)
-{
- readConfig(in, syncCfg.directionCfg);
-
- in["DeletionPolicy"](syncCfg.handleDeletion);
-
- warn_static("remove after migration?")
- if (in["CustomDeletionFolder"])
- in["CustomDeletionFolder"](syncCfg.versioningDirectory);//obsolete name
- else
- in["VersioningFolder"](syncCfg.versioningDirectory);
-
- warn_static("remove after migration?")
- if (in["VersioningFolder"] &&
- in["VersioningFolder"].get()->getAttribute("Style", syncCfg.versioningStyle)) //new parameter, do not complain when missing
- ;
- else if (in["VersioningStyle"]) //obsolete name
- in["VersioningStyle"](syncCfg.versioningStyle);
- else
- syncCfg.versioningStyle = VER_STYLE_ADD_TIMESTAMP; //obsolete fallback
-}
-
-
-void readConfig(const XmlIn& in, FilterConfig& filter)
-{
- warn_static("remove after migration?")
- auto haveFilterAsSingleString = [&]() -> bool
- {
- if (in["Include"])
- if (auto elem = in["Include"].get())
- {
- std::string tmp;
- if (elem->getValue(tmp))
- return !tmp.empty();
- }
- return false;
- };
- if (haveFilterAsSingleString()) //obsolete style
- {
- in["Include"](filter.includeFilter);
- in["Exclude"](filter.excludeFilter);
- }
- else
- {
- std::vector<Zstring> tmp = splitFilterByLines(filter.includeFilter); //default value
- in["Include"](tmp);
- filter.includeFilter = mergeFilterLines(tmp);
-
- std::vector<Zstring> tmp2 = splitFilterByLines(filter.excludeFilter); //default value
- in["Exclude"](tmp2);
- filter.excludeFilter = mergeFilterLines(tmp2);
- }
-
- in["TimeSpan"](filter.timeSpan);
- warn_static("remove after migration?")
- if (in["UnitTimeSpan"]) in["UnitTimeSpan"](filter.unitTimeSpan);//obsolete name
- else
- in["TimeSpan"].attribute("Type", filter.unitTimeSpan);
-
- in["SizeMin"](filter.sizeMin);
- if (in["UnitSizeMin"]) in["UnitSizeMin"](filter.unitSizeMin);//obsolete name
- else
- in["SizeMin"].attribute("Unit", filter.unitSizeMin);
-
- in["SizeMax"](filter.sizeMax);
- if (in["UnitSizeMax"]) in["UnitSizeMax"](filter.unitSizeMax);//obsolete name
- else
- in["SizeMax"].attribute("Unit", filter.unitSizeMax);
-}
-
-
-void readConfig(const XmlIn& in, FolderPairEnh& enhPair)
-{
- //read folder pairs
- in["Left" ](enhPair.leftDirectory);
- in["Right"](enhPair.rightDirectory);
-
- //###########################################################
- //alternate comp configuration (optional)
- if (XmlIn inAltCmp = in["CompareConfig"])
- {
- CompConfig altCmpCfg;
- readConfig(inAltCmp, altCmpCfg);
-
- enhPair.altCmpConfig = std::make_shared<CompConfig>(altCmpCfg);
- }
- //###########################################################
- //alternate sync configuration (optional)
- if (XmlIn inAltSync = in["SyncConfig"])
- {
- SyncConfig altSyncCfg;
- readConfig(inAltSync, altSyncCfg);
-
- enhPair.altSyncConfig = std::make_shared<SyncConfig>(altSyncCfg);
- }
-
- //###########################################################
- //alternate filter configuration
- if (XmlIn inLocFilter = in["LocalFilter"])
- readConfig(inLocFilter, enhPair.localFilter);
-}
-
-
-void readConfig(const XmlIn& in, MainConfiguration& mainCfg)
-{
- //read compare settings
- XmlIn inMain = in["MainConfig"];
-
- readConfig(inMain["Comparison"], mainCfg.cmpConfig);
- //###########################################################
-
- //read sync configuration
- readConfig(inMain["SyncConfig"], mainCfg.syncCfg);
- //###########################################################
-
- //read filter settings
- readConfig(inMain["GlobalFilter"], mainCfg.globalFilter);
-
- //###########################################################
- //read all folder pairs
- mainCfg.additionalPairs.clear();
-
- bool firstItem = true;
- for (XmlIn inPair = inMain["FolderPairs"]["Pair"]; inPair; inPair.next())
- {
- FolderPairEnh newPair;
- readConfig(inPair, newPair);
-
- if (firstItem)
- {
- firstItem = false;
- mainCfg.firstPair = newPair; //set first folder pair
- }
- else
- mainCfg.additionalPairs.push_back(newPair); //set additional folder pairs
- }
-
- warn_static("remove after migration?")
- if (inMain["ExecuteWhenFinished"]) inMain["ExecuteWhenFinished"](mainCfg.onCompletion); //obsolete name
- else
- inMain["OnCompletion"](mainCfg.onCompletion);
-}
-
-
-void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config)
-{
- readConfig(in, config.mainCfg); //read main config
-
- //read GUI specific config data
- XmlIn inGuiCfg = in["GuiConfig"];
-
- warn_static("remove after migration?")
- if (inGuiCfg["HideFiltered" ]) //obsolete name
- inGuiCfg["HideFiltered" ](config.hideExcludedItems);
- else if (inGuiCfg["ShowFiltered" ]) //obsolete name
- {
- inGuiCfg["ShowFiltered"](config.hideExcludedItems);
- config.hideExcludedItems = !config.hideExcludedItems;
- }
- else
- inGuiCfg["HideExcluded"](config.hideExcludedItems);
-
- inGuiCfg["HandleError" ](config.handleError);
-
- warn_static("remove after migration?")
- if (inGuiCfg["SyncPreviewActive"]) //obsolete name
- inGuiCfg["SyncPreviewActive"](config.highlightSyncAction);
- else
- {
- std::string val;
- if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!?
- config.highlightSyncAction = val == "Action";
- }
-}
-
-
-void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config)
-{
- readConfig(in, config.mainCfg); //read main config
-
- //read GUI specific config data
- XmlIn inBatchCfg = in["BatchConfig"];
-
- inBatchCfg["HandleError" ](config.handleError);
- inBatchCfg["ShowProgress" ](config.showProgress);
-
- warn_static("remove after migration?")
- if (inBatchCfg["LogfileDirectory"]) inBatchCfg["LogfileDirectory"](config.logFileDirectory); //obsolete name
- else
- inBatchCfg["LogfileFolder"](config.logFileDirectory);
-
- if (inBatchCfg["LogfileCountMax" ]) inBatchCfg["LogfileCountMax"](config.logfilesCountLimit); //obsolete name
- else
- inBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit);
-}
-
-
-void readConfig(const XmlIn& in, XmlGlobalSettings& config)
-{
- XmlIn inShared = in["Shared"];
-
- inShared["Language"].attribute("Id", config.programLanguage);
-
- inShared["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy);
- inShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles);
- inShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions);
- inShared["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount);
- inShared["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay);
- inShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance);
- inShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority);
- inShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile);
- inShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy);
- inShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax);
-
- XmlIn inOpt = inShared["OptionalDialogs"];
- inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
- inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace);
- inOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference);
- inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing);
- inOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty);
- inOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError);
- inOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders);
- inOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition);
- inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
- inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
- inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
- inOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke);
-
- //gui specific global settings (optional)
- XmlIn inGui = in["Gui"];
- XmlIn inWnd = inGui["MainDialog"];
-
- //read application window size and position
- inWnd.attribute("Width", config.gui.dlgSize.x);
- inWnd.attribute("Height", config.gui.dlgSize.y);
- inWnd.attribute("PosX", config.gui.dlgPos.x);
- inWnd.attribute("PosY", config.gui.dlgPos.y);
- inWnd.attribute("Maximized", config.gui.isMaximized);
-
- XmlIn inManualDel = inWnd["ManualDeletion"];
- //inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides);
- inManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion);
-
- inWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase);
- inWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible);
-
- //###########################################################
-
- XmlIn inOverview = inWnd["OverviewPanel"];
- inOverview.attribute("ShowPercentage", config.gui.showPercentBar);
- inOverview.attribute("SortByColumn", config.gui.naviLastSortColumn);
- inOverview.attribute("SortAscending", config.gui.naviLastSortAscending);
-
- //read column attributes
- XmlIn inColNavi = inOverview["Columns"];
- inColNavi(config.gui.columnAttribNavi);
-
- XmlIn inMainGrid = inWnd["MainGrid"];
- inMainGrid.attribute("ShowIcons", config.gui.showIcons);
- inMainGrid.attribute("IconSize", config.gui.iconSize);
- inMainGrid.attribute("SashOffset", config.gui.sashOffset);
-
- XmlIn inColLeft = inMainGrid["ColumnsLeft"];
- inColLeft(config.gui.columnAttribLeft);
-
- XmlIn inColRight = inMainGrid["ColumnsRight"];
- inColRight(config.gui.columnAttribRight);
- //###########################################################
-
- inWnd["ViewFilterDefault"](config.gui.viewFilterDefault);
- inWnd["Perspective2" ](config.gui.guiPerspectiveLast);
-
- std::vector<Zstring> tmp = splitFilterByLines(config.gui.defaultExclusionFilter); //default value
- inGui["DefaultExclusionFilter"](tmp);
- config.gui.defaultExclusionFilter = mergeFilterLines(tmp);
-
- //load config file history
- inGui["LastUsedConfig"](config.gui.lastUsedConfigFiles);
-
- inGui["ConfigHistory"](config.gui.cfgFileHistory);
- inGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax);
-
- inGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft);
- inGui["FolderHistoryRight"](config.gui.folderHistoryRight);
- inGui["FolderHistoryLeft"].attribute("MaxSize", config.gui.folderHistMax);
-
- inGui["OnCompletionHistory"](config.gui.onCompletionHistory);
- inGui["OnCompletionHistory"].attribute("MaxSize", config.gui.onCompletionHistoryMax);
-
- //external applications
- inGui["ExternalApplications"](config.gui.externelApplications);
-
- //last update check
- inGui["LastVersionCheck"](config.gui.lastUpdateCheck);
-
- //batch specific global settings
- //XmlIn inBatch = in["Batch"];
-}
-
-
-bool needsMigration(const XmlDoc& doc, int currentXmlFormatVer)
-{
- //(try to) migrate old configuration if needed
- int xmlFormatVer = 0;
- /*bool success = */doc.root().getAttribute("XmlFormat", xmlFormatVer);
- return xmlFormatVer < currentXmlFormatVer;
-}
-
-
-template <class ConfigType>
-void readConfig(const Zstring& filename, XmlType type, ConfigType& cfg, int currentXmlFormatVer, bool& needMigration) //throw FfsXmlError
-{
- XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError
-
- if (getXmlType(doc) != type) //throw()
- throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)));
-
- XmlIn in(doc);
- ::readConfig(in, cfg);
-
- if (in.errorsOccured())
- throw FfsXmlError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" +
- getErrorMessageFormatted(in.getErrorsAs<std::wstring>()), FfsXmlError::WARNING);
-
- //(try to) migrate old configuration if needed
- needMigration = needsMigration(doc, currentXmlFormatVer);
-}
-}
-
-
-void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlGuiConfig& cfg)
-{
- bool needMigration = false;
- ::readConfig(filename, XML_TYPE_GUI, cfg, XML_FORMAT_VER_FFS_GUI, needMigration); //throw FfsXmlError
-
- if (needMigration) //(try to) migrate old configuration
- try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ }
- catch (FfsXmlError&) { assert(false); } //don't bother user!
-}
-
-
-void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlBatchConfig& cfg)
-{
- bool needMigration = false;
- ::readConfig(filename, XML_TYPE_BATCH, cfg, XML_FORMAT_VER_FFS_BATCH, needMigration); //throw FfsXmlError
-
- if (needMigration) //(try to) migrate old configuration
- try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ }
- catch (FfsXmlError&) { assert(false); } //don't bother user!
-}
-
-
-void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& cfg)
-{
- bool needMigration = false;
- ::readConfig(getGlobalConfigFile(), XML_TYPE_GLOBAL, cfg, XML_FORMAT_VER_GLOBAL, needMigration); //throw FfsXmlError
-}
-
-
-namespace
-{
-template <class XmlCfg>
-XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filename, int currentXmlFormatVer, std::unique_ptr<FfsXmlError>& warning) //nothrow
-{
- XmlCfg cfg;
- XmlIn in(doc);
- ::readConfig(in, cfg);
-
- if (in.errorsOccured())
- {
- if (!warning)
- warning = make_unique<FfsXmlError>(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" +
- getErrorMessageFormatted(in.getErrorsAs<std::wstring>()), FfsXmlError::WARNING);
- }
- else
- {
- //(try to) migrate old configuration if needed
- if (needsMigration(doc, currentXmlFormatVer))
- try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ }
- catch (FfsXmlError&) { assert(false); } //don't bother user!
- }
- return cfg;
-}
-}
-
-
-void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config) //throw FfsXmlError
-{
- assert(!filenames.empty());
-
- std::vector<zen::MainConfiguration> mainCfgs;
- std::unique_ptr<FfsXmlError> warning;
-
- for (auto it = filenames.begin(); it != filenames.end(); ++it)
- {
- const Zstring& filename = *it;
- const bool firstItem = it == filenames.begin(); //init all non-"mainCfg" settings with first config file
-
- XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError
- //do NOT use zen::loadStream as it will superfluously load even huge files!
-
- switch (::getXmlType(doc))
- {
- case XML_TYPE_GUI:
- {
- XmlGuiConfig guiCfg = parseConfig<XmlGuiConfig>(doc, filename, XML_FORMAT_VER_FFS_GUI, warning); //nothrow
- if (firstItem)
- config = guiCfg;
- mainCfgs.push_back(guiCfg.mainCfg);
- }
- break;
-
- case XML_TYPE_BATCH:
- {
- XmlBatchConfig batchCfg = parseConfig<XmlBatchConfig>(doc, filename, XML_FORMAT_VER_FFS_BATCH, warning); //nothrow
- if (firstItem)
- config = convertBatchToGui(batchCfg);
- mainCfgs.push_back(batchCfg.mainCfg);
- }
- break;
-
- case XML_TYPE_GLOBAL:
- case XML_TYPE_OTHER:
- throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)));
- }
- }
-
- config.mainCfg = merge(mainCfgs);
-
- if (warning)
- throw* warning;
-}
-
-//################################################################################################
-
-namespace
-{
-void writeConfig(const CompConfig& cmpConfig, XmlOut& out)
-{
- out["Variant" ](cmpConfig.compareVar);
- out["Symlinks"](cmpConfig.handleSymlinks);
-}
-
-
-void writeConfig(const DirectionConfig& directCfg, XmlOut& out)
-{
- out["Variant"](directCfg.var);
-
- XmlOut outCustDir = out["CustomDirections"];
- outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly);
- outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly);
- outCustDir["LeftNewer" ](directCfg.custom.leftNewer);
- outCustDir["RightNewer"](directCfg.custom.rightNewer);
- outCustDir["Different" ](directCfg.custom.different);
- outCustDir["Conflict" ](directCfg.custom.conflict);
-
- out["DetectMovedFiles"](directCfg.detectMovedFiles);
-}
-
-
-void writeConfig(const SyncConfig& syncCfg, XmlOut& out)
-{
- writeConfig(syncCfg.directionCfg, out);
-
- out["DeletionPolicy" ](syncCfg.handleDeletion);
- out["VersioningFolder"](syncCfg.versioningDirectory);
- //out["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit);
- out["VersioningFolder"].attribute("Style", syncCfg.versioningStyle);
-}
-
-
-void writeConfig(const FilterConfig& filter, XmlOut& out)
-{
- out["Include"](splitFilterByLines(filter.includeFilter));
- out["Exclude"](splitFilterByLines(filter.excludeFilter));
-
- out["TimeSpan"](filter.timeSpan);
- out["TimeSpan"].attribute("Type", filter.unitTimeSpan);
-
- out["SizeMin"](filter.sizeMin);
- out["SizeMin"].attribute("Unit", filter.unitSizeMin);
-
- out["SizeMax"](filter.sizeMax);
- out["SizeMax"].attribute("Unit", filter.unitSizeMax);
-}
-
-
-void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out)
-{
- XmlOut outPair = out.ref().addChild("Pair");
-
- //read folder pairs
- outPair["Left" ](enhPair.leftDirectory);
- outPair["Right"](enhPair.rightDirectory);
-
- //###########################################################
- //alternate comp configuration (optional)
- if (enhPair.altCmpConfig.get())
- {
- XmlOut outAlt = outPair["CompareConfig"];
- writeConfig(*enhPair.altCmpConfig, outAlt);
- }
- //###########################################################
- //alternate sync configuration (optional)
- if (enhPair.altSyncConfig.get())
- {
- XmlOut outAltSync = outPair["SyncConfig"];
- writeConfig(*enhPair.altSyncConfig, outAltSync);
- }
-
- //###########################################################
- //alternate filter configuration
- if (enhPair.localFilter != FilterConfig()) //don't spam .ffs_gui file with default filter entries
- {
- XmlOut outFilter = outPair["LocalFilter"];
- writeConfig(enhPair.localFilter, outFilter);
- }
-}
-
-
-void writeConfig(const MainConfiguration& mainCfg, XmlOut& out)
-{
- XmlOut outMain = out["MainConfig"];
-
- XmlOut outCmp = outMain["Comparison"];
-
- writeConfig(mainCfg.cmpConfig, outCmp);
- //###########################################################
-
- XmlOut outSync = outMain["SyncConfig"];
-
- writeConfig(mainCfg.syncCfg, outSync);
- //###########################################################
-
- XmlOut outFilter = outMain["GlobalFilter"];
- //write filter settings
- writeConfig(mainCfg.globalFilter, outFilter);
-
- //###########################################################
- //write all folder pairs
-
- XmlOut outFp = outMain["FolderPairs"];
-
- //write first folder pair
- writeConfigFolderPair(mainCfg.firstPair, outFp);
-
- //write additional folder pairs
- std::for_each(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(),
- [&](const FolderPairEnh& fp) { writeConfigFolderPair(fp, outFp); });
-
- outMain["OnCompletion"](mainCfg.onCompletion);
-}
-
-
-void writeConfig(const XmlGuiConfig& config, XmlOut& out)
-{
- writeConfig(config.mainCfg, out); //write main config
-
- //write GUI specific config data
- XmlOut outGuiCfg = out["GuiConfig"];
-
- outGuiCfg["HideExcluded" ](config.hideExcludedItems);
- outGuiCfg["HandleError" ](config.handleError);
- outGuiCfg["MiddleGridView"](config.highlightSyncAction ? "Action" : "Category"); //refactor into enum!?
-}
-
-void writeConfig(const XmlBatchConfig& config, XmlOut& out)
-{
-
- writeConfig(config.mainCfg, out); //write main config
-
- //write GUI specific config data
- XmlOut outBatchCfg = out["BatchConfig"];
-
- outBatchCfg["HandleError" ](config.handleError);
- outBatchCfg["ShowProgress" ](config.showProgress);
- outBatchCfg["LogfileFolder" ](config.logFileDirectory);
- outBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit);
-}
-
-
-void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
-{
- XmlOut outShared = out["Shared"];
-
- outShared["Language"].attribute("Id", config.programLanguage);
-
- outShared["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy);
- outShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles);
- outShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions);
- outShared["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount);
- outShared["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay);
- outShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance);
- outShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority);
- outShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile);
- outShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy);
- outShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax);
-
- XmlOut outOpt = outShared["OptionalDialogs"];
- outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
- outOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace);
- outOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference);
- outOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing);
- outOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty);
- outOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError);
- outOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders);
- outOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition);
- outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
- outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
- outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
- outOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke);
-
- //gui specific global settings (optional)
- XmlOut outGui = out["Gui"];
- XmlOut outWnd = outGui["MainDialog"];
-
- //write application window size and position
- outWnd.attribute("Width", config.gui.dlgSize.x);
- outWnd.attribute("Height", config.gui.dlgSize.y);
- outWnd.attribute("PosX", config.gui.dlgPos.x);
- outWnd.attribute("PosY", config.gui.dlgPos.y);
- outWnd.attribute("Maximized", config.gui.isMaximized);
-
- XmlOut outManualDel = outWnd["ManualDeletion"];
- //outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides);
- outManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion);
-
- outWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase);
- outWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible);
-
- //###########################################################
-
- XmlOut outOverview = outWnd["OverviewPanel"];
- outOverview.attribute("ShowPercentage", config.gui.showPercentBar);
- outOverview.attribute("SortByColumn", config.gui.naviLastSortColumn);
- outOverview.attribute("SortAscending", config.gui.naviLastSortAscending);
-
- //write column attributes
- XmlOut outColNavi = outOverview["Columns"];
- outColNavi(config.gui.columnAttribNavi);
-
- XmlOut outMainGrid = outWnd["MainGrid"];
- outMainGrid.attribute("ShowIcons", config.gui.showIcons);
- outMainGrid.attribute("IconSize", config.gui.iconSize);
- outMainGrid.attribute("SashOffset", config.gui.sashOffset);
-
- XmlOut outColLeft = outMainGrid["ColumnsLeft"];
- outColLeft(config.gui.columnAttribLeft);
-
- XmlOut outColRight = outMainGrid["ColumnsRight"];
- outColRight(config.gui.columnAttribRight);
- //###########################################################
-
- outWnd["ViewFilterDefault"](config.gui.viewFilterDefault);
- outWnd["Perspective2" ](config.gui.guiPerspectiveLast);
-
- outGui["DefaultExclusionFilter"](splitFilterByLines(config.gui.defaultExclusionFilter));
-
- //load config file history
- outGui["LastUsedConfig"](config.gui.lastUsedConfigFiles);
-
- outGui["ConfigHistory" ](config.gui.cfgFileHistory);
- outGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax);
-
- outGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft);
- outGui["FolderHistoryRight"](config.gui.folderHistoryRight);
- outGui["FolderHistoryLeft" ].attribute("MaxSize", config.gui.folderHistMax);
-
- outGui["OnCompletionHistory"](config.gui.onCompletionHistory);
- outGui["OnCompletionHistory"].attribute("MaxSize", config.gui.onCompletionHistoryMax);
-
- //external applications
- outGui["ExternalApplications"](config.gui.externelApplications);
-
- //last update check
- outGui["LastVersionCheck"](config.gui.lastUpdateCheck);
-
- //batch specific global settings
- //XmlOut outBatch = out["Batch"];
-}
-
-
-template <class ConfigType>
-void writeConfig(const ConfigType& config, XmlType type, int xmlFormatVer, const Zstring& filename)
-{
- XmlDoc doc("FreeFileSync");
- setXmlType(doc, type); //throw()
-
- doc.root().setAttribute("XmlFormat", xmlFormatVer);
-
- XmlOut out(doc);
- writeConfig(config, out);
-
- saveXmlDocument(doc, filename); //throw FfsXmlError
-}
-}
-
-void xmlAccess::writeConfig(const XmlGuiConfig& cfg, const Zstring& filename)
-{
- ::writeConfig(cfg, XML_TYPE_GUI, XML_FORMAT_VER_FFS_GUI, filename); //throw FfsXmlError
-}
-
-
-void xmlAccess::writeConfig(const XmlBatchConfig& cfg, const Zstring& filename)
-{
- ::writeConfig(cfg, XML_TYPE_BATCH, XML_FORMAT_VER_FFS_BATCH, filename); //throw FfsXmlError
-}
-
-
-void xmlAccess::writeConfig(const XmlGlobalSettings& cfg)
-{
- ::writeConfig(cfg, XML_TYPE_GLOBAL, XML_FORMAT_VER_GLOBAL, getGlobalConfigFile()); //throw FfsXmlError
-}
-
-
-std::wstring xmlAccess::extractJobName(const Zstring& configFilename)
-{
- const Zstring shortName = afterLast(configFilename, FILE_NAME_SEPARATOR); //returns the whole string if separator not found
- const Zstring jobName = beforeLast(shortName, Zstr('.')); //returns empty string if seperator not found
- return utfCvrtTo<std::wstring>(jobName.empty() ? shortName : jobName);
-}
diff --git a/lib/process_xml.h b/lib/process_xml.h
deleted file mode 100644
index 4092c072..00000000
--- a/lib/process_xml.h
+++ /dev/null
@@ -1,298 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef PROCESSXML_H_INCLUDED
-#define PROCESSXML_H_INCLUDED
-
-#include <wx/gdicmn.h>
-#include "../structures.h"
-#include "xml_base.h"
-#include "localization.h"
-#include "../ui/column_attr.h"
-#include "../ui/folder_history_types.h"
-//#include "ffs_paths.h"
-
-namespace xmlAccess
-{
-enum XmlType
-{
- XML_TYPE_GUI,
- XML_TYPE_BATCH,
- XML_TYPE_GLOBAL,
- XML_TYPE_OTHER
-};
-
-XmlType getXmlType(const Zstring& filename); //throw()
-
-
-enum OnError
-{
- ON_ERROR_IGNORE,
- ON_ERROR_POPUP,
- ON_ERROR_STOP
-};
-
-enum OnGuiError
-{
- ON_GUIERROR_POPUP,
- ON_GUIERROR_IGNORE
-};
-
-typedef std::wstring Description;
-typedef std::wstring Commandline;
-typedef std::vector<std::pair<Description, Commandline>> ExternalApps;
-
-//---------------------------------------------------------------------
-struct XmlGuiConfig
-{
- XmlGuiConfig() :
- hideExcludedItems(false),
- handleError(ON_GUIERROR_POPUP),
- highlightSyncAction(true) {} //initialize values
-
- zen::MainConfiguration mainCfg;
-
- bool hideExcludedItems;
- OnGuiError handleError; //reaction on error situation during synchronization
- bool highlightSyncAction;
-};
-
-
-inline
-bool operator==(const XmlGuiConfig& lhs, const XmlGuiConfig& rhs)
-{
- return lhs.mainCfg == rhs.mainCfg &&
- lhs.hideExcludedItems == rhs.hideExcludedItems &&
- lhs.handleError == rhs.handleError &&
- lhs.highlightSyncAction == rhs.highlightSyncAction;
-}
-
-
-struct XmlBatchConfig
-{
- XmlBatchConfig() :
- showProgress(true),
- logfilesCountLimit(-1),
- handleError(ON_ERROR_POPUP) {}
-
- zen::MainConfiguration mainCfg;
-
- bool showProgress;
- Zstring logFileDirectory;
- int logfilesCountLimit; //max logfiles; 0 := don't save logfiles; < 0 := no limit
- OnError handleError; //reaction on error situation during synchronization
-};
-
-
-struct OptionalDialogs
-{
- OptionalDialogs() { resetDialogs();}
-
- void resetDialogs();
-
- bool warningDependentFolders;
- bool warningFolderPairRaceCondition;
- bool warningSignificantDifference;
- bool warningNotEnoughDiskSpace;
- bool warningUnresolvedConflicts;
- bool warningDatabaseError;
- bool warningRecyclerMissing;
- bool warningInputFieldEmpty;
- bool warningDirectoryLockFailed;
- bool popupOnConfigChange;
- bool confirmSyncStart;
- bool confirmExternalCommandMassInvoke;
-};
-
-
-enum FileIconSize
-{
- ICON_SIZE_SMALL,
- ICON_SIZE_MEDIUM,
- ICON_SIZE_LARGE
-};
-
-
-struct ViewFilterDefault
-{
- ViewFilterDefault() : equal(false)
- {
- leftOnly = rightOnly = leftNewer = rightNewer = different = conflict = true;
- createLeft = createRight = updateLeft = updateRight = deleteLeft = deleteRight = doNothing = true;
- }
- bool equal;
- bool leftOnly, rightOnly, leftNewer, rightNewer, different, conflict; //category view
- bool createLeft, createRight, updateLeft, updateRight, deleteLeft, deleteRight, doNothing; //action view
-};
-
-Zstring getGlobalConfigFile();
-
-struct XmlGlobalSettings
-{
- //---------------------------------------------------------------------
- //Shared (GUI/BATCH) settings
- XmlGlobalSettings() :
- programLanguage(zen::retrieveSystemLanguage()),
- failsafeFileCopy(true),
- copyLockedFiles(true),
- copyFilePermissions(false),
- automaticRetryCount(0),
- automaticRetryDelay(5),
- fileTimeTolerance(2), //default 2s: FAT vs NTFS
- runWithBackgroundPriority(false),
- createLockFile(true),
- verifyFileCopy(false),
- lastSyncsLogFileSizeMax(100000) //maximum size for LastSyncs.log: use a human-readable number
- {}
-
- int programLanguage;
- bool failsafeFileCopy;
- bool copyLockedFiles;
- bool copyFilePermissions;
- size_t automaticRetryCount;
- size_t automaticRetryDelay; //unit: [sec]
-
- size_t fileTimeTolerance; //max. allowed file time deviation
- bool runWithBackgroundPriority;
- bool createLockFile;
- bool verifyFileCopy; //verify copied files
- size_t lastSyncsLogFileSizeMax;
-
- OptionalDialogs optDialogs;
-
- //---------------------------------------------------------------------
- struct Gui
- {
- Gui() :
- dlgPos(wxDefaultCoord, wxDefaultCoord),
- dlgSize(wxDefaultCoord, wxDefaultCoord),
- isMaximized(false),
- sashOffset(0),
- maxFolderPairsVisible(6),
- columnAttribNavi (zen::getDefaultColumnAttributesNavi()),
- columnAttribLeft (zen::getDefaultColumnAttributesLeft()),
- columnAttribRight(zen::getDefaultColumnAttributesRight()),
- naviLastSortColumn(zen::defaultValueLastSortColumn),
- naviLastSortAscending(zen::defaultValueLastSortAscending),
- showPercentBar(zen::defaultValueShowPercentage),
- cfgFileHistMax(30),
- folderHistMax(15),
- onCompletionHistoryMax(8),
-#ifdef ZEN_WIN
- defaultExclusionFilter(Zstr("\\System Volume Information\\") Zstr("\n")
- Zstr("\\$Recycle.Bin\\") Zstr("\n")
- Zstr("\\RECYCLER\\") Zstr("\n")
- Zstr("\\RECYCLED\\") Zstr("\n")
- Zstr("*\\desktop.ini") Zstr("\n")
- Zstr("*\\thumbs.db")),
-#elif defined ZEN_LINUX
- defaultExclusionFilter(Zstr("/.Trash-*/") Zstr("\n")
- Zstr("/.recycle/")),
-#elif defined ZEN_MAC
- defaultExclusionFilter(Zstr("/.fseventsd/") Zstr("\n")
- Zstr("/.Spotlight-V100/") Zstr("\n")
- Zstr("/.Trashes/") Zstr("\n")
- Zstr("/._.Trashes") Zstr("\n")
- Zstr("*/.DS_Store")),
-#endif
- //deleteOnBothSides(false),
- useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message
-#if defined ZEN_WIN || defined ZEN_MAC
- textSearchRespectCase(false),
-#elif defined ZEN_LINUX
- textSearchRespectCase(true),
-#endif
- showIcons(true),
- iconSize(ICON_SIZE_SMALL),
- lastUpdateCheck(0)
- {
- //default external apps will be translated "on the fly"!!! First entry will be used for [Enter] or mouse double-click!
-#ifdef ZEN_WIN
- externelApplications.push_back(std::make_pair(L"Show in Explorer", L"explorer /select, \"%item_path%\""));
- externelApplications.push_back(std::make_pair(L"Open with default application", L"\"%item_path%\""));
- //mark for extraction: _("Show in Explorer")
- //mark for extraction: _("Open with default application")
-#elif defined ZEN_LINUX
- externelApplications.push_back(std::make_pair(L"Browse directory", L"xdg-open \"%item_folder%\""));
- externelApplications.push_back(std::make_pair(L"Open with default application", L"xdg-open \"%item_path%\""));
- //mark for extraction: _("Browse directory") Linux doesn't use the term "folder"
-#elif defined ZEN_MAC
- externelApplications.push_back(std::make_pair(L"Browse directory", L"open -R \"%item_path%\""));
- externelApplications.push_back(std::make_pair(L"Open with default application", L"open \"%item_path%\""));
-#endif
- }
-
- wxPoint dlgPos;
- wxSize dlgSize;
- bool isMaximized;
- int sashOffset;
-
- int maxFolderPairsVisible;
-
- std::vector<zen::ColumnAttributeNavi> columnAttribNavi; //compressed view/navigation
- std::vector<zen::ColumnAttributeRim> columnAttribLeft;
- std::vector<zen::ColumnAttributeRim> columnAttribRight;
-
- zen::ColumnTypeNavi naviLastSortColumn; //remember sort on navigation panel
- bool naviLastSortAscending; //
-
- bool showPercentBar; //in navigation panel
-
- ExternalApps externelApplications;
-
- std::vector<zen::ConfigHistoryItem> cfgFileHistory;
- size_t cfgFileHistMax;
-
- std::vector<Zstring> lastUsedConfigFiles;
-
- std::vector<Zstring> folderHistoryLeft;
- std::vector<Zstring> folderHistoryRight;
- size_t folderHistMax;
-
- std::vector<std::wstring> onCompletionHistory;
- size_t onCompletionHistoryMax;
-
- Zstring defaultExclusionFilter;
-
- //bool deleteOnBothSides;
- bool useRecyclerForManualDeletion;
- bool textSearchRespectCase;
-
- bool showIcons;
- FileIconSize iconSize;
-
- long lastUpdateCheck; //time of last update check
-
- ViewFilterDefault viewFilterDefault;
- wxString guiPerspectiveLast; //used by wxAuiManager
- } gui;
-
- //---------------------------------------------------------------------
- //struct Batch
-};
-
-//read/write specific config types
-void readConfig(const Zstring& filename, XmlGuiConfig& config); //
-void readConfig(const Zstring& filename, XmlBatchConfig& config); //throw FfsXmlError
-void readConfig( XmlGlobalSettings& config); //
-
-void writeConfig(const XmlGuiConfig& config, const Zstring& filename); //
-void writeConfig(const XmlBatchConfig& config, const Zstring& filename); //throw FfsXmlError
-void writeConfig(const XmlGlobalSettings& config); //
-
-//convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure:
-void readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config); //throw FfsXmlError
-
-//config conversion utilities
-XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); //noexcept
-XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg ); //
-XmlBatchConfig convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile); //noexcept
-
-std::wstring extractJobName(const Zstring& configFilename);
-}
-
-
-#endif // PROCESSXML_H_INCLUDED
diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp
deleted file mode 100644
index 768876c9..00000000
--- a/lib/resolve_path.cpp
+++ /dev/null
@@ -1,708 +0,0 @@
-#include "resolve_path.h"
-#include <set> //not necessarily included by <map>!
-#include <map>
-#include <zen/time.h>
-#include <zen/thread.h>
-#include <zen/utf.h>
-#include <zen/scope_guard.h>
-#include <wx/utils.h> //wxGetEnv
-
-#ifdef ZEN_WIN
-#include <zen/long_path_prefix.h>
-#include <zen/file_handling.h>
-#include <zen/win.h> //includes "windows.h"
-#include <zen/dll.h>
-#include <Shlobj.h>
-#ifdef _MSC_VER
-#pragma comment(lib, "Mpr.lib")
-#endif
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
-#include <stdlib.h> //getenv()
-#include <unistd.h> //getcwd
-#endif
-
-using namespace zen;
-
-
-namespace
-{
-#ifdef ZEN_WIN
-Zstring resolveRelativePath(const Zstring& relativeName) //note: ::GetFullPathName() is documented not threadsafe!
-{
- //don't use long path prefix! does not work with relative paths "." and ".."
- const DWORD bufferSize = ::GetFullPathName(relativeName.c_str(), 0, nullptr, nullptr);
- if (bufferSize > 0)
- {
- std::vector<wchar_t> buffer(bufferSize);
- const DWORD charsWritten = ::GetFullPathName(relativeName.c_str(), //__in LPCTSTR lpFileName,
- bufferSize, //__in DWORD nBufferLength,
- &buffer[0], //__out LPTSTR lpBuffer,
- nullptr); //__out LPTSTR *lpFilePart
- if (0 < charsWritten && charsWritten < bufferSize) //theoretically, charsWritten can never be == "bufferSize"
- return Zstring(&buffer[0], charsWritten);
- }
- return relativeName; //ERROR! Don't do anything
-}
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
-Zstring resolveRelativePath(const Zstring& relativeName)
-{
- //http://linux.die.net/man/2/path_resolution
- if (!startsWith(relativeName, FILE_NAME_SEPARATOR)) //absolute names are exactly those starting with a '/'
- {
- /*
- basic support for '~': strictly speaking this is a shell-layer feature, so "realpath()" won't handle it
- http://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html
-
- http://linux.die.net/man/3/getpwuid: An application that wants to determine its user's home directory
- should inspect the value of HOME (rather than the value getpwuid(getuid())->pw_dir) since this allows
- the user to modify their notion of "the home directory" during a login session.
- */
- if (startsWith(relativeName, "~/") || relativeName == "~")
- {
- const char* homeDir = ::getenv("HOME");
- if (!homeDir)
- return relativeName; //error! no further processing!
-
- if (startsWith(relativeName, "~/"))
- return appendSeparator(homeDir) + afterFirst(relativeName, '/');
- else if (relativeName == "~")
- return homeDir;
- }
-
- //we cannot use ::realpath() since it resolves *existing* relative paths only!
- if (char* dirpath = ::getcwd(nullptr, 0))
- {
- ZEN_ON_SCOPE_EXIT(::free(dirpath));
- return appendSeparator(dirpath) + relativeName;
- }
- }
- return relativeName;
-}
-#endif
-
-
-#ifdef ZEN_WIN
-class CsidlConstants
-{
-public:
- typedef std::map<Zstring, Zstring, LessFilename> CsidlToDirMap; //case-insensitive comparison
-
- static const CsidlToDirMap& get()
- {
- static CsidlConstants inst; //potential MT solved by intializing at startup, see below
- return inst.csidlToDir;
- }
-
-private:
- CsidlConstants()
- {
- auto addCsidl = [&](int csidl, const Zstring& paramName)
- {
- wchar_t buffer[MAX_PATH] = {};
- if (SUCCEEDED(::SHGetFolderPath(nullptr, //__in HWND hwndOwner,
- csidl | CSIDL_FLAG_DONT_VERIFY, //__in int nFolder,
- nullptr, //__in HANDLE hToken,
- 0 /* == SHGFP_TYPE_CURRENT*/, //__in DWORD dwFlags,
- buffer))) //__out LPTSTR pszPath
- {
- Zstring dirname = buffer;
- if (!dirname.empty())
- csidlToDir.insert(std::make_pair(paramName, dirname));
- }
- };
-
- //================================================================================================
- //SHGetKnownFolderPath: API available only with Windows Vista and later:
-#ifdef __MINGW32__
-#define KF_FLAG_DONT_VERIFY 0x00004000
-#endif
- typedef HRESULT (STDAPICALLTYPE* SHGetKnownFolderPathFunc)(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR* ppszPath);
- const SysDllFun<SHGetKnownFolderPathFunc> shGetKnownFolderPath(L"Shell32.dll", "SHGetKnownFolderPath");
-
- auto addFolderId = [&](REFKNOWNFOLDERID rfid, const Zstring& paramName)
- {
- if (shGetKnownFolderPath != nullptr) //[!] avoid bogus MSVC "performance warning"
- {
- PWSTR path = nullptr;
- if (SUCCEEDED(shGetKnownFolderPath(rfid, //_In_ REFKNOWNFOLDERID rfid,
- KF_FLAG_DONT_VERIFY, //_In_ DWORD dwFlags,
- nullptr, //_In_opt_ HANDLE hToken,
- &path))) //_Out_ PWSTR *ppszPath
- {
- ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(path));
-
- Zstring dirname = path;
- if (!dirname.empty())
- csidlToDir.insert(std::make_pair(paramName, dirname));
- }
- }
- };
-
- addCsidl(CSIDL_DESKTOPDIRECTORY, L"csidl_Desktop"); // C:\Users\<user>\Desktop
- addCsidl(CSIDL_COMMON_DESKTOPDIRECTORY, L"csidl_PublicDesktop"); // C:\Users\All Users\Desktop
-
- addCsidl(CSIDL_FAVORITES, L"csidl_Favorites"); // C:\Users\<user>\Favorites
- addCsidl(CSIDL_COMMON_FAVORITES, L"csidl_PublicFavorites"); // C:\Users\<user>\Favorites; unused? -> http://blogs.msdn.com/b/oldnewthing/archive/2012/09/04/10346022.aspx
-
- addCsidl(CSIDL_PERSONAL, L"csidl_MyDocuments"); // C:\Users\<user>\Documents
- addCsidl(CSIDL_COMMON_DOCUMENTS, L"csidl_PublicDocuments"); // C:\Users\Public\Documents
-
- addCsidl(CSIDL_MYMUSIC, L"csidl_MyMusic"); // C:\Users\<user>\Music
- addCsidl(CSIDL_COMMON_MUSIC, L"csidl_PublicMusic"); // C:\Users\Public\Music
-
- addCsidl(CSIDL_MYPICTURES, L"csidl_MyPictures"); // C:\Users\<user>\Pictures
- addCsidl(CSIDL_COMMON_PICTURES, L"csidl_PublicPictures"); // C:\Users\Public\Pictures
-
- addCsidl(CSIDL_MYVIDEO, L"csidl_MyVideos"); // C:\Users\<user>\Videos
- addCsidl(CSIDL_COMMON_VIDEO, L"csidl_PublicVideos"); // C:\Users\Public\Videos
-
- addCsidl(CSIDL_NETHOOD, L"csidl_Nethood"); // C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Network Shortcuts
-
- addCsidl(CSIDL_PROGRAMS, L"csidl_Programs"); // C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
- addCsidl(CSIDL_COMMON_PROGRAMS, L"csidl_PublicPrograms"); // C:\ProgramData\Microsoft\Windows\Start Menu\Programs
-
- addCsidl(CSIDL_RESOURCES, L"csidl_Resources"); // C:\Windows\Resources
-
- addCsidl(CSIDL_STARTMENU, L"csidl_StartMenu"); // C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Start Menu
- addCsidl(CSIDL_COMMON_STARTMENU, L"csidl_PublicStartMenu"); // C:\ProgramData\Microsoft\Windows\Start Menu
-
- addCsidl(CSIDL_STARTUP, L"csidl_Startup"); // C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\StartUp
- addCsidl(CSIDL_COMMON_STARTUP, L"csidl_PublicStartup"); // C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
-
- addCsidl(CSIDL_TEMPLATES, L"csidl_Templates"); // C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Templates
- addCsidl(CSIDL_COMMON_TEMPLATES, L"csidl_PublicTemplates"); // C:\ProgramData\Microsoft\Windows\Templates
-
- //Vista and later:
- addFolderId(FOLDERID_Downloads, L"csidl_Downloads"); // C:\Users\<user>\Downloads
- addFolderId(FOLDERID_PublicDownloads, L"csidl_PublicDownloads"); // C:\Users\Public\Downloads
-
- addFolderId(FOLDERID_QuickLaunch, L"csidl_QuickLaunch"); // C:\Users\<user>\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch
-
- /*
- CSIDL_APPDATA covered by %AppData%
- CSIDL_LOCAL_APPDATA covered by %LocalAppData% -> not on XP!
- CSIDL_COMMON_APPDATA covered by %ProgramData% -> not on XP!
- CSIDL_PROFILE covered by %UserProfile%
- CSIDL_WINDOWS covered by %WinDir%
- CSIDL_SYSTEM covered by %WinDir%
- CSIDL_SYSTEMX86 covered by %WinDir%
- CSIDL_PROGRAM_FILES covered by %ProgramFiles%
- CSIDL_PROGRAM_FILES_COMMON covered by %CommonProgramFiles%
- CSIDL_PROGRAM_FILESX86 covered by %ProgramFiles(x86)% -> not on XP!
- CSIDL_PROGRAM_FILES_COMMONX86 covered by %CommonProgramFiles(x86)% -> not on XP!
- CSIDL_ADMINTOOLS not relevant?
- CSIDL_COMMON_ADMINTOOLS not relevant?
-
- FOLDERID_Public covered by %Public%
- */
- }
-
- CsidlConstants(const CsidlConstants&);
- CsidlConstants& operator=(const CsidlConstants&);
-
- CsidlToDirMap csidlToDir;
-};
-
-//caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start!
-struct Dummy { Dummy() { CsidlConstants::get(); }} blah;
-#endif
-
-
-std::unique_ptr<Zstring> getEnvironmentVar(const Zstring& envName) //return nullptr if not found
-{
- wxString value;
- if (!wxGetEnv(utfCvrtTo<wxString>(envName), &value))
- return nullptr;
-
- //some postprocessing:
- trim(value); //remove leading, trailing blanks
-
- //remove leading, trailing double-quotes
- if (startsWith(value, L"\"") &&
- endsWith (value, L"\"") &&
- value.length() >= 2)
- value = wxString(value.c_str() + 1, value.length() - 2);
-
- return make_unique<Zstring>(utfCvrtTo<Zstring>(value));
-}
-
-
-std::unique_ptr<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters
- const std::vector<std::pair<Zstring, Zstring>>& ext) //return nullptr if not resolved
-{
- auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo<wxString>(lhs).CmpNoCase(utfCvrtTo<wxString>(rhs)) == 0; };
-
- //there exist environment variables named %TIME%, %DATE% so check for our internal macros first!
- if (equalNoCase(macro, Zstr("time")))
- return make_unique<Zstring>(formatTime<Zstring>(Zstr("%H%M%S")));
-
- if (equalNoCase(macro, Zstr("date")))
- return make_unique<Zstring>(formatTime<Zstring>(FORMAT_ISO_DATE));
-
- if (equalNoCase(macro, Zstr("timestamp")))
- return make_unique<Zstring>(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"))); //e.g. "2012-05-15 131513"
-
- std::unique_ptr<Zstring> cand;
- auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool
- {
- if (!equalNoCase(macro, phrase))
- return false;
-
- cand = make_unique<Zstring>(formatTime<Zstring>(format));
- return true;
- };
-
- if (processPhrase(Zstr("weekday"), Zstr("%A"))) return cand;
- if (processPhrase(Zstr("day" ), Zstr("%d"))) return cand;
- if (processPhrase(Zstr("month" ), Zstr("%m"))) return cand;
- if (processPhrase(Zstr("week" ), Zstr("%U"))) return cand;
- if (processPhrase(Zstr("year" ), Zstr("%Y"))) return cand;
- if (processPhrase(Zstr("hour" ), Zstr("%H"))) return cand;
- if (processPhrase(Zstr("min" ), Zstr("%M"))) return cand;
- if (processPhrase(Zstr("sec" ), Zstr("%S"))) return cand;
-
- //check domain-specific extensions
- {
- auto it = std::find_if(ext.begin(), ext.end(), [&](const std::pair<Zstring, Zstring>& p) { return equalNoCase(macro, p.first); });
- if (it != ext.end())
- return make_unique<Zstring>(it->second);
- }
-
- //try to resolve as environment variable
- if (std::unique_ptr<Zstring> value = getEnvironmentVar(macro))
- return value;
-
-#ifdef ZEN_WIN
- //try to resolve as CSIDL value
- {
- const auto& csidlMap = CsidlConstants::get();
- auto it = csidlMap.find(macro);
- if (it != csidlMap.end())
- return make_unique<Zstring>(it->second);
- }
-#endif
-
- return nullptr;
-}
-
-const Zchar MACRO_SEP = Zstr('%');
-
-//returns expanded or original string
-Zstring expandMacros(const Zstring& text, const std::vector<std::pair<Zstring, Zstring>>& ext)
-{
- if (contains(text, MACRO_SEP))
- {
- Zstring prefix = beforeFirst(text, MACRO_SEP);
- Zstring rest = afterFirst (text, MACRO_SEP);
- if (contains(rest, MACRO_SEP))
- {
- Zstring potentialMacro = beforeFirst(rest, MACRO_SEP);
- Zstring postfix = afterFirst (rest, MACRO_SEP); //text == prefix + MACRO_SEP + potentialMacro + MACRO_SEP + postfix
-
- if (std::unique_ptr<Zstring> value = resolveMacro(potentialMacro, ext))
- return prefix + *value + expandMacros(postfix, ext);
- else
- return prefix + MACRO_SEP + potentialMacro + expandMacros(MACRO_SEP + postfix, ext);
- }
- }
- return text;
-}
-}
-
-
-Zstring zen::expandMacros(const Zstring& text) { return ::expandMacros(text, std::vector<std::pair<Zstring, Zstring>>()); }
-
-
-namespace
-{
-#ifdef ZEN_WIN
-//networks and cdrom excluded - may still block for slow USB sticks!
-Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on error
-{
- //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);
-
- const DWORD rv = ::GetLogicalDriveStrings(bufferSize, //__in DWORD nBufferLength,
- &buffer[0]); //__out LPTSTR lpBuffer
- if (0 < rv && rv < bufferSize)
- {
- //search for matching path in parallel until first hit
- RunUntilFirstHit<Zstring> findFirstMatch;
-
- for (const wchar_t* it = &buffer[0]; *it != 0; it += strLength(it) + 1) //list terminated by empty c-string
- {
- const Zstring path = it;
-
- findFirstMatch.addJob([path, volumeName]() -> std::unique_ptr<Zstring>
- {
- UINT type = ::GetDriveType(appendSeparator(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(appendSeparator(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;
- });
- }
- if (auto result = findFirstMatch.get()) //blocks until ready
- return *result;
- }
-
- return Zstring();
-}
-
-
-//networks and cdrom excluded - this should not block
-Zstring getVolumeName(const Zstring& volumePath) //return empty string on error
-{
- UINT rv = ::GetDriveType(appendSeparator(volumePath).c_str()); //non-blocking call!
- if (rv != DRIVE_REMOTE &&
- rv != DRIVE_CDROM)
- {
- const DWORD bufferSize = MAX_PATH + 1;
- std::vector<wchar_t> buffer(bufferSize);
- if (::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName,
- &buffer[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
- return &buffer[0];
- }
- return Zstring();
-}
-#endif
-
-
-//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]"
- Zstring textTmp = text;
- trim(textTmp, true, false);
-
- if (startsWith(textTmp, Zstr("[")))
- {
- size_t posEnd = textTmp.find(Zstr("]"));
- if (posEnd != Zstring::npos)
- {
- Zstring volname = Zstring(textTmp.c_str() + 1, posEnd - 1);
- Zstring rest = Zstring(textTmp.c_str() + posEnd + 1);
-
- if (startsWith(rest, Zstr(':')))
- rest = afterFirst(rest, Zstr(':'));
- if (startsWith(rest, FILE_NAME_SEPARATOR))
- rest = afterFirst(rest, FILE_NAME_SEPARATOR);
-#ifdef ZEN_WIN
- //[.*] pattern was found...
- if (!volname.empty())
- {
- Zstring volPath = getPathByVolumenName(volname); //may block for slow USB sticks!
- if (!volPath.empty())
- return appendSeparator(volPath) + rest; //successfully replaced pattern
- }
- //error: did not find corresponding volume name:
-
- /*make sure directory creation will fail later if attempted, instead of inconveniently interpreting this string as a relative name!
- [FFS USB]\FreeFileSync will be resolved as
- ?:\[FFS USB]\FreeFileSync\ - Windows
- /.../[FFS USB]/FreeFileSync/ - Linux
- instead of:
- C:\Program Files\FreeFileSync\[FFS USB]\FreeFileSync\ */
- return L"?:\\[" + volname + L"]\\" + rest;
-
-#elif defined ZEN_LINUX || defined ZEN_MAC //neither supported nor needed
- return "/.../[" + volname + "]/" + rest;
-#endif
- }
- }
- return text;
-}
-}
-
-
-void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, LessFilename>& output)
-{
-#ifdef ZEN_WIN
- //1. replace volume path by volume name: c:\dirname -> [SYSTEM]\dirname
- if (dirname.size() >= 3 &&
- std::iswalpha(dirname[0]) &&
- dirname[1] == L':' &&
- dirname[2] == L'\\')
- {
- Zstring volname = getVolumeName(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 = expandVolumeName(dirname); //should not block
- if (testVolname != dirname)
- if (output.insert(testVolname).second)
- getDirectoryAliasesRecursive(testVolname, output); //recurse!
- }
-#endif
-
- //3. environment variables: C:\Users\<user> -> %USERPROFILE%
- {
- std::map<Zstring, Zstring> envToDir;
-
- //get list of useful variables
- auto addEnvVar = [&](const Zstring& envName)
- {
- if (std::unique_ptr<Zstring> value = getEnvironmentVar(envName))
- envToDir.insert(std::make_pair(envName, *value));
- };
-#ifdef ZEN_WIN
- addEnvVar(L"AllUsersProfile"); // C:\ProgramData
- addEnvVar(L"AppData"); // C:\Users\<user>\AppData\Roaming
- addEnvVar(L"LocalAppData"); // C:\Users\<user>\AppData\Local
- addEnvVar(L"ProgramData"); // C:\ProgramData
- addEnvVar(L"ProgramFiles"); // C:\Program Files
- addEnvVar(L"ProgramFiles(x86)");// C:\Program Files (x86)
- addEnvVar(L"CommonProgramFiles"); // C:\Program Files\Common Files
- addEnvVar(L"CommonProgramFiles(x86)"); // C:\Program Files (x86)\Common Files
- addEnvVar(L"Public"); // C:\Users\Public
- addEnvVar(L"UserProfile"); // C:\Users\<user>
- addEnvVar(L"WinDir"); // C:\Windows
- addEnvVar(L"Temp"); // C:\Windows\Temp
-
- //add CSIDL values: http://msdn.microsoft.com/en-us/library/bb762494(v=vs.85).aspx
- const auto& csidlMap = CsidlConstants::get();
- envToDir.insert(csidlMap.begin(), csidlMap.end());
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
- addEnvVar("HOME"); //Linux: /home/<user> Mac: /Users/<user>
-#endif
- //substitute paths by symbolic names
- auto pathStartsWith = [](const Zstring& path, const Zstring& prefix) -> bool
- {
-#if defined ZEN_WIN || defined ZEN_MAC
- Zstring tmp = path;
- Zstring tmp2 = prefix;
- ::makeUpper(tmp);
- ::makeUpper(tmp2);
- return startsWith(tmp, tmp2);
-#elif defined ZEN_LINUX
- return startsWith(path, prefix);
-#endif
- };
- for (const auto& entry : envToDir)
- if (pathStartsWith(dirname, entry.second))
- output.insert(MACRO_SEP + entry.first + MACRO_SEP + (dirname.c_str() + entry.second.size()));
- }
-
- //4. replace (all) macros: %USERPROFILE% -> C:\Users\<user>
- {
- Zstring testMacros = expandMacros(dirname);
- if (testMacros != dirname)
- if (output.insert(testMacros).second)
- getDirectoryAliasesRecursive(testMacros, output); //recurse!
- }
-}
-
-
-std::vector<Zstring> zen::getDirectoryAliases(const Zstring& dirString)
-{
- Zstring dirname = dirString;
- trim(dirname, true, false);
- if (dirname.empty())
- return std::vector<Zstring>();
-
- std::set<Zstring, LessFilename> tmp;
- getDirectoryAliasesRecursive(dirname, tmp);
-
- tmp.erase(dirname);
- tmp.erase(Zstring());
-
- return std::vector<Zstring>(tmp.begin(), tmp.end());
-}
-
-
-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.
-
- Zstring dirname = dirString;
-
- //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix()
- trim(dirname, true, false);
- while (endsWith(dirname, Zstr(' '))) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name
- dirname.resize(dirname.size() - 1);
-
- if (dirname.empty()) //an empty string would later be resolved as "\"; this is not desired
- return Zstring();
-
- dirname = expandMacros(dirname);
- dirname = expandVolumeName(dirname); //may block for slow USB sticks!
-
- /*
- need to resolve relative paths:
- WINDOWS:
- - \\?\-prefix which needs absolute names
- - Volume Shadow Copy: volume name needs to be part of each filename
- - file icon buffer (at least for extensions that are actually read from disk, like "exe")
- - ::SHFileOperation(): Using relative path names is not thread safe
- WINDOWS/LINUX:
- - detection of dependent directories, e.g. "\" and "C:\test"
- */
- dirname = resolveRelativePath(dirname);
-
- return appendSeparator(dirname);
-}
-
-
-#ifdef ZEN_WIN
-void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteraction) //throw() - user interaction: show OS password prompt
-{
- /*
- ATTENTION: it is not safe to retrieve UNC path via ::WNetGetConnection() for every type of network share:
-
- network type |::WNetGetConnection rv | lpRemoteName | existing UNC path
- -----------------------------|-------------------------|---------------------------------|----------------
- inactive local network share | ERROR_CONNECTION_UNAVAIL| \\192.168.1.27\new2 | YES
- WebDrive | NO_ERROR | \\Webdrive-ZenJu\GNU | NO
- Box.net (WebDav) | NO_ERROR | \\www.box.net\DavWWWRoot\dav | YES
- NetDrive | ERROR_NOT_CONNECTED | <empty> | NO
- ____________________________________________________________________________________________________________
-
- Windows Login Prompt Naming Conventions:
- user account: <Domain>\<user> e.g. WIN-XP\ZenJu
- network share: \\<server>\<share> e.g. \\WIN-XP\test
-
- Windows Command Line:
- - list *all* active network connections, including deviceless ones which are hidden in Explorer:
- net use
- - delete active connection:
- net use /delete \\server\share
- ____________________________________________________________________________________________________________
-
- Scenario: XP-shared folder is accessed by Win 7 over LAN with access limited to a certain user
-
- Problems:
- I. WNetAddConnection2() allows (at least certain) invalid credentials (e.g. username: a/password: a) and establishes an *unusable* connection
- II. WNetAddConnection2() refuses to overwrite an existing (unusable) connection created in I), but shows prompt repeatedly
- III. WNetAddConnection2() won't bring up the prompt if *wrong* credentials had been entered just recently, even with CONNECT_INTERACTIVE specified! => 2-step proccess
- */
-
- auto connect = [&](NETRESOURCE& trgRes) //blocks heavily if network is not reachable!!!
- {
- //1. first try to connect without user interaction - blocks!
- DWORD rv = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
- nullptr, // __in LPCTSTR lpPassword,
- nullptr, // __in LPCTSTR lpUsername,
- 0); //__in DWORD dwFlags
- //53L ERROR_BAD_NETPATH The network path was not found.
- //86L ERROR_INVALID_PASSWORD
- //1219L ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
- //1326L ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password.
- //1236L ERROR_CONNECTION_ABORTED
- if (somethingExists(trgRes.lpRemoteName)) //blocks!
- return; //success: connection usable! -> don't care about "rv"
-
- if (rv == ERROR_BAD_NETPATH || //Windows 7
- rv == ERROR_BAD_NET_NAME|| //XP
- rv == ERROR_CONNECTION_ABORTED) //failed to connect to a network that existed not too long ago; will later return ERROR_BAD_NETPATH
- return; //no need to show a prompt for an unreachable network device
-
- //2. if first attempt failed, we need to *force* prompt by using CONNECT_PROMPT
- if (allowUserInteraction)
- {
- //avoid problem II.)
- DWORD rv2= ::WNetCancelConnection2(trgRes.lpRemoteName, //_In_ LPCTSTR lpName,
- 0, //_In_ DWORD dwFlags,
- true); //_In_ BOOL fForce
- //2250L ERROR_NOT_CONNECTED
-
- //enforce login prompt
- DWORD rv3 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
- nullptr, // __in LPCTSTR lpPassword,
- nullptr, // __in LPCTSTR lpUsername,
- CONNECT_INTERACTIVE | CONNECT_PROMPT); //__in DWORD dwFlags
- (void)rv2;
- (void)rv3;
- }
- };
-
-
- Zstring dirname = removeLongPathPrefix(dirnameOrig);
- trim(dirname, true, false);
-
- //1. locally mapped network share
- if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':')
- {
- Zstring driveLetter(dirname.c_str(), 2); //e.g.: "Q:"
- {
- DWORD bufferSize = 10000;
- std::vector<wchar_t> remoteNameBuffer(bufferSize);
-
- //map local -> remote
-
- //note: following function call does NOT block!
- DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form "<driveletter>:"
- &remoteNameBuffer[0], //__out LPTSTR lpRemoteName,
- &bufferSize); //__inout LPDWORD lpnLength
- if (rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless!
- {
- //ERROR_CONNECTION_UNAVAIL: network mapping is existing, but not connected
-
- Zstring networkShare = &remoteNameBuffer[0];
- if (!networkShare.empty())
- {
- NETRESOURCE trgRes = {};
- trgRes.dwType = RESOURCETYPE_DISK;
- trgRes.lpLocalName = const_cast<LPWSTR>(driveLetter.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct!
- trgRes.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //
-
- connect(trgRes); //blocks!
- }
- }
- }
- }
- //2. deviceless network connection
- else if (startsWith(dirname, L"\\\\")) //UNC path
- {
- const Zstring networkShare = [&]() -> Zstring //extract prefix "\\server\share"
- {
- size_t pos = dirname.find('\\', 2);
- if (pos == Zstring::npos)
- return Zstring();
- pos = dirname.find('\\', pos + 1);
- return pos == Zstring::npos ? dirname : Zstring(dirname.c_str(), pos);
- }();
-
- if (!networkShare.empty())
- {
- //::WNetGetResourceInformation seems to fail with ERROR_BAD_NET_NAME even for existing unconnected network shares!
- // => unconditionally try to connect to network share, seems we cannot reliably detect connection status otherwise
-
- NETRESOURCE trgRes = {};
- trgRes.dwType = RESOURCETYPE_DISK;
- trgRes.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //trgRes is "__in"
-
- connect(trgRes); //blocks!
- }
- }
-}
-#endif
diff --git a/lib/resolve_path.h b/lib/resolve_path.h
deleted file mode 100644
index b9c7196f..00000000
--- a/lib/resolve_path.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef RESOLVE_PATH_H_INCLUDED
-#define RESOLVE_PATH_H_INCLUDED
-
-#include <vector>
-#include <zen/zstring.h>
-
-namespace zen
-{
-/*
-FULL directory format:
- - expand macros
- - expand volume path by name
- - convert relative paths into absolute
- - trim whitespace and append file name separator
-*/
-Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - may still block for slow USB sticks! not thread-safe!!!(see ::GetFullPathName())
-
-//macro substitution only
-Zstring expandMacros(const Zstring& text);
-
-std::vector<Zstring> getDirectoryAliases(const Zstring& dirString); //may block for slow USB sticks when resolving [<volume name>]
-
-#ifdef ZEN_WIN
-//*blocks* if network is not reachable or when showing login prompt dialog!
-void loginNetworkShare(const Zstring& dirname, bool allowUserInteraction); //throw() - user interaction: show OS password prompt
-#endif
-}
-
-
-#endif // RESOLVE_PATH_H_INCLUDED
diff --git a/lib/return_codes.h b/lib/return_codes.h
deleted file mode 100644
index a37e11f2..00000000
--- a/lib/return_codes.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef RETURN_CODES_H_INCLUDED
-#define RETURN_CODES_H_INCLUDED
-
-namespace zen
-{
-enum FfsReturnCode
-{
- FFS_RC_SUCCESS = 0,
- FFS_RC_FINISHED_WITH_WARNINGS,
- FFS_RC_FINISHED_WITH_ERRORS,
- FFS_RC_ABORTED,
- FFS_RC_EXCEPTION,
-};
-
-
-inline
-void raiseReturnCode(FfsReturnCode& rc, FfsReturnCode rcProposed)
-{
- if (rc < rcProposed)
- rc = rcProposed;
-}
-}
-
-#endif // RETURN_CODES_H_INCLUDED
diff --git a/lib/shadow.cpp b/lib/shadow.cpp
deleted file mode 100644
index d1027f69..00000000
--- a/lib/shadow.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "shadow.h"
-#include <stdexcept>
-#include <zen/win.h> //includes "windows.h"
-#include <zen/dll.h>
-#include <zen/win_ver.h>
-#include <zen/assert_static.h>
-#include <zen/long_path_prefix.h>
-#include <zen/symlink_target.h>
-#include "ShadowCopy/shadow.h"
-#include <zen/scope_guard.h>
-
-using namespace zen;
-using namespace shadow;
-
-
-namespace
-{
-bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx)
-{
- typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process);
-
- const SysDllFun<IsWow64ProcessFun> isWow64Process(L"kernel32.dll", "IsWow64Process");
- if (isWow64Process)
- {
- BOOL isWow64 = FALSE;
- if (isWow64Process(::GetCurrentProcess(), &isWow64))
- return isWow64 != FALSE;
- }
- return false;
-}
-
-const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup
-}
-
-//#############################################################################################################
-
-class ShadowCopy::ShadowVolume
-{
-public:
- 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(_("Cannot access the Volume Shadow Copy Service."),
- _("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 || !getLastError)
- throw FileError(_("Cannot access the Volume Shadow Copy Service."),
- replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName())));
-
- //---------------------------------------------------------------------------------------------------------
- //start volume shadow copy service:
- backupHandle = createShadowCopy(volumeNamePf.c_str());
- if (!backupHandle)
- throw FileError(_("Cannot access the Volume Shadow Copy Service."),
- getLastError() + std::wstring(L" Volume: ") + fmtFileName(volumeNamePf));
-
- shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash
- }
-
- ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary
-
- Zstring geNamePf() const { return shadowVolPf; } //with trailing path separator
-
-private:
- ShadowVolume(const ShadowVolume&);
- ShadowVolume& operator=(const ShadowVolume&);
-
- const DllFun<FunType_createShadowCopy> createShadowCopy;
- const DllFun<FunType_releaseShadowCopy> releaseShadowCopy;
- const DllFun<FunType_getShadowVolume> getShadowVolume;
- const DllFun<FunType_getLastError> getLastError;
-
- Zstring shadowVolPf;
- ShadowHandle backupHandle;
-};
-
-//#############################################################################################################
-
-Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy)
-{
- Zstring filenameFinal = inputFile;
-
- //try to resolve symlinks and junctions:
- //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked!
- //2. junctions: C:\Users\<username> is a junction that may link to e.g. D:\Users\<username>, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y."
- if (wereVistaOrLater)
- filenameFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later!
- //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix
-
- DWORD bufferSize = 10000;
- std::vector<wchar_t> volBuffer(bufferSize);
- if (!::GetVolumePathName(filenameFinal.c_str(), //__in LPCTSTR lpszFileName,
- &volBuffer[0], //__out LPTSTR lpszVolumePathName,
- bufferSize)) //__in DWORD cchBufferLength
- throw FileError(replaceCpy(_("Cannot determine volume name for %x."), L"%x", fmtFileName(filenameFinal)), formatSystemError(L"GetVolumePathName", ::GetLastError()));
-
- const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error!
-
- //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found.
- const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider \\?\ prefix!
- if (pos == Zstring::npos)
- throw FileError(replaceCpy(replaceCpy(_("Volume name %x is not part of file path %y."),
- L"%x", fmtFileName(volumeNamePf)),
- L"%y", fmtFileName(filenameFinal)));
-
- //get or create instance of shadow volume
- auto it = shadowVol.find(volumeNamePf);
- if (it == shadowVol.end())
- {
- onBeforeMakeVolumeCopy(volumeNamePf); //notify client before (potentially) blocking some time
- auto newEntry = std::make_shared<ShadowVolume>(volumeNamePf); //throw FileError
- it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first;
- }
-
- //return filename alias on shadow copy volume
- return it->second->geNamePf() + Zstring(filenameFinal.c_str() + pos + volumeNamePf.length());
-}
diff --git a/lib/shadow.h b/lib/shadow.h
deleted file mode 100644
index 36c72c25..00000000
--- a/lib/shadow.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef SHADOW_H_INCLUDED
-#define SHADOW_H_INCLUDED
-
-#include <map>
-#include <memory>
-#include <functional>
-#include <zen/zstring.h>
-#include <zen/file_error.h>
-
-namespace shadow
-{
-class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as needed
-{
-public:
- ShadowCopy() {}
-
- //return filename on shadow copy volume - follows symlinks!
- Zstring makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy); //throw FileError
-
-private:
- ShadowCopy(const ShadowCopy&);
- ShadowCopy& operator=(const ShadowCopy&);
-
- class ShadowVolume;
- typedef std::map<Zstring, std::shared_ptr<ShadowVolume>, LessFilename> VolNameShadowMap;
- VolNameShadowMap shadowVol;
-};
-}
-
-#endif //SHADOW_H_INCLUDED
diff --git a/lib/soft_filter.h b/lib/soft_filter.h
deleted file mode 100644
index 010a8913..00000000
--- a/lib/soft_filter.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef SOFT_FILTER_H_INCLUDED
-#define SOFT_FILTER_H_INCLUDED
-
-#include <algorithm>
-#include <limits>
-#include "../structures.h"
-//#include <wx/stopwatch.h>
-
-namespace zen
-{
-/*
-Semantics of SoftFilter:
-1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches
-2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed)
-3. => equivalent to a user temporarily (de-)selecting rows => not relevant for <two way>-mode!
-*/
-
-class SoftFilter
-{
-public:
- SoftFilter(size_t timeSpan, UnitTime unitTimeSpan,
- size_t sizeMin, UnitSize unitSizeMin,
- size_t sizeMax, UnitSize unitSizeMax);
-
- bool matchTime(Int64 writeTime) const { return timeFrom_ <= writeTime; }
- bool matchSize(UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; }
- bool matchFolder() const { return matchesFolder_; }
- bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower
-
- //small helper method: merge two soft filters
- friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second);
-
-private:
- SoftFilter(const Int64& timeFrom,
- const UInt64& sizeMin,
- const UInt64& sizeMax,
- bool matchesFolder);
-
- Int64 timeFrom_; //unit: UTC, seconds
- UInt64 sizeMin_; //unit: bytes
- UInt64 sizeMax_; //unit: bytes
- bool matchesFolder_;
-};
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-// ----------------------- implementation -----------------------
-namespace zen
-{
-inline
-SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan,
- size_t sizeMin, UnitSize unitSizeMin,
- size_t sizeMax, UnitSize unitSizeMax) :
- matchesFolder_(unitTimeSpan == UTIME_NONE&&
- unitSizeMin == USIZE_NONE&&
- unitSizeMax == USIZE_NONE) //exclude folders if size or date filter is active: avoids creating empty folders if not needed!
-{
- resolveUnits(timeSpan, unitTimeSpan,
- sizeMin, unitSizeMin,
- sizeMax, unitSizeMax,
- timeFrom_,
- sizeMin_,
- sizeMax_);
-}
-
-inline
-SoftFilter::SoftFilter(const Int64& timeFrom,
- const UInt64& sizeMin,
- const UInt64& sizeMax,
- bool matchesFolder) :
- timeFrom_(timeFrom),
- sizeMin_ (sizeMin),
- sizeMax_ (sizeMax),
- matchesFolder_(matchesFolder) {}
-
-inline
-SoftFilter combineFilters(const SoftFilter& lhs, const SoftFilter& rhs)
-{
- return SoftFilter(std::max(lhs.timeFrom_, rhs.timeFrom_),
- std::max(lhs.sizeMin_, rhs.sizeMin_),
- std::min(lhs.sizeMax_, rhs.sizeMax_),
- lhs.matchesFolder_ && rhs.matchesFolder_);
-}
-
-inline
-bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower
-{
- return timeFrom_ == std::numeric_limits<Int64>::min() &&
- sizeMin_ == 0U &&
- sizeMax_ == std::numeric_limits<UInt64>::max() &&
- matchesFolder_ == true;;
-}
-}
-
-#endif // SOFT_FILTER_H_INCLUDED
diff --git a/lib/status_handler.cpp b/lib/status_handler.cpp
deleted file mode 100644
index 5fb80161..00000000
--- a/lib/status_handler.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "status_handler.h"
-//#include <wx/app.h>
-#include <zen/tick_count.h>
-
-using namespace zen;
-
-
-namespace
-{
-const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
-TickVal lastExec = getTicks();
-};
-
-bool zen::updateUiIsAllowed()
-{
- const TickVal now = getTicks(); //0 on error
- if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary
- {
- lastExec = now;
- return true;
- }
- return false;
-}
diff --git a/lib/status_handler.h b/lib/status_handler.h
deleted file mode 100644
index 9a288b6f..00000000
--- a/lib/status_handler.h
+++ /dev/null
@@ -1,147 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef STATUSHANDLER_H_INCLUDED
-#define STATUSHANDLER_H_INCLUDED
-
-#include "../process_callback.h"
-#include <vector>
-#include <string>
-#include <zen/int64.h>
-#include <zen/i18n.h>
-
-namespace zen
-{
-bool updateUiIsAllowed(); //test if a specific amount of time is over
-
-/*
-Updating GUI is fast!
- time per single call to ProcessCallback::forceUiRefresh()
- - Comparison 0.025 ms
- - Synchronization 0.74 ms (despite complex graph control!)
-*/
-
-//gui may want to abort process
-struct AbortCallback
-{
- virtual ~AbortCallback() {}
- virtual void requestAbortion() = 0;
-};
-
-
-//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() : 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 MUST 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()
- {
- if (abortRequested) //triggered by requestAbortion()
- {
- forceUiRefresh();
- abortThisProcess();
- }
- else if (updateUiIsAllowed()) //test if specific time span between ui updates is over
- forceUiRefresh();
- }
-
- 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_ =_("Stop 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;
-
- 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
deleted file mode 100644
index 210e6ecd..00000000
--- a/lib/status_handler_impl.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef STATUSHANDLER_IMPL_H_INCLUDED
-#define STATUSHANDLER_IMPL_H_INCLUDED
-
-#include <zen/optional.h>
-#include <zen/file_error.h>
-#include "process_callback.h"
-
-
-template <typename Function> inline
-zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& handler) //return ignored error message if available
-{
- for (size_t retryNumber = 0;; ++retryNumber)
- try
- {
- cmd(); //throw FileError
- return zen::NoValue();
- }
- catch (zen::FileError& error)
- {
- switch (handler.reportError(error.toString(), retryNumber)) //may throw!
- {
- case ProcessCallback::IGNORE_ERROR:
- return error.toString();
- case ProcessCallback::RETRY:
- break; //continue with loop
- }
- }
-}
-
-
-#endif //STATUSHANDLER_IMPL_H_INCLUDED
diff --git a/lib/versioning.cpp b/lib/versioning.cpp
deleted file mode 100644
index 82696e3a..00000000
--- a/lib/versioning.cpp
+++ /dev/null
@@ -1,439 +0,0 @@
-#include "versioning.h"
-#include <map>
-#include <zen/file_handling.h>
-#include <zen/file_traverser.h>
-#include <zen/string_tools.h>
-
-using namespace zen;
-
-
-namespace
-{
-Zstring getExtension(const Zstring& relativeName) //including "." if extension is existing, returns empty string otherwise
-{
- auto iterSep = find_last(relativeName.begin(), relativeName.end(), FILE_NAME_SEPARATOR);
- auto iterName = iterSep != relativeName.end() ? iterSep + 1 : relativeName.begin(); //find beginning of short name
- auto iterDot = find_last(iterName, relativeName.end(), Zstr('.')); //equal to relativeName.end() if file has no extension!!
- return Zstring(&*iterDot, relativeName.end() - iterDot);
-};
-}
-
-bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt")
-{
- auto it = shortnameVersion.begin();
- auto last = shortnameVersion.end();
-
- auto nextDigit = [&]() -> bool
- {
- if (it == last || !isDigit(*it))
- return false;
- ++it;
- return true;
- };
- auto nextDigits = [&](size_t count) -> bool
- {
- while (count-- > 0)
- if (!nextDigit())
- return false;
- return true;
- };
- auto nextChar = [&](Zchar c) -> bool
- {
- if (it == last || *it != c)
- return false;
- ++it;
- return true;
- };
- auto nextStringI = [&](const Zstring& str) -> bool //windows: ignore case!
- {
- if (last - it < static_cast<ptrdiff_t>(str.size()) || !EqualFilename()(str, Zstring(&*it, str.size())))
- return false;
- it += str.size();
- return true;
- };
-
- return nextStringI(shortname) && //versioned file starts with original name
- nextChar(Zstr(' ')) && //validate timestamp: e.g. "2012-05-15 131513"; Regex: \d{4}-\d{2}-\d{2} \d{6}
- nextDigits(4) && //YYYY
- nextChar(Zstr('-')) && //
- nextDigits(2) && //MM
- nextChar(Zstr('-')) && //
- nextDigits(2) && //DD
- nextChar(Zstr(' ')) && //
- nextDigits(6) && //HHMMSS
- nextStringI(getExtension(shortname)) &&
- it == last;
-}
-
-
-namespace
-{
-/*
-- handle not existing source
-- create target super directories if missing
-*/
-template <class Function>
-void moveItemToVersioning(const Zstring& fullName, //throw FileError
- const Zstring& relativeName,
- const Zstring& versioningDirectory,
- const Zstring& timestamp,
- VersioningStyle versioningStyle,
- Function moveObj) //move source -> target; may throw FileError
-{
- assert(!startsWith(relativeName, FILE_NAME_SEPARATOR));
- assert(!endsWith (relativeName, FILE_NAME_SEPARATOR));
-
- Zstring targetName;
- switch (versioningStyle)
- {
- case VER_STYLE_REPLACE:
- targetName = appendSeparator(versioningDirectory) + relativeName;
- break;
-
- case VER_STYLE_ADD_TIMESTAMP:
- //assemble time-stamped version name
- targetName = appendSeparator(versioningDirectory) + relativeName + Zstr(' ') + timestamp + getExtension(relativeName);
- assert(impl::isMatchingVersion(afterLast(relativeName, FILE_NAME_SEPARATOR), afterLast(targetName, FILE_NAME_SEPARATOR))); //paranoid? no!
- break;
- }
-
- try
- {
- moveObj(fullName, targetName); //throw FileError
- }
- catch (FileError&) //expected to fail if target directory is not yet existing!
- {
- if (!somethingExists(fullName)) //no source at all is not an error (however a directory as source when a file is expected, *is* an error!)
- return; //object *not* processed
-
- //create intermediate directories if missing
- const Zstring targetDir = beforeLast(targetName, FILE_NAME_SEPARATOR);
- if (!dirExists(targetDir)) //->(minor) file system race condition!
- {
- makeDirectory(targetDir); //throw FileError
- moveObj(fullName, targetName); //throw FileError -> this should work now!
- }
- else
- throw;
- }
-}
-
-
-//move source to target across volumes
-//no need to check if: - super-directories of target exist - source exists: done by moveItemToVersioning()
-//if target already exists, it is overwritten, even if it is a different type, e.g. a directory!
-template <class Function>
-void moveObject(const Zstring& sourceFile, //throw FileError
- const Zstring& targetFile,
- Function copyDelete) //throw FileError; fallback if move failed
-{
- assert(fileExists(sourceFile) || symlinkExists(sourceFile) || !somethingExists(sourceFile)); //we process files and symlinks only
-
- auto removeTarget = [&]
- {
- //remove target object
- if (dirExists(targetFile)) //directory or dir-symlink
- removeDirectory(targetFile); //throw FileError; we do not expect targetFile to be a directory in general => no callback required
- else //file or (broken) file-symlink
- removeFile(targetFile); //throw FileError
- };
-
- //first try to move directly without copying
- try
- {
- renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
- return; //great, we get away cheaply!
- }
- //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file)
- catch (const ErrorDifferentVolume&)
- {
- removeTarget(); //throw FileError
- copyDelete(); //
- }
- catch (const ErrorTargetExisting&)
- {
- removeTarget(); //throw FileError
- try
- {
- renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
- }
- catch (const ErrorDifferentVolume&)
- {
- copyDelete(); //throw FileError
- }
- }
-}
-
-
-void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile& callback) //throw FileError
-{
- moveObject(sourceFile, //throw FileError
- targetFile,
- [&]
- {
- //create target
- if (symlinkExists(sourceFile))
- copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions
- else
- copyFile(sourceFile, targetFile, false, true, &callback); //throw FileError - permissions "false", transactional copy "true"
-
- //delete source
- removeFile(sourceFile); //throw FileError; newly copied file is NOT deleted if exception is thrown here!
- });
-}
-
-
-void moveDirSymlink(const Zstring& sourceLink, const Zstring& targetLink) //throw FileError
-{
- moveObject(sourceLink, //throw FileError
- targetLink,
- [&]
- {
- //create target
- copySymlink(sourceLink, targetLink, false); //throw FileError; don't copy filesystem permissions
-
- //delete source
- removeDirectory(sourceLink); //throw FileError; newly copied link is NOT deleted if exception is thrown here!
- });
-}
-
-
-class TraverseFilesOneLevel : public TraverseCallback
-{
-public:
- TraverseFilesOneLevel(std::vector<Zstring>& files, std::vector<Zstring>& dirs) : files_(files), dirs_(dirs) {}
-
-private:
- virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details)
- {
- files_.push_back(shortName);
- }
-
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
- {
- if (dirExists(fullName)) //dir symlink
- dirs_.push_back(shortName);
- else //file symlink, broken symlink
- files_.push_back(shortName);
- return LINK_SKIP;
- }
-
- virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName)
- {
- dirs_.push_back(shortName);
- return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively!
- }
-
- virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); }
- virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); }
-
- std::vector<Zstring>& files_;
- std::vector<Zstring>& dirs_;
-};
-}
-
-
-bool FileVersioner::revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError
-{
- struct CallbackMoveFileImpl : public CallbackMoveDir
- {
- CallbackMoveFileImpl(CallbackMoveFile& callback) : callback_(callback) {}
- private:
- virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) {}
- virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) {}
- virtual void updateStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); }
- CallbackMoveFile& callback_;
- } cb(callback);
-
- return revisionFileImpl(fullName, relativeName, cb); //throw FileError
-}
-
-
-bool FileVersioner::revisionFileImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError
-{
- bool moveSuccessful = false;
-
- moveItemToVersioning(fullName, //throw FileError
- relativeName,
- versioningDirectory_,
- timeStamp_,
- versioningStyle_,
- [&](const Zstring& source, const Zstring& target)
- {
- callback.onBeforeFileMove(source, target); //if we're called by revisionDirImpl() we know that "source" exists!
- //when called by revisionFile(), "source" might not exist, however onBeforeFileMove() is not propagated in this case!
-
- struct CopyCallbackImpl : public CallbackCopyFile
- {
- CopyCallbackImpl(CallbackMoveDir& callback) : callback_(callback) {}
- private:
- virtual void deleteTargetFile(const Zstring& targetFile) { assert(!somethingExists(targetFile)); }
- virtual void updateCopyStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); }
- CallbackMoveDir& callback_;
- } copyCallback(callback);
-
- moveFile(source, target, copyCallback); //throw FileError
- moveSuccessful = true;
- });
- return moveSuccessful;
-}
-
-
-void FileVersioner::revisionDir(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError
-{
- //no error situation if directory is not existing! manual deletion relies on it!
- if (!somethingExists(fullName))
- return; //neither directory nor any other object (e.g. broken symlink) with that name existing
- revisionDirImpl(fullName, relativeName, callback); //throw FileError
-}
-
-
-void FileVersioner::revisionDirImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError
-{
- assert(somethingExists(fullName)); //[!]
-
- //create target
- if (symlinkExists(fullName)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well!
- {
- moveItemToVersioning(fullName, //throw FileError
- relativeName,
- versioningDirectory_,
- timeStamp_,
- versioningStyle_,
- [&](const Zstring& source, const Zstring& target)
- {
- callback.onBeforeDirMove(source, target);
- moveDirSymlink(source, target); //throw FileError
- });
- }
- else
- {
- assert(!startsWith(relativeName, FILE_NAME_SEPARATOR));
- assert(endsWith(fullName, relativeName)); //usually, yes, but we might relax this in the future
- const Zstring targetDir = appendSeparator(versioningDirectory_) + relativeName;
-
- //makeDirectory(targetDir); //FileError -> create only when needed in moveFileToVersioning(); avoids empty directories
-
- //traverse source directory one level
- std::vector<Zstring> fileList; //list of *short* names
- std::vector<Zstring> dirList; //
- {
- TraverseFilesOneLevel tol(fileList, dirList); //throw FileError
- traverseFolder(fullName, tol); //
- }
-
- const Zstring fullNamePf = appendSeparator(fullName);
- const Zstring relnamePf = appendSeparator(relativeName);
-
- //move files
- std::for_each(fileList.begin(), fileList.end(),
- [&](const Zstring& shortname)
- {
- revisionFileImpl(fullNamePf + shortname, //throw FileError
- relnamePf + shortname,
- callback);
- });
-
- //move items in subdirectories
- std::for_each(dirList.begin(), dirList.end(),
- [&](const Zstring& shortname)
- {
- revisionDirImpl(fullNamePf + shortname, //throw FileError
- relnamePf + shortname,
- callback);
- });
-
- //delete source
- callback.onBeforeDirMove(fullName, targetDir);
- removeDirectory(fullName); //throw FileError
- }
-}
-
-
-/*
-namespace
-{
-class TraverseVersionsOneLevel : public TraverseCallback
-{
-public:
- TraverseVersionsOneLevel(std::vector<Zstring>& files, std::function<void()> updateUI) : files_(files), updateUI_(updateUI) {}
-
-private:
- virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) { files_.push_back(shortName); updateUI_(); }
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { files_.push_back(shortName); updateUI_(); return LINK_SKIP; }
- virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { updateUI_(); return nullptr; } //DON'T traverse into subdirs
- virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); }
- virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); }
-
- std::vector<Zstring>& files_;
- std::function<void()> updateUI_;
-};
-}
-
-void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileError
-{
- if (versionCountLimit_ < 0) //no limit!
- return;
-
- //buffer map "directory |-> list of immediate child file and symlink short names"
- std::map<Zstring, std::vector<Zstring>, LessFilename> dirBuffer;
-
- auto getVersionsBuffered = [&](const Zstring& dirname) -> const std::vector<Zstring>&
- {
- auto it = dirBuffer.find(dirname);
- if (it != dirBuffer.end())
- return it->second;
-
- std::vector<Zstring> fileShortNames;
- TraverseVersionsOneLevel tol(fileShortNames, updateUI); //throw FileError
- traverseFolder(dirname, tol);
-
- auto& newEntry = dirBuffer[dirname]; //transactional behavior!!!
- newEntry.swap(fileShortNames); //-> until C++11 emplace is available
-
- return newEntry;
- };
-
- std::for_each(fileRelNames.begin(), fileRelNames.end(),
- [&](const Zstring& relativeName) //e.g. "subdir\Sample.txt"
- {
- const Zstring fullname = appendSeparator(versioningDirectory_) + relativeName; //e.g. "D:\Revisions\subdir\Sample.txt"
- const Zstring parentDir = beforeLast(fullname, FILE_NAME_SEPARATOR); //e.g. "D:\Revisions\subdir"
- const Zstring shortname = afterLast(relativeName, FILE_NAME_SEPARATOR); //e.g. "Sample.txt"; returns the whole string if seperator not found
-
- const std::vector<Zstring>& allVersions = getVersionsBuffered(parentDir);
-
- //filter out only those versions that match the given relative name
- std::vector<Zstring> matches; //e.g. "Sample.txt 2012-05-15 131513.txt"
-
- std::copy_if(allVersions.begin(), allVersions.end(), std::back_inserter(matches),
- [&](const Zstring& shortnameVer) { return impl::isMatchingVersion(shortname, shortnameVer); });
-
- //take advantage of version naming convention to find oldest versions
- if (matches.size() <= static_cast<size_t>(versionCountLimit_))
- return;
- std::nth_element(matches.begin(), matches.end() - versionCountLimit_, matches.end(), LessFilename()); //windows: ignore case!
-
- //delete obsolete versions
- std::for_each(matches.begin(), matches.end() - versionCountLimit_,
- [&](const Zstring& shortnameVer)
- {
- updateUI();
- const Zstring fullnameVer = parentDir + FILE_NAME_SEPARATOR + shortnameVer;
- try
- {
- removeFile(fullnameVer); //throw FileError
- }
- catch (FileError&)
- {
-#ifdef ZEN_WIN //if it's a directory symlink:
- if (symlinkExists(fullnameVer) && dirExists(fullnameVer))
- removeDirectory(fullnameVer); //throw FileError
- else
-#endif
- throw;
- }
- });
- });
-}
-*/
diff --git a/lib/versioning.h b/lib/versioning.h
deleted file mode 100644
index 06065656..00000000
--- a/lib/versioning.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef VERSIONING_HEADER_8760247652438056
-#define VERSIONING_HEADER_8760247652438056
-
-#include <vector>
-#include <functional>
-#include <zen/time.h>
-#include <zen/zstring.h>
-#include <zen/int64.h>
-#include <zen/file_error.h>
-#include "../structures.h"
-
-namespace zen
-{
-struct CallbackMoveDir;
-struct CallbackMoveFile;
-
-//e.g. move C:\Source\subdir\Sample.txt -> D:\Revisions\subdir\Sample.txt 2012-05-15 131513.txt
-//scheme: <revisions directory>\<relpath>\<filename>.<ext> YYYY-MM-DD HHMMSS.<ext>
-/*
- - ignores missing source files/dirs
- - creates missing intermediate directories
- - does not create empty directories
- - handles symlinks
- - replaces already existing target files/dirs (supports retry)
- => (unlikely) risk of data loss for naming convention "versioning":
- race-condition if two FFS instances start at the very same second OR multiple folder pairs process the same filename!!
-*/
-
-class FileVersioner
-{
-public:
- FileVersioner(const Zstring& versioningDirectory, //throw FileError
- VersioningStyle versioningStyle,
- const TimeComp& timeStamp) : //max versions per file; < 0 := no limit
- versioningStyle_(versioningStyle),
- versioningDirectory_(versioningDirectory),
- timeStamp_(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513"
- {
- if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000!
- throw FileError(_("Unable to create timestamp for versioning:") + L" \"" + timeStamp_ + L"\"");
- }
-
- bool revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError; return "false" if file is not existing
- void revisionDir (const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError
-
- //void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning!
-
-private:
- bool revisionFileImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError
- void revisionDirImpl (const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError
-
- const VersioningStyle versioningStyle_;
- const Zstring versioningDirectory_;
- const Zstring timeStamp_;
-
- //std::vector<Zstring> fileRelNames; //store list of revisioned file and symlink relative names for limitVersions()
-};
-
-
-struct CallbackMoveFile //see CallbackCopyFile for limitations when throwing exceptions!
-{
- virtual ~CallbackMoveFile() {}
- virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete:
-};
-
-struct CallbackMoveDir //see CallbackCopyFile for limitations when throwing exceptions!
-{
- virtual ~CallbackMoveDir() {}
-
- virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call for each *existing* object!
- virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; //
- virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete:
-};
-
-
-
-namespace impl //declare for unit tests:
-{
-bool isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion);
-}
-}
-
-#endif //VERSIONING_HEADER_8760247652438056
diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp
deleted file mode 100644
index f504d19a..00000000
--- a/lib/xml_base.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "xml_base.h"
-#include <zen/file_handling.h>
-#include <zen/file_io.h>
-
-using namespace zen;
-
-
-//loadXmlDocument vs loadStream:
-//1. better error reporting
-//2. quick exit if (potentially large) input file is not an XML
-XmlDoc xmlAccess::loadXmlDocument(const Zstring& filename) //throw FfsXmlError
-{
- std::string stream;
- try
- {
- FileInput inputFile(filename); //throw FileError
- {
- //quick test whether input is an XML: avoid loading large binary files up front!
- const std::string xmlBegin = "<?xml version=";
- stream.resize(strLength(zen::BYTE_ORDER_MARK_UTF8) + xmlBegin.size());
-
- const size_t bytesRead = inputFile.read(&stream[0], stream.size()); //throw FileError
- stream.resize(bytesRead);
-
- if (!startsWith(stream, xmlBegin) &&
- !startsWith(stream, zen::BYTE_ORDER_MARK_UTF8 + xmlBegin)) //respect BOM!
- throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)));
- }
-
- const size_t blockSize = 128 * 1024;
- do
- {
- stream.resize(stream.size() + blockSize);
-
- const size_t bytesRead = inputFile.read(&*stream.begin() + stream.size() - blockSize, blockSize); //throw FileError
- if (bytesRead < blockSize)
- stream.resize(stream.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
- }
- while (!inputFile.eof());
- }
- catch (const FileError& error)
- {
- throw FfsXmlError(error.toString());
- }
-
- try
- {
- return zen::parse(stream); //throw XmlParsingError
- }
- catch (const XmlParsingError& e)
- {
- throw FfsXmlError(
- replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."),
- L"%x", fmtFileName(filename)),
- L"%y", numberTo<std::wstring>(e.row + 1)),
- L"%z", numberTo<std::wstring>(e.col + 1)));
- }
-}
-
-
-const std::wstring xmlAccess::getErrorMessageFormatted(const std::vector<std::wstring>& failedElements)
-{
- std::wstring msg;
-
- if (!failedElements.empty())
- {
- msg = _("Cannot read the following XML elements:") + L"\n";
- std::for_each(failedElements.begin(), failedElements.end(), [&](const std::wstring& elem) { msg += L"\n" + elem; });
- }
-
- return msg;
-}
-
-
-void xmlAccess::saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename) //throw FfsXmlError
-{
- std::string stream = serialize(doc); //throw ()
-
- bool saveNecessary = true;
- try
- {
- if (zen::getFilesize(filename) == stream.size()) //throw FileError
- try
- {
- if (zen::loadStream(filename) == stream) //throw XmlFileError
- saveNecessary = false;
- }
- catch (const zen::XmlFileError&) {}
- }
- catch (FileError&) {}
-
- if (saveNecessary)
- try
- {
- FileOutput outputFile(filename, FileOutput::ACC_OVERWRITE); //throw FileError
- outputFile.write(stream.c_str(), stream.length()); //
- }
- catch (const FileError& error)
- {
- throw FfsXmlError(error.toString());
- }
-}
diff --git a/lib/xml_base.h b/lib/xml_base.h
deleted file mode 100644
index 85d4dfa1..00000000
--- a/lib/xml_base.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef XMLBASE_H_INCLUDED
-#define XMLBASE_H_INCLUDED
-
-#include <zenxml/xml.h>
-#include <zen/zstring.h>
-#include <zen/file_error.h>
-
-//bind zen::Xml and zen file handling together
-
-namespace xmlAccess
-{
-class FfsXmlError //Exception class
-{
-public:
- enum Severity
- {
- WARNING = 77,
- FATAL
- };
-
- explicit FfsXmlError(const std::wstring& message, Severity sev = FATAL) : errorMessage(message), m_severity(sev) {}
-
- const std::wstring& toString() const { return errorMessage; }
- Severity getSeverity() const { return m_severity; }
-private:
- const std::wstring errorMessage;
- const Severity m_severity;
-};
-
-void saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename); //throw FfsXmlError
-zen::XmlDoc loadXmlDocument(const Zstring& filename); //throw FfsXmlError
-
-const std::wstring getErrorMessageFormatted(const std::vector<std::wstring>& failedElements);
-}
-
-#endif // XMLBASE_H_INCLUDED
bgstack15