From 75c07011b7c4d06acd7b45dabdcd60ab9d80f385 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 17:29:28 +0200 Subject: 5.23 --- lib/Batch.ico | Bin 103260 -> 0 bytes lib/FreeFileSync.ico | Bin 114124 -> 0 bytes lib/ShadowCopy/Shadow_Windows7.vcxproj | 240 ----- lib/ShadowCopy/shadow.cpp | 199 ---- lib/ShadowCopy/shadow.h | 94 -- lib/SyncDB.ico | Bin 111496 -> 0 bytes lib/Thumbnail/Thumbnail.vcxproj | 236 ----- lib/Thumbnail/thumbnail.cpp | 486 ---------- lib/Thumbnail/thumbnail.h | 85 -- lib/binary.cpp | 134 --- lib/binary.h | 25 - lib/cmp_filetime.h | 57 -- lib/db_file.cpp | 821 ----------------- lib/db_file.h | 102 --- lib/dir_exist_async.h | 78 -- lib/dir_lock.cpp | 685 -------------- lib/dir_lock.h | 45 - lib/error_log.h | 43 - lib/ffs_paths.cpp | 144 --- lib/ffs_paths.h | 25 - lib/generate_logfile.h | 181 ---- lib/hard_filter.cpp | 404 -------- lib/hard_filter.h | 270 ------ lib/help_provider.h | 98 -- lib/icon_buffer.cpp | 674 -------------- lib/icon_buffer.h | 52 -- lib/localization.cpp | 485 ---------- lib/localization.h | 46 - lib/lock_holder.h | 57 -- lib/norm_filter.h | 85 -- lib/osx_file_icon.h | 36 - lib/osx_file_icon.mm | 179 ---- lib/parallel_scan.cpp | 588 ------------ lib/parallel_scan.h | 76 -- lib/parse_lng.h | 706 -------------- lib/parse_plural.h | 478 ---------- lib/perf_check.cpp | 262 ------ lib/perf_check.h | 45 - lib/process_xml.cpp | 1577 -------------------------------- lib/process_xml.h | 298 ------ lib/resolve_path.cpp | 708 -------------- lib/resolve_path.h | 36 - lib/return_codes.h | 30 - lib/shadow.cpp | 131 --- lib/shadow.h | 36 - lib/soft_filter.h | 112 --- lib/status_handler.cpp | 29 - lib/status_handler.h | 147 --- lib/status_handler_impl.h | 37 - lib/versioning.cpp | 439 --------- lib/versioning.h | 89 -- lib/xml_base.cpp | 108 --- lib/xml_base.h | 42 - 53 files changed, 12040 deletions(-) delete mode 100644 lib/Batch.ico delete mode 100644 lib/FreeFileSync.ico delete mode 100644 lib/ShadowCopy/Shadow_Windows7.vcxproj delete mode 100644 lib/ShadowCopy/shadow.cpp delete mode 100644 lib/ShadowCopy/shadow.h delete mode 100644 lib/SyncDB.ico delete mode 100644 lib/Thumbnail/Thumbnail.vcxproj delete mode 100644 lib/Thumbnail/thumbnail.cpp delete mode 100644 lib/Thumbnail/thumbnail.h delete mode 100644 lib/binary.cpp delete mode 100644 lib/binary.h delete mode 100644 lib/cmp_filetime.h delete mode 100644 lib/db_file.cpp delete mode 100644 lib/db_file.h delete mode 100644 lib/dir_exist_async.h delete mode 100644 lib/dir_lock.cpp delete mode 100644 lib/dir_lock.h delete mode 100644 lib/error_log.h delete mode 100644 lib/ffs_paths.cpp delete mode 100644 lib/ffs_paths.h delete mode 100644 lib/generate_logfile.h delete mode 100644 lib/hard_filter.cpp delete mode 100644 lib/hard_filter.h delete mode 100644 lib/help_provider.h delete mode 100644 lib/icon_buffer.cpp delete mode 100644 lib/icon_buffer.h delete mode 100644 lib/localization.cpp delete mode 100644 lib/localization.h delete mode 100644 lib/lock_holder.h delete mode 100644 lib/norm_filter.h delete mode 100644 lib/osx_file_icon.h delete mode 100644 lib/osx_file_icon.mm delete mode 100644 lib/parallel_scan.cpp delete mode 100644 lib/parallel_scan.h delete mode 100644 lib/parse_lng.h delete mode 100644 lib/parse_plural.h delete mode 100644 lib/perf_check.cpp delete mode 100644 lib/perf_check.h delete mode 100644 lib/process_xml.cpp delete mode 100644 lib/process_xml.h delete mode 100644 lib/resolve_path.cpp delete mode 100644 lib/resolve_path.h delete mode 100644 lib/return_codes.h delete mode 100644 lib/shadow.cpp delete mode 100644 lib/shadow.h delete mode 100644 lib/soft_filter.h delete mode 100644 lib/status_handler.cpp delete mode 100644 lib/status_handler.h delete mode 100644 lib/status_handler_impl.h delete mode 100644 lib/versioning.cpp delete mode 100644 lib/versioning.h delete mode 100644 lib/xml_base.cpp delete mode 100644 lib/xml_base.h (limited to 'lib') diff --git a/lib/Batch.ico b/lib/Batch.ico deleted file mode 100644 index faa2db64..00000000 Binary files a/lib/Batch.ico and /dev/null differ diff --git a/lib/FreeFileSync.ico b/lib/FreeFileSync.ico deleted file mode 100644 index 88f656ee..00000000 Binary files a/lib/FreeFileSync.ico and /dev/null 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 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Windows7 - {7E217D76-90A5-4B03-A6F8-E7C3ADD22901} - ShadowDll - Win32Proj - - - - DynamicLibrary - Unicode - true - false - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - Shadow_$(ProjectName)_$(Platform) - Shadow_$(ProjectName)_$(Platform) - Shadow_$(ProjectName)_$(Platform) - Shadow_$(ProjectName)_$(Platform) - - - - $(IntDir)Build.html - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100;4996 - ../..;C:\Data\C++\boost - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX86 - C:\Data\C++\Boost\stage\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - Disabled - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100;4996 - ../..;C:\Data\C++\boost - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX64 - C:\Data\C++\Boost\stage_x64\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - Speed - 4100;4996 - ../..;C:\Data\C++\boost - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX86 - C:\Data\C++\Boost\stage\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - MaxSpeed - true - MultiThreaded - true - - - Level4 - true - ProgramDatabase - Speed - 4100;4996 - ../..;C:\Data\C++\boost - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX64 - C:\Data\C++\Boost\stage_x64\lib - %(AdditionalDependencies) - - - - - - - - - - - - - \ 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 -#include -#include -#include -#include -#include - -#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 // -#include //part of Windows SDK for Windows 7 -#include // -#pragma comment(lib, "VssApi.lib") - -#else -#error adapt! -#endif - -using namespace zen; - - -struct shadow::ShadowData -{ - ShadowData(const ComPtr& backupComp, - const std::wstring& shadowVolume) : backupComp_(backupComp), shadowVolume_(shadowVolume) {} - - ComPtr 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 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 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(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 prepareAsync; - ZEN_COM_CHECK(backupComp->PrepareForBackup(prepareAsync.init())); - waitForComFuture(*prepareAsync); - - ComPtr 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 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 -#include - - -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 Binary files a/lib/SyncDB.ico and /dev/null 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 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {70394AEF-5897-4911-AFA1-82EAF0581EFA} - ShadowDll - Win32Proj - - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - Thumbnail_$(Platform) - Thumbnail_$(Platform) - Thumbnail_$(Platform) - Thumbnail_$(Platform) - - - - $(IntDir)Build.html - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100;4996 - ../.. - true - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100;4996 - ../.. - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100;4996 - Speed - ../.. - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100;4996 - Speed - ../.. - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - - - - - - - - - - - - \ 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 -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include - -#define STRICT_TYPED_ITEMIDS //better type safety for IDLists -#include - -#include -#include - -#include -#include -#include -#include -#include -//#include - -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 idata = make_unique(); - - 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(&bi), //_In_ const BITMAPINFO *pbmi, - DIB_RGB_COLORS, //_In_ UINT iUsage, - reinterpret_cast(&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 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(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 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(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 extractImage; - ZEN_COM_CHECK(imageFolder->GetUIObjectOf(nullptr, // [in] HWND hwndOwner, - 1, // [in] UINT cidl, - reinterpret_cast(&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(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 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(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(numeric::confineCpy(tmp / 3, 0, 255)); - - auto calcColor = [&](unsigned char c_black, unsigned char c_white) - { - return static_cast(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 -//#include - -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 -#include -#include -#include -#include - -using namespace zen; - - -namespace -{ -inline -void setMinSize(std::vector& 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> cpyBuf1; - static boost::thread_specific_ptr> cpyBuf2; - if (!cpyBuf1.get()) - cpyBuf1.reset(new std::vector()); - if (!cpyBuf2.get()) - cpyBuf2.reset(new std::vector()); - - std::vector& memory1 = *cpyBuf1; - std::vector& 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(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 -#include -#include - -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 -#include - -namespace zen -{ -//--------------------------------------------------------------------------------------------------------------- -inline -bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance) -{ - if (a < b) - return b <= a + static_cast(tolerance); - else - return a <= b + static_cast(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 -#include -#include -#include -#include -#include - -#ifdef ZEN_WIN -#include //includes "windows.h" -#include -#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 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 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() + 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(streamOut, DB_FORMAT_CONTAINER); - - //save stream list - writeNumber(streamOut, static_cast(streamList.size())); //number of streams, one for each sync-pair - - for (const auto& stream : streamList) - { - writeContainer(streamOut, stream.first ); - writeContainer(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(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(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(streamIn); //number of streams, one for each sync-pair - while (dbCount-- != 0) - { - //DB id of partner databases - std::string sessionID = readContainer(streamIn); //throw UnexpectedEndOfStreamError - BinaryStream stream = readContainer(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(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(outL, DB_FORMAT_STREAM); - writeNumber(outR, DB_FORMAT_STREAM); - - //distribute "outputBoth" over left and right streams: - writeNumber(outL, true); //this side contains first part of "outputBoth" - writeNumber(outR, false); - - const size_t size1stPart = tmpB.size() / 2; - const size_t size2ndPart = tmpB.size() - size1stPart; - - writeNumber(outL, size1stPart); - writeNumber(outR, size2ndPart); - - writeArray(outL, &*tmpB.begin(), size1stPart); - writeArray(outR, &*tmpB.begin() + size1stPart, size2ndPart); - - //write streams corresponding to one side only - writeContainer(outL, tmpL); - writeContainer(outR, tmpR); - - streamL = outL.get(); - streamR = outR.get(); - } - -private: - void recurse(const InSyncDir& container) - { - writeNumber(outputBoth, static_cast(container.files.size())); - for (const auto& dbFile : container.files) - { - writeUtf8(outputBoth, dbFile.first); - writeNumber(outputBoth, dbFile.second.cmpVar); - writeNumber(outputBoth, to(dbFile.second.fileSize)); - - writeFile(outputLeft, dbFile.second.left); - writeFile(outputRight, dbFile.second.right); - } - - writeNumber(outputBoth, static_cast(container.symlinks.size())); - for (const auto& dbSymlink : container.symlinks) - { - writeUtf8(outputBoth, dbSymlink.first); - writeNumber(outputBoth, dbSymlink.second.cmpVar); - - writeLink(outputLeft, dbSymlink.second.left); - writeLink(outputRight, dbSymlink.second.right); - } - - writeNumber(outputBoth, static_cast(container.dirs.size())); - for (const auto& dbDir : container.dirs) - { - writeUtf8(outputBoth, dbDir.first); - writeNumber(outputBoth, dbDir.second.status); - - recurse(dbDir.second); - } - } - - static void writeUtf8(BinStreamOut& output, const Zstring& str) { writeContainer(output, utfCvrtTo>(str)); } - - static void writeFile(BinStreamOut& output, const InSyncDescrFile& descr) - { - writeNumber(output, to(descr.lastWriteTimeRaw)); - writeNumber(output, descr.fileId.first); - writeNumber(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(output, to(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 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(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(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(inL) != 0; //throw UnexpectedEndOfStreamError - has1stPartR = readNumber(inR) != 0; // - } - else - { - has1stPartL = readNumber(inL) != 0; //throw UnexpectedEndOfStreamError - has1stPartR = readNumber(inR) != 0; // - } - - if (has1stPartL == has1stPartR) - throw UnexpectedEndOfStreamError(); - - BinStreamIn& in1stPart = has1stPartL ? inL : inR; - BinStreamIn& in2ndPart = has1stPartL ? inR : inL; - - const size_t size1stPart = static_cast(readNumber(in1stPart)); - const size_t size2ndPart = static_cast(readNumber(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(inL); - const BinaryStream tmpR = readContainer(inR); - - auto output = std::make_shared(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(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(inputBoth); - while (fileCount-- != 0) - { - const Zstring shortName = readUtf8(inputBoth); - - if (migrateStreamFromOldFormat_) - { - const auto inSyncType = readNumber(inputBoth); - const CompareVariant cmpVar = inSyncType == 0 ? CMP_BY_CONTENT : CMP_BY_TIME_SIZE; - - auto lastWriteTimeRawL = readNumber(inputLeft); //throw UnexpectedEndOfStreamError - const UInt64 fileSize = readNumber(inputLeft); - auto devIdL = static_cast(readNumber(inputLeft)); // - auto fileIdxL = static_cast(readNumber(inputLeft)); //silence "loss of precision" compiler warnings - const InSyncDescrFile dataL = InSyncDescrFile(lastWriteTimeRawL, FileId(devIdL, fileIdxL)); - - auto lastWriteTimeRaw = readNumber(inputRight); //throw UnexpectedEndOfStreamError - readNumber(inputRight); - auto devId = static_cast(readNumber(inputRight)); // - auto fileIdx = static_cast(readNumber(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(readNumber(inputBoth)); - const UInt64 fileSize = readNumber(inputBoth); - const InSyncDescrFile dataL = readFile(inputLeft); - const InSyncDescrFile dataR = readFile(inputRight); - container.addFile(shortName, dataL, dataR, cmpVar, fileSize); - } - } - - size_t linkCount = readNumber(inputBoth); - while (linkCount-- != 0) - { - const Zstring shortName = readUtf8(inputBoth); - - if (migrateStreamFromOldFormat_) - { - const CompareVariant cmpVar = CMP_BY_CONTENT; - auto lastWriteTimeRaw = readNumber(inputLeft); - readUtf8(inputLeft);//descr.targetPath = readUtf8(input); - readNumber(inputLeft);//descr.type = static_cast(readNumber(input)); - InSyncDescrLink dataL = InSyncDescrLink(lastWriteTimeRaw); - - auto lastWriteTimeRawR = readNumber(inputRight); - readUtf8(inputRight);//descr.targetPath = readUtf8(input); - readNumber(inputRight);//descr.type = static_cast(readNumber(input)); - InSyncDescrLink dataR = InSyncDescrLink(lastWriteTimeRawR); - - container.addSymlink(shortName, dataL, dataR, cmpVar); - } - else - { - const auto cmpVar = static_cast(readNumber(inputBoth)); - InSyncDescrLink dataL = readLink(inputLeft); - InSyncDescrLink dataR = readLink(inputRight); - container.addSymlink(shortName, dataL, dataR, cmpVar); - } - } - - size_t dirCount = readNumber(inputBoth); - while (dirCount-- != 0) - { - const Zstring shortName = readUtf8(inputBoth); - auto status = static_cast(readNumber(inputBoth)); - - InSyncDir& subDir = container.addDir(shortName, status); - recurse(subDir); - } - } - - static Zstring readUtf8(BinStreamIn& input) { return utfCvrtTo(readContainer>(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(input); //throw UnexpectedEndOfStreamError - auto devId = static_cast(readNumber(input)); // - auto fileIdx = static_cast(readNumber(input)); //silence "loss of precision" compiler warnings - return InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx)); - } - - static InSyncDescrLink readLink(BinStreamIn& input) - { - auto lastWriteTimeRaw = readNumber(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 - 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 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() == fileObj.getShortName()); - //this should be taken for granted: - assert(fileObj.getFileSize() == fileObj.getFileSize()); - - //create or update new "in-sync" state - InSyncFile& file = updateItem(dbFiles, fileObj.getObjShortName(), - InSyncFile(InSyncDescrFile(fileObj.getLastWriteTime(), - fileObj.getFileId ()), - InSyncDescrFile(fileObj.getLastWriteTime(), - fileObj.getFileId ()), - activeCmpVar_, - fileObj.getFileSize())); - 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 toPreserve; - for (const SymlinkPair& linkObj : currentLinks) - if (!linkObj.isEmpty()) - { - if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state - { - assert(linkObj.getShortName() == linkObj.getShortName()); - - //create or update new "in-sync" state - InSyncSymlink& link = updateItem(dbLinks, linkObj.getObjShortName(), - InSyncSymlink(InSyncDescrLink(linkObj.getLastWriteTime()), - InSyncDescrLink(linkObj.getLastWriteTime()), - 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 toPreserve; - for (const DirPair& dirObj : currentDirs) - if (!dirObj.isEmpty()) - switch (dirObj.getDirCategory()) - { - case DIR_EQUAL: - { - assert(dirObj.getShortName() == dirObj.getShortName()); - - //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 zen::loadLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! -{ - const Zstring fileNameLeft = getDBFilename(baseDirObj); - const Zstring fileNameRight = getDBFilename(baseDirObj); - - if (!baseDirObj.isExisting() || - !baseDirObj.isExisting()) - { - //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() ? 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(baseDirObj, true); - const Zstring dbNameRightTmp = getDBFilename(baseDirObj, true); - - const Zstring dbNameLeft = getDBFilename(baseDirObj); - const Zstring dbNameRight = getDBFilename(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 lastSyncState = std::make_shared(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 -#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 DirList; // - typedef std::map FileList; // key: shortName - typedef std::map 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 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 -#include -#include -#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 -std::set getExistingDirsUpdating(const std::set& dirnames, - std::set& missing, - bool allowUserInteraction, - ProcessCallback& procCallback) -{ - using namespace zen; - - missing.clear(); - - std::list>> futureInfo; - for (const Zstring& dirname : dirnames) - if (!dirname.empty()) - futureInfo.push_back(std::make_pair(dirname, async2([=]() -> bool - { -#ifdef ZEN_WIN - //1. login to network share, if necessary - loginNetworkShare(dirname, allowUserInteraction); -#endif - //2. check dir existence - return dirExists(dirname); - }))); - - std::set 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 missing; - std::set 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 -#include -#include -#include -#include //includes -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef ZEN_WIN -#include -#include //includes "windows.h" -#include -#include //login sid -#include //UNLEN - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include //open() -#include // -#include //getsid() -#include //kill() -#include //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(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 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(&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 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 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(&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(&buffer[0]); - -#elif defined ZEN_LINUX || defined ZEN_MAC - processId(::getpid()) //never fails - { - std::vector 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(&userIdNo), sizeof(userIdNo)); - - //the id alone is not very distinctive, e.g. often 1000 on Ubuntu => add name - buffer.resize(std::max(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 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(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(stream); // - computerName = readContainer(stream); //UnexpectedEndOfStreamError - userId = readContainer(stream); // - sessionId = static_cast(readNumber(stream)); //[!] conversion - processId = static_cast(readNumber(stream)); //[!] conversion - } - - void toStream(BinStreamOut& stream) const //throw () - { - writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); - writeNumber(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(stream, sessionId); - writeNumber(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(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 = 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(0, DETECT_ABANDONED_INTERVAL - dist(lastLifeSign, getTicks()) / TICKS_PER_SEC); - const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo(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 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& 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& 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(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 FileToGuidMap; //n:1 handle uppper/lower case correctly - typedef std::map> GuidToLockMap; //1:1 - - std::shared_ptr 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 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 -#include - -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 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 -#include -#include -#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(FORMAT_DATE) + " "+ formatTime(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 -#include -#include -#include - -#ifdef ZEN_MAC -#include -#include -#include -//keep in .cpp file to not pollute global namespace! e.g. with UInt64: -#include //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(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 - -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 -#include -#include -#include -#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(FORMAT_DATE); - if (!s.jobName.empty()) - headerLine += L" - " + s.jobName; - headerLine += L": " + s.finalStatus; - - //assemble results box - std::vector 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(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(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(formatMessage(*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(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(formatMessage(*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(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 -#include -#include -#include -#include -//#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(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& fileFilter, std::vector& 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 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& 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& filter) -{ - return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); }); -} - - -std::vector splitByDelimiter(const Zstring& filterString) -{ - //delimiters may be ';' or '\n' - std::vector output; - - const std::vector blocks = split(filterString, Zchar(';')); //split by less common delimiter first - std::for_each(blocks.begin(), blocks.end(), - [&](const Zstring& item) - { - const std::vector 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& includeList = splitByDelimiter(includeFilter); - const std::vector& 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& cont) - { - std::vector output; - std::set 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(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(stream); //throw UnexpectedEndOfStreamError -// const Zstring exclude = readString(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 -#include -#include -//#include - -namespace zen -{ -//------------------------------------------------------------------ -/* -Semantics of HardFilter: -1. using it creates a NEW folder hierarchy! -> must be considered by -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 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 filterFileIn; // - std::vector filterFolderIn; //upper case (windows) + unique items by construction - std::vector filterFileEx; // - std::vector 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(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(); - else - return second; - } - else - { - if (second->isNull()) - return first; - else - return std::make_shared(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 -#include - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include -#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(zen::getResourceDir()) + L"FreeFileSync.chm"); - } - - void openSection(const wxString& section, wxWindow* parent) - { - if (section.empty()) - chmHlp.DisplayContents(); - else - chmHlp.DisplaySection(replaceCpy(section, L'/', utfCvrtTo(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(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 -#include -#include //includes -#include - -#ifdef ZEN_WIN -#include -#include -#include -#include "Thumbnail/thumbnail.h" - -#elif defined ZEN_LINUX -#include - -#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::getDllName(), thumb::funName_##name) : DllFun(); -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 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 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& newLoad) //context of main thread - { - assert(boost::this_thread::get_id() == mainThreadId ); - { - boost::lock_guard 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 dummy(lockFiles); - filesToLoad.push_back(newEntry); //set as next item to retrieve - } - conditionNewFiles.notify_all(); - } - -private: - std::list 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 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 retrieveFileIcon(const Zstring& fileName) - { - assert(boost::this_thread::get_id() == mainThreadId); - boost::lock_guard 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(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 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 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 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 iconList; //shared resource; Zstring is thread-safe like an int - std::queue 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, - const std::shared_ptr& buffer, - IconBuffer::IconSize st) : - workload_(workload), - buffer_(buffer), - iconSizeType(st) {} - - void operator()(); //thread entry - -private: - std::shared_ptr workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) - std::shared_ptr 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 fileIconInit(L"Shell32.dll", reinterpret_cast(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()), - buffer (std::make_shared()) {} - - std::shared_ptr workload; - std::shared_ptr buffer; - - boost::thread worker; -}; - - -IconBuffer::IconBuffer(IconSize sz) : pimpl(make_unique()), - 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()); //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 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 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 ico = pimpl->buffer->retrieveFileIcon(extension); - assert(ico); - return ico; - } - } -#endif - - if (Opt 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& 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 -#include -#include -#include -#include - -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 retrieveFileIcon(const Zstring& filename); - - void setWorkload(const std::list& load); //(re-)set new workload of icons to be retrieved; - -private: - struct Pimpl; - std::unique_ptr 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "parse_plural.h" -#include "parse_lng.h" -#include "ffs_paths.h" - -#ifdef ZEN_LINUX -#include //wcscasecmp - -#elif defined ZEN_MAC -#include -#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 Translation; //hash_map is 15% faster than std::map on GCC - typedef std::map, std::vector> TranslationPlural; - - Translation transMapping; //map original text |-> translation - TranslationPlural transMappingPl; - std::unique_ptr 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(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(item.first); - const std::wstring translation = utfCvrtTo(item.second); - transMapping.insert(std::make_pair(original, translation)); - } - - for (const auto& item : transPluralInput) - { - const std::wstring engSingular = utfCvrtTo(item.first.first); - const std::wstring engPlural = utfCvrtTo(item.first.second); - - std::vector plFormsWide; - for (const std::string& pf : item.second) - plFormsWide.push_back(utfCvrtTo(pf)); - - transMappingPl.insert(std::make_pair(std::make_pair(engSingular, engPlural), plFormsWide)); - } - - pluralParser = make_unique(header.pluralDefinition); //throw parse_plural::ParsingError -} - - -class FindLngfiles : public zen::TraverseCallback -{ -public: - FindLngfiles(std::vector& 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& 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(lhs.languageName.size()), //__in int cchCount1, - rhs.languageName.c_str(), //__in LPCTSTR lpString2, - static_cast(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(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 lngFiles; - FindLngfiles traverseCallback(lngFiles); - - traverseFolder(zen::getResourceDir() + Zstr("Languages"), //throw(); - traverseCallback); - - for (const Zstring& filename : lngFiles) - { - try - { - const std::string stream = loadBinStream(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(lngHeader.localeName))) - { - ExistingTranslations::Entry newEntry; - newEntry.languageID = locInfo->Language; - newEntry.languageName = utfCvrtTo(lngHeader.languageName); - newEntry.languageFile = utfCvrtTo(filename); - newEntry.translatorName = utfCvrtTo(lngHeader.translatorName); - newEntry.languageFlag = utfCvrtTo(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::get() -{ - static ExistingTranslations instance; - return instance.locMapping; -} - - -namespace -{ -wxLanguage mapLanguageDialect(wxLanguage language) -{ - switch (static_cast(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 locale; - static wxLanguage locLng; -}; -std::unique_ptr 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(languageFile), static_cast(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(languageFile))), - L"%y", numberTo(e.row_ + 1)), - L"%z", numberTo(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(language)); -} - - -int zen::getLanguage() -{ - const FFSTranslation* loc = dynamic_cast(zen::getTranslator()); - return loc ? loc->langId() : wxLANGUAGE_ENGLISH_US; -} - - -int zen::retrieveSystemLanguage() -{ - return mapLanguageDialect(static_cast(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 -#include -//#include - -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& get(); - -private: - ExistingTranslations(); - ExistingTranslations(const ExistingTranslations&); - ExistingTranslations& operator=(const ExistingTranslations&); - - std::vector 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 -#include -#include -#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& 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 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(global.includeFilter, global.excludeFilter); - HardFilter::FilterRef localName = std::make_shared(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 -#include - -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 rgb; //rgb-byte order for use with wxImage - std::vector 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 -#include -#include - -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 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(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 -#include -#include //includes -#include -#include -#include -#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 volName(std::max(pathName.size(), static_cast(10000))); - - DiskInfo output; - - //full pathName need not yet exist! - if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &volName[0], //__out LPTSTR lpszVolumePathName, - static_cast(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 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(buffer.size()), // size of output buffer - &bytesReturned, // number of bytes returned - nullptr)) // OVERLAPPED structure - return output; - - const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast(&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> separateByDistinctDisk(const std::set& dirkeys) -{ - //use one thread per physical disk: - typedef std::map> DiskKeyMapping; - DiskKeyMapping tmp; - std::for_each(dirkeys.begin(), dirkeys.end(), - [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); }); - - std::vector> buckets; - std::transform(tmp.begin(), tmp.end(), std::back_inserter(buckets), - [&](const DiskKeyMapping::value_type& diskToKey) { return diskToKey.second; }); - return buckets; -} -*/ - -//------------------------------------------------------------------------------------------ -typedef Zbase 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 dummy(lockErrorInfo); - while (errorInfo.get() || errorResponse.get()) - conditionCanReportError.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! - - errorInfo = make_unique>(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 dummy(lockErrorInfo); - if (errorInfo.get() && !errorResponse.get()) - { - FillBufferCallback::HandleError rv = callback.reportError(copyStringTo(errorInfo->first), errorInfo->second); //throw! - errorResponse = make_unique(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 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 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 dummy(lockCurrentStatus); - if (!currentFile.empty()) - filename = currentFile; - else if (!currentStatus.empty()) - statusMsg = copyStringTo(currentStatus); - } - - if (!filename.empty()) - { - std::wstring statusText = copyStringTo(textScanning); - const long activeCount = activeWorker; - if (activeCount >= 2) - statusText += L" [" + replaceCpy(_P("1 thread", "%x threads", activeCount), L"%x", numberTo(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> errorInfo; //error message + retry number - std::unique_ptr 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& failedDirReads, - std::set& 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& failedDirReads_; - std::set& 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& 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 acb_; - const DirectoryKey dirKey_; - DirectoryValue& dirOutput_; -}; -} - - -void zen::fillBuffer(const std::set& keysToRead, //in - std::map& buf, //out - FillBufferCallback& callback, - size_t updateInterval) -{ - buf.clear(); - - FixedList worker; //note: we cannot use std::vector: 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(); - - //init worker threads - for (const DirectoryKey& key : keysToRead) - { - assert(buf.find(key) == buf.end()); - DirectoryValue& dirOutput = buf[key]; - - const long threadId = static_cast(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 -#include -#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 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 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& keysToRead, //in - std::map& 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "parse_plural.h" -//#include - -namespace lngfile -{ -//singular forms -typedef std::map TranslationMap; //orig |-> translation - -//plural forms -typedef std::pair SingularPluralPair; //1 house| n houses -typedef std::vector PluralForms; //1 dom | 2 domy | 5 domów -typedef std::map 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(std::make_pair(orig, it->second))); - else - switch (newItemPos_) - { - case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared(std::make_pair(orig, std::string()))); - break; - case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared(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(std::make_pair(orig, it->second))); - else - switch (newItemPos_) - { - case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared(std::make_pair(orig, PluralForms()))); - break; - case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared(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) { return !item->hasTranslation(); }); } - - template - 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(item.get())) - onTrans(regular->value); - else if (auto plural = dynamic_cast(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> sequence; //ordered list of translation elements - - std::set transUnique; //check uniqueness - std::set 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 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, "
")); - tokens.insert(std::make_pair(Token::TK_HEADER_END, "
")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "")); - - //item level - tokens.insert(std::make_pair(Token::TK_SRC_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_SRC_END, "")); - tokens.insert(std::make_pair(Token::TK_TRG_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_TRG_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_END, "")); - } - 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(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(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(L"Placeholder %x missing in translation", L"%x", zen::utfCvrtTo(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(translation.size())) - throw ParsingError(replaceCpy(replaceCpy(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo(translation.size())), L"%y", numberTo(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(L"Duplicate plural form translation at index position %x", L"%x", numberTo(it2 - translation.begin())), scn.posRow(), scn.posCol()); - } - - for (int pos = 0; pos < static_cast(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(firstNumber)))) - throw ParsingError(replaceCpy(replaceCpy(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder", - L"%y", numberTo(pos)), L"%z", numberTo(firstNumber)), scn.posRow(), scn.posCol()); - } - } - else - { - //ensure the placeholder is used when needed - if (!contains(translation[pos], "%x")) - throw ParsingError(replaceCpy(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo(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(L"Placeholder %x missing in plural form source", L"%x", zen::utfCvrtTo(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(L"Placeholder %x missing in plural form translation", L"%x", zen::utfCvrtTo(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(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 -#include -#include -#include - -namespace parse_plural -{ -//expression interface -struct Expression { virtual ~Expression() {} }; - -template -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(expr->eval()); } - -private: - std::shared_ptr> 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(forms.size()); } - bool isSingleNumberForm(int index) const { return 0 <= index && index < static_cast(forms.size()) ? forms[index].count == 1 : false; } - int getFirstNumber (int index) const { return 0 <= index && index < static_cast(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 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 -struct BinaryExp : public Expr -{ - typedef std::shared_ptr> ExpLhs; - typedef std::shared_ptr> 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 inline -std::shared_ptr> makeBiExp(const std::shared_ptr& lhs, const std::shared_ptr& rhs, StlOp biop) //throw ParsingError -{ - auto exLeft = std::dynamic_pointer_cast>(lhs); - auto exRight = std::dynamic_pointer_cast>(rhs); - if (!exLeft || !exRight) - throw ParsingError(); - return std::make_shared>(exLeft, exRight, biop); -} - -template -struct ConditionalExp : public Expr -{ - ConditionalExp(const std::shared_ptr>& ifExp, - const std::shared_ptr>& thenExp, - const std::shared_ptr>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } - - virtual typename Expr::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } -private: - std::shared_ptr> ifExp_; - std::shared_ptr> thenExp_; - std::shared_ptr> elseExp_; -}; - -struct ConstNumberExp : public Expr -{ - ConstNumberExp(std::int64_t n) : n_(n) {} - virtual std::int64_t eval() const { return n_; } -private: - std::int64_t n_; -}; - -struct VariableNumberNExp : public Expr -{ - 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::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(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); - } - - typedef std::vector> 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> parse() //throw ParsingError; return value always bound! - { - auto e = std::dynamic_pointer_cast>(parseExpression()); //throw ParsingError - if (!e) - throw ParsingError(); - expectToken(Token::TK_END); - return e; - } - -private: - std::shared_ptr parseExpression() { return parseConditional(); }//throw ParsingError - - std::shared_ptr parseConditional() //throw ParsingError - { - std::shared_ptr e = parseLogicalOr(); - - if (token().type == Token::TK_TERNARY_QUEST) - { - nextToken(); - - auto ifExp = std::dynamic_pointer_cast>(e); - auto thenExp = std::dynamic_pointer_cast>(parseExpression()); //associativity: <- - - expectToken(Token::TK_TERNARY_COLON); - nextToken(); - - auto elseExp = std::dynamic_pointer_cast>(parseExpression()); // - if (!ifExp || !thenExp || !elseExp) - throw ParsingError(); - return std::make_shared>(ifExp, thenExp, elseExp); - } - return e; - } - - std::shared_ptr parseLogicalOr() - { - std::shared_ptr e = parseLogicalAnd(); - while (token().type == Token::TK_OR) //associativity: -> - { - nextToken(); - - std::shared_ptr rhs = parseLogicalAnd(); - e = makeBiExp(e, rhs, std::logical_or()); //throw ParsingError - } - return e; - } - - std::shared_ptr parseLogicalAnd() - { - std::shared_ptr e = parseEquality(); - while (token().type == Token::TK_AND) //associativity: -> - { - nextToken(); - std::shared_ptr rhs = parseEquality(); - - e = makeBiExp(e, rhs, std::logical_and()); //throw ParsingError - } - return e; - } - - std::shared_ptr parseEquality() - { - std::shared_ptr e = parseRelational(); - - Token::Type t = token().type; - if (t == Token::TK_EQUAL || //associativity: n/a - t == Token::TK_NOT_EQUAL) - { - nextToken(); - std::shared_ptr rhs = parseRelational(); - - if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to ()); //throw ParsingError - if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to()); // - } - return e; - } - - std::shared_ptr parseRelational() - { - std::shared_ptr 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 rhs = parseMultiplicative(); - - if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less ()); // - if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal ()); //throw ParsingError - if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater ()); // - if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal()); // - } - return e; - } - - std::shared_ptr parseMultiplicative() - { - std::shared_ptr e = parsePrimary(); - - while (token().type == Token::TK_MODULUS) //associativity: -> - { - nextToken(); - std::shared_ptr rhs = parsePrimary(); - - //"compile-time" check: n % 0 - if (auto literal = std::dynamic_pointer_cast(rhs)) - if (literal->eval() == 0) - throw ParsingError(); - - e = makeBiExp(e, rhs, std::modulus()); //throw ParsingError - } - return e; - } - - std::shared_ptr parsePrimary() - { - if (token().type == Token::TK_VARIABLE_N) - { - nextToken(); - return std::make_shared(n_); - } - else if (token().type == Token::TK_CONST_NUMBER) - { - const std::int64_t number = token().number; - nextToken(); - return std::make_shared(number); - } - else if (token().type == Token::TK_BRACKET_LEFT) - { - nextToken(); - std::shared_ptr 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(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 -//#include -#include -#include -#include - -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(it->first)); - outputFile.Write(wxT(";")); - outputFile.Write(numberTo(it->second.objCount_)); - outputFile.Write(wxT(";")); - outputFile.Write(numberTo(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::value_type*, const std::multimap::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 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 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 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 -#include -#include - -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 getRemainingTime(double dataRemaining) const; - zen::Opt getBytesPerSecond() const; //for window - zen::Opt getItemsPerSecond() const; //for window - -private: - struct Record - { - Record(int itemCount, double data) : itemCount_(itemCount), data_(data) {} - int itemCount_; - double data_; //unit: [bytes] - }; - - std::pair::value_type*, - const std::multimap::value_type*> getBlockFromEnd(long windowSize) const; - - const long windowSizeRemTime; //unit: [ms] - const long windowSizeSpeed_; // - const long windowMax; - - std::map 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 -#include -#include "ffs_paths.h" -#include -#include -//#include -#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() == "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 splitFilterByLines(const Zstring& filterPhrase) -{ - if (filterPhrase.empty()) - return std::vector(); - return split(filterPhrase, Zstr('\n')); -} - -Zstring mergeFilterLines(const std::vector& 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 tmp = splitFilterByLines(filter.includeFilter); //default value - in["Include"](tmp); - filter.includeFilter = mergeFilterLines(tmp); - - std::vector 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(altCmpCfg); - } - //########################################################### - //alternate sync configuration (optional) - if (XmlIn inAltSync = in["SyncConfig"]) - { - SyncConfig altSyncCfg; - readConfig(inAltSync, altSyncCfg); - - enhPair.altSyncConfig = std::make_shared(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 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 -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()), 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 -XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filename, int currentXmlFormatVer, std::unique_ptr& warning) //nothrow -{ - XmlCfg cfg; - XmlIn in(doc); - ::readConfig(in, cfg); - - if (in.errorsOccured()) - { - if (!warning) - warning = make_unique(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + - getErrorMessageFormatted(in.getErrorsAs()), 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& filenames, XmlGuiConfig& config) //throw FfsXmlError -{ - assert(!filenames.empty()); - - std::vector mainCfgs; - std::unique_ptr 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(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(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 -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(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 -#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> 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 columnAttribNavi; //compressed view/navigation - std::vector columnAttribLeft; - std::vector columnAttribRight; - - zen::ColumnTypeNavi naviLastSortColumn; //remember sort on navigation panel - bool naviLastSortAscending; // - - bool showPercentBar; //in navigation panel - - ExternalApps externelApplications; - - std::vector cfgFileHistory; - size_t cfgFileHistMax; - - std::vector lastUsedConfigFiles; - - std::vector folderHistoryLeft; - std::vector folderHistoryRight; - size_t folderHistMax; - - std::vector 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& 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 //not necessarily included by ! -#include -#include -#include -#include -#include -#include //wxGetEnv - -#ifdef ZEN_WIN -#include -#include -#include //includes "windows.h" -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "Mpr.lib") -#endif - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include //getenv() -#include //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 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 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 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\\Desktop - addCsidl(CSIDL_COMMON_DESKTOPDIRECTORY, L"csidl_PublicDesktop"); // C:\Users\All Users\Desktop - - addCsidl(CSIDL_FAVORITES, L"csidl_Favorites"); // C:\Users\\Favorites - addCsidl(CSIDL_COMMON_FAVORITES, L"csidl_PublicFavorites"); // C:\Users\\Favorites; unused? -> http://blogs.msdn.com/b/oldnewthing/archive/2012/09/04/10346022.aspx - - addCsidl(CSIDL_PERSONAL, L"csidl_MyDocuments"); // C:\Users\\Documents - addCsidl(CSIDL_COMMON_DOCUMENTS, L"csidl_PublicDocuments"); // C:\Users\Public\Documents - - addCsidl(CSIDL_MYMUSIC, L"csidl_MyMusic"); // C:\Users\\Music - addCsidl(CSIDL_COMMON_MUSIC, L"csidl_PublicMusic"); // C:\Users\Public\Music - - addCsidl(CSIDL_MYPICTURES, L"csidl_MyPictures"); // C:\Users\\Pictures - addCsidl(CSIDL_COMMON_PICTURES, L"csidl_PublicPictures"); // C:\Users\Public\Pictures - - addCsidl(CSIDL_MYVIDEO, L"csidl_MyVideos"); // C:\Users\\Videos - addCsidl(CSIDL_COMMON_VIDEO, L"csidl_PublicVideos"); // C:\Users\Public\Videos - - addCsidl(CSIDL_NETHOOD, L"csidl_Nethood"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Network Shortcuts - - addCsidl(CSIDL_PROGRAMS, L"csidl_Programs"); // C:\Users\\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\\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\\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\\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\\Downloads - addFolderId(FOLDERID_PublicDownloads, L"csidl_PublicDownloads"); // C:\Users\Public\Downloads - - addFolderId(FOLDERID_QuickLaunch, L"csidl_QuickLaunch"); // C:\Users\\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 getEnvironmentVar(const Zstring& envName) //return nullptr if not found -{ - wxString value; - if (!wxGetEnv(utfCvrtTo(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(utfCvrtTo(value)); -} - - -std::unique_ptr resolveMacro(const Zstring& macro, //macro without %-characters - const std::vector>& ext) //return nullptr if not resolved -{ - auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo(lhs).CmpNoCase(utfCvrtTo(rhs)) == 0; }; - - //there exist environment variables named %TIME%, %DATE% so check for our internal macros first! - if (equalNoCase(macro, Zstr("time"))) - return make_unique(formatTime(Zstr("%H%M%S"))); - - if (equalNoCase(macro, Zstr("date"))) - return make_unique(formatTime(FORMAT_ISO_DATE)); - - if (equalNoCase(macro, Zstr("timestamp"))) - return make_unique(formatTime(Zstr("%Y-%m-%d %H%M%S"))); //e.g. "2012-05-15 131513" - - std::unique_ptr cand; - auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool - { - if (!equalNoCase(macro, phrase)) - return false; - - cand = make_unique(formatTime(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& p) { return equalNoCase(macro, p.first); }); - if (it != ext.end()) - return make_unique(it->second); - } - - //try to resolve as environment variable - if (std::unique_ptr 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(it->second); - } -#endif - - return nullptr; -} - -const Zchar MACRO_SEP = Zstr('%'); - -//returns expanded or original string -Zstring expandMacros(const Zstring& text, const std::vector>& 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 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>()); } - - -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 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 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 - { - 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 volName(MAX_PATH + 1); //docu says so - - if (::GetVolumeInformation(appendSeparator(path).c_str(), //__in_opt LPCTSTR lpRootPathName, - &volName[0], //__out LPTSTR lpVolumeNameBuffer, - static_cast(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(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 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& 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\ -> %USERPROFILE% - { - std::map envToDir; - - //get list of useful variables - auto addEnvVar = [&](const Zstring& envName) - { - if (std::unique_ptr value = getEnvironmentVar(envName)) - envToDir.insert(std::make_pair(envName, *value)); - }; -#ifdef ZEN_WIN - addEnvVar(L"AllUsersProfile"); // C:\ProgramData - addEnvVar(L"AppData"); // C:\Users\\AppData\Roaming - addEnvVar(L"LocalAppData"); // C:\Users\\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\ - 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/ Mac: /Users/ -#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\ - { - Zstring testMacros = expandMacros(dirname); - if (testMacros != dirname) - if (output.insert(testMacros).second) - getDirectoryAliasesRecursive(testMacros, output); //recurse! - } -} - - -std::vector zen::getDirectoryAliases(const Zstring& dirString) -{ - Zstring dirname = dirString; - trim(dirname, true, false); - if (dirname.empty()) - return std::vector(); - - std::set tmp; - getDirectoryAliasesRecursive(dirname, tmp); - - tmp.erase(dirname); - tmp.erase(Zstring()); - - return std::vector(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 | | NO - ____________________________________________________________________________________________________________ - - Windows Login Prompt Naming Conventions: - user account: \ e.g. WIN-XP\ZenJu - network 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 remoteNameBuffer(bufferSize); - - //map local -> remote - - //note: following function call does NOT block! - DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form ":" - &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(driveLetter.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct! - trgRes.lpRemoteName = const_cast(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(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 -#include - -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 getDirectoryAliases(const Zstring& dirString); //may block for slow USB sticks when resolving [] - -#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 -#include //includes "windows.h" -#include -#include -#include -#include -#include -#include "ShadowCopy/shadow.h" -#include - -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 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 createShadowCopy; - const DllFun releaseShadowCopy; - const DllFun getShadowVolume; - const DllFun getLastError; - - Zstring shadowVolPf; - ShadowHandle backupHandle; -}; - -//############################################################################################################# - -Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function& 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\ is a junction that may link to e.g. D:\Users\, 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 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(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 -#include -#include -#include -#include - -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& onBeforeMakeVolumeCopy); //throw FileError - -private: - ShadowCopy(const ShadowCopy&); - ShadowCopy& operator=(const ShadowCopy&); - - class ShadowVolume; - typedef std::map, 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 -#include -#include "../structures.h" -//#include - -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 -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::min() && - sizeMin_ == 0U && - sizeMax_ == std::numeric_limits::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 -#include - -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 -#include -#include -#include - -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> StatNumbers; - - void updateData(StatNumbers& num, int objectsDelta, Int64 dataDelta) - { - auto& st = refNumbers(num, currentPhase_); - st.first += objectsDelta; - st.second += dataDelta; - } - - static const std::pair& 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& refNumbers(StatNumbers& num, Phase phaseId) { return const_cast&>(refNumbers(static_cast(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 -#include -#include "process_callback.h" - - -template inline -zen::Opt 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 -#include -#include -#include - -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(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 -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 -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& files, std::vector& 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& files_; - std::vector& 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 fileList; //list of *short* names - std::vector 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& files, std::function 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 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& files_; - std::function updateUI_; -}; -} - -void FileVersioner::limitVersions(std::function updateUI) //throw FileError -{ - if (versionCountLimit_ < 0) //no limit! - return; - - //buffer map "directory |-> list of immediate child file and symlink short names" - std::map, LessFilename> dirBuffer; - - auto getVersionsBuffered = [&](const Zstring& dirname) -> const std::vector& - { - auto it = dirBuffer.find(dirname); - if (it != dirBuffer.end()) - return it->second; - - std::vector 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& allVersions = getVersionsBuffered(parentDir); - - //filter out only those versions that match the given relative name - std::vector 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(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 -#include -#include -#include -#include -#include -#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: \\. YYYY-MM-DD HHMMSS. -/* - - 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(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 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 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 -#include - -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 = "(e.row + 1)), - L"%z", numberTo(e.col + 1))); - } -} - - -const std::wstring xmlAccess::getErrorMessageFormatted(const std::vector& 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 -#include -#include - -//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& failedElements); -} - -#endif // XMLBASE_H_INCLUDED -- cgit