diff options
Diffstat (limited to 'lib')
34 files changed, 660 insertions, 602 deletions
diff --git a/lib/ShadowCopy/Shadow_Server2003.vcxproj b/lib/ShadowCopy/Shadow_Server2003.vcxproj index 4622c246..520a4d6d 100644 --- a/lib/ShadowCopy/Shadow_Server2003.vcxproj +++ b/lib/ShadowCopy/Shadow_Server2003.vcxproj @@ -68,10 +68,8 @@ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> diff --git a/lib/ShadowCopy/Shadow_Windows7.vcxproj b/lib/ShadowCopy/Shadow_Windows7.vcxproj index c735371b..1fe769d0 100644 --- a/lib/ShadowCopy/Shadow_Windows7.vcxproj +++ b/lib/ShadowCopy/Shadow_Windows7.vcxproj @@ -68,10 +68,8 @@ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> diff --git a/lib/ShadowCopy/Shadow_XP.vcxproj b/lib/ShadowCopy/Shadow_XP.vcxproj index a5d07786..0d231f3d 100644 --- a/lib/ShadowCopy/Shadow_XP.vcxproj +++ b/lib/ShadowCopy/Shadow_XP.vcxproj @@ -67,10 +67,8 @@ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> diff --git a/lib/ShadowCopy/shadow.cpp b/lib/ShadowCopy/shadow.cpp index 4f6881ad..e915663f 100644 --- a/lib/ShadowCopy/shadow.cpp +++ b/lib/ShadowCopy/shadow.cpp @@ -75,7 +75,7 @@ std::wstring formatVssError(HRESULT hr) //at least the one's from IVssBackupComp } -shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError +shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw SysError { ComPtr<IVssBackupComponents> backupComp; { @@ -83,13 +83,13 @@ shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError if (FAILED(hr)) { if (hr == E_ACCESSDENIED) - throw ComError(L"The caller does not have sufficient backup privileges or is not an administrator.", hr); - throw ComError(L"Error calling \"CreateVssBackupComponents\".", hr); + 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 ComError - ZEN_COM_CHECK(backupComp->SetBackupState(false, false, VSS_BT_FULL)); //throw ComError + ZEN_COM_CHECK(backupComp->InitializeForBackup()); //throw SysError + ZEN_COM_CHECK(backupComp->SetBackupState(false, false, VSS_BT_FULL)); //throw SysError auto waitForComFuture = [](IVssAsync& fut) { @@ -98,7 +98,7 @@ shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError HRESULT hr = S_OK; ZEN_COM_CHECK(fut.QueryStatus(&hr, nullptr)); //check if the async operation succeeded... if (FAILED(hr)) - throw ComError(L"Error calling \"fut->QueryStatus\".", hr); + throw SysError(formatComError(L"Error calling \"fut->QueryStatus\".", hr)); }; ComPtr<IVssAsync> gatherAsync; @@ -117,12 +117,12 @@ shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError if (FAILED(hr)) { if (hr == VSS_E_VOLUME_NOT_SUPPORTED) - throw ComError(L"Volume Shadow Copy Service is not supported on this volume!"); + throw SysError(L"Volume Shadow Copy Service is not supported on this volume!"); const std::wstring vssError = formatVssError(hr); if (!vssError.empty()) - throw ComError(L"Error calling \"backupComp->AddToSnapshotSet\": " + vssError); + throw SysError(L"Error calling \"backupComp->AddToSnapshotSet\": " + vssError); else - throw ComError(L"Error calling \"backupComp->AddToSnapshotSet\".", hr); + throw SysError(formatComError(L"Error calling \"backupComp->AddToSnapshotSet\".", hr)); } } @@ -151,10 +151,10 @@ shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName) { try { - ShadowData result = ::createShadowCopy(volumeName); //throw ComError + ShadowData result = ::createShadowCopy(volumeName); //throw SysError return new ShadowData(result); //shadow handle owned by caller! std::bad_alloc? } - catch (const zen::ComError& e) + catch (const zen::SysError& e) { lastErrorMessage.reset(new std::wstring(e.toString())); return nullptr; diff --git a/lib/Thumbnail/Thumbnail.vcxproj b/lib/Thumbnail/Thumbnail.vcxproj index 2045f10e..87fb152b 100644 --- a/lib/Thumbnail/Thumbnail.vcxproj +++ b/lib/Thumbnail/Thumbnail.vcxproj @@ -66,10 +66,8 @@ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> @@ -87,7 +85,7 @@ </BuildLog> <ClCompile> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>FFS_WIN;FFS_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> @@ -122,7 +120,7 @@ </Midl> <ClCompile> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>FFS_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> @@ -155,7 +153,7 @@ <ClCompile> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>FFS_WIN;FFS_WIN;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> @@ -192,7 +190,7 @@ <ClCompile> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>FFS_WIN;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp index b00ce81d..53c30bc3 100644 --- a/lib/Thumbnail/thumbnail.cpp +++ b/lib/Thumbnail/thumbnail.cpp @@ -11,6 +11,7 @@ #define WIN32_LEAN_AND_MEAN #include <zen/win.h> #include <zen/win_ver.h> +#include <zen/sys_error.h> #define STRICT_TYPED_ITEMIDS //better type safety for IDLists #include <Shlobj.h> @@ -30,9 +31,9 @@ using namespace zen; namespace { -thumb::ImageData* allocImageData(int width, int height) //throw ComError; return value always bound! +thumb::ImageData* allocImageData(int width, int height) //throw SysError; return value always bound! { - ZEN_COM_ASSERT(width >= 0 && height >= 0); //throw ComError + ZEN_COM_ASSERT(width >= 0 && height >= 0); //throw SysError std::unique_ptr<thumb::ImageData> idata = make_unique<thumb::ImageData>(); @@ -55,7 +56,7 @@ void releaseImageData_impl(const thumb::ImageData* id) //caller takes ownership! -HICON createIconFromBitmap(HBITMAP bitmap) //throw ComError +HICON createIconFromBitmap(HBITMAP bitmap) //throw SysError { BITMAP bmpInfo = {}; ZEN_COM_ASSERT(::GetObject(bitmap, //__in HGDIOBJ hgdiobj, @@ -77,13 +78,13 @@ HICON createIconFromBitmap(HBITMAP bitmap) //throw ComError iconInfo.hbmMask = bitmapMask; HICON result = ::CreateIconIndirect(&iconInfo); - if (!result) throw ComError(L"Error calling \"CreateIconIndirect\".", GetLastError()); + if (!result) throw SysError(formatSystemError(L"CreateIconIndirect", getLastError())); return result; } //caller takes ownership! -thumb::ImageData* convertToImageData(HBITMAP bmp) //throw ComError +thumb::ImageData* convertToImageData(HBITMAP bmp) //throw SysError { //GetDIBits ???? @@ -145,13 +146,13 @@ thumb::ImageData* convertToImageData(HBITMAP bmp) //throw ComError 0, //_In_ int nXSrc, 0, //_In_ int nYSrc, SRCCOPY)) //_In_ DWORD dwRop - throw ComError(L"Error calling \"BitBlt\".", GetLastError()); + 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 ComError + thumb::ImageData* imgOut = allocImageData(bmpInfo.bmWidth, bmpInfo.bmHeight); //throw SysError ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); }); unsigned char* rgbPtr = imgOut->rgb; @@ -176,13 +177,13 @@ thumb::ImageData* convertToImageData(HBITMAP bmp) //throw ComError //caller takes ownership! -const thumb::ImageData* getThumbnail_impl(const wchar_t* filename, int requestedSize) //throw ComError +const thumb::ImageData* getThumbnail_impl(const wchar_t* filename, int requestedSize) //throw SysError { const std::wstring filenameStr(filename); ComPtr<IShellFolder> desktopFolder; - ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw ComError - ZEN_COM_ASSERT(desktopFolder); //throw ComError -> better safe than sorry? + ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError + ZEN_COM_ASSERT(desktopFolder); //throw SysError -> better safe than sorry? PIDLIST_RELATIVE pidlFolder = nullptr; { @@ -246,14 +247,14 @@ const thumb::ImageData* getThumbnail_impl(const wchar_t* filename, int requested ZEN_COM_ASSERT(bitmap); ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmap)); - return convertToImageData(bitmap); //throw ComError, pass ownership + 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 ComError +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 @@ -413,7 +414,7 @@ const thumb::ImageData* getIconByIndex_impl(int iconIndex, thumb::IconSizeType s /*bool rv = */ ::GdiFlush(); - ImageData* imgOut = allocImageData(targetWidth, targetHeight); //throw ComError + ImageData* imgOut = allocImageData(targetWidth, targetHeight); //throw SysError ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); }); unsigned char* rgbPtr = imgOut->rgb; @@ -457,9 +458,9 @@ const thumb::ImageData* thumb::getThumbnail(const wchar_t* filename, int request { try { - return getThumbnail_impl(filename, requestedSize); //throw ComError + return getThumbnail_impl(filename, requestedSize); //throw SysError } - catch (const ComError&) + catch (const SysError&) { return nullptr; } @@ -470,9 +471,9 @@ const thumb::ImageData* thumb::getIconByIndex(int iconIndex, thumb::IconSizeType { try { - return getIconByIndex_impl(iconIndex, st); //throw ComError + return getIconByIndex_impl(iconIndex, st); //throw SysError } - catch (const ComError&) + catch (const SysError&) { return nullptr; } diff --git a/lib/binary.cpp b/lib/binary.cpp index 4ef30c15..0e41f7a6 100644 --- a/lib/binary.cpp +++ b/lib/binary.cpp @@ -95,7 +95,7 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename const TickVal startTime = getTicks(); - const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError() + const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError const size_t length2 = file2.read(&memory2[0], bufferSize); //returns actual number of bytes read //send progress updates immediately after reading to reliably allow speed calculations for our clients! callback.updateCompareStatus(to<Int64>(std::max(length1, length2))); diff --git a/lib/db_file.cpp b/lib/db_file.cpp index e4e3d748..2f699e3a 100644 --- a/lib/db_file.cpp +++ b/lib/db_file.cpp @@ -12,7 +12,7 @@ #include <zen/serialize.h> #include <wx+/zlib_wrap.h> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <zen/win.h> //includes "windows.h" #include <zen/long_path_prefix.h> #endif @@ -25,6 +25,11 @@ namespace //------------------------------------------------------------------------------------------------------------------------------- const char FILE_FORMAT_DESCR[] = "FreeFileSync"; const int DB_FILE_FORMAT_VER = 9; + +warn_static("wee need two version ids!") +//const int DB_FILE_FORMAT_CONTAINER = 10; +//const int DB_FILE_FORMAT_STREAM = 1; + //------------------------------------------------------------------------------------------------------------------------------- typedef std::string UniqueId; @@ -34,22 +39,21 @@ typedef std::map<UniqueId, BinaryStream> StreamMapping; //list of streams ordere //| ensure 32/64 bit portability: use fixed size data types only e.g. std::uint32_t | //----------------------------------------------------------------------------------- - template <SelectedSide side> inline -Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) +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 FFS_WIN +#ifdef ZEN_WIN Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; -#elif defined FFS_LINUX || defined FFS_MAC +#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 baseMap.getBaseDirPf<side>() + dbname; + return baseDirObj.getBaseDirPf<side>() + dbname; } //####################################################################################################################################### @@ -76,7 +80,7 @@ void saveStreams(const StreamMapping& streamList, const Zstring& filename) //thr assert(!somethingExists(filename)); //orphan tmp files should be cleaned up already at this point! saveBinStream(filename, streamOut.get()); //throw FileError -#ifdef FFS_WIN +#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 @@ -125,8 +129,8 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError, FileErrorD } catch (const std::bad_alloc& e) //still required? { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename) + L"\n\n" + - _("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what())); + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename), + _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what())); } } @@ -167,7 +171,7 @@ public: } catch (ZlibInternalError&) { - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)) + L" (zlib error)"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)), L"zlib internal error"); } }; @@ -201,31 +205,31 @@ public: private: void recurse(const InSyncDir& container) { - // for (const auto& filePair : container.files) { processFile(filePair); }); ! + // for (const auto& dbFile : container.files) { processFile(dbFile); }); ! writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.files.size())); - std::for_each(container.files.begin(), container.files.end(), [&](const std::pair<Zstring, InSyncFile>& filePair) { this->process(filePair); }); + std::for_each(container.files.begin(), container.files.end(), [&](const std::pair<Zstring, InSyncFile>& dbFile) { this->process(dbFile); }); writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.symlinks.size())); - std::for_each(container.symlinks.begin(), container.symlinks.end(), [&](const std::pair<Zstring, InSyncSymlink>& symlinkPair) { this->process(symlinkPair); }); + std::for_each(container.symlinks.begin(), container.symlinks.end(), [&](const std::pair<Zstring, InSyncSymlink>& dbSymlink) { this->process(dbSymlink); }); writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.dirs.size())); - std::for_each(container.dirs.begin(), container.dirs.end(), [&](const std::pair<Zstring, InSyncDir>& dirPair) { this->process(dirPair); }); + std::for_each(container.dirs.begin(), container.dirs.end(), [&](const std::pair<Zstring, InSyncDir>& dbDir) { this->process(dbDir); }); } static void writeUtf8(BinStreamOut& output, const Zstring& str) { writeContainer(output, utfCvrtTo<Zbase<char>>(str)); } - static void write(BinStreamOut& output, const FileDescriptor& descr) + static void writeFile(BinStreamOut& output, const InSyncDescrFile& descr, const UInt64& fileSize) { writeNumber<std:: int64_t>(output, to<std:: int64_t>(descr.lastWriteTimeRaw)); - writeNumber<std::uint64_t>(output, to<std::uint64_t>(descr.fileSize)); - writeNumber<std::uint64_t>(output, descr.devId); - writeNumber<std::uint64_t>(output, descr.fileIdx); - assert_static(sizeof(descr.devId ) <= sizeof(std::uint64_t)); - assert_static(sizeof(descr.fileIdx) <= sizeof(std::uint64_t)); + writeNumber<std::uint64_t>(output, to<std::uint64_t>(fileSize)); + writeNumber<std::uint64_t>(output, descr.fileId.first); + writeNumber<std::uint64_t>(output, descr.fileId.second); + assert_static(sizeof(descr.fileId.first ) <= sizeof(std::uint64_t)); + assert_static(sizeof(descr.fileId.second) <= sizeof(std::uint64_t)); } - static void write(BinStreamOut& output, const LinkDescriptor& descr) + static void writeLink(BinStreamOut& output, const InSyncDescrLink& descr) { writeNumber<std::int64_t>(output, to<std:: int64_t>(descr.lastWriteTimeRaw)); @@ -236,37 +240,39 @@ private: writeNumber<std::int32_t>(output, 0); } - static void write(BinStreamOut& output, const InSyncDir::InSyncStatus& status) + static void writeDir(BinStreamOut& output, const InSyncDir::InSyncStatus& status) { writeNumber<std::int32_t>(output, status); } - void process(const std::pair<Zstring, InSyncFile>& filePair) + void process(const std::pair<Zstring, InSyncFile>& dbFile) { - writeUtf8(outputBoth, filePair.first); - writeNumber<std::int32_t>(outputBoth, filePair.second.inSyncType); + writeUtf8(outputBoth, dbFile.first); + writeNumber<std::int32_t>(outputBoth, dbFile.second.inSyncType); - write(outputLeft, filePair.second.left); - write(outputRight, filePair.second.right); + warn_static("implement proper migration: get rid of duplicate fileSize!") + + writeFile(outputLeft, dbFile.second.left, dbFile.second.fileSize); + writeFile(outputRight, dbFile.second.right, dbFile.second.fileSize); } - void process(const std::pair<Zstring, InSyncSymlink>& symlinkPair) + void process(const std::pair<Zstring, InSyncSymlink>& dbSymlink) { - writeUtf8(outputBoth, symlinkPair.first); + writeUtf8(outputBoth, dbSymlink.first); warn_static("new parameter: imp proper migration!") - //writeNumber<std::int32_t>(outputBoth, symlinkPair.second.inSyncType); + //writeNumber<std::int32_t>(outputBoth, dbSymlink.second.inSyncType); - write(outputLeft, symlinkPair.second.left); - write(outputRight, symlinkPair.second.right); + writeLink(outputLeft, dbSymlink.second.left); + writeLink(outputRight, dbSymlink.second.right); } - void process(const std::pair<Zstring, InSyncDir>& dirPair) + void process(const std::pair<Zstring, InSyncDir>& dbDir) { - writeUtf8(outputBoth, dirPair.first); - write(outputBoth, dirPair.second.status); + writeUtf8(outputBoth, dbDir.first); + writeDir(outputBoth, dbDir.second.status); - recurse(dirPair.second); + recurse(dbDir.second); } BinStreamOut outputLeft; //data related to one side only @@ -291,7 +297,7 @@ public: } catch (ZlibInternalError&) { - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)) + L" (zlib error)"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)), L"zlib internal error"); } }; @@ -319,11 +325,11 @@ public: const BinaryStream tmpL = readContainer<BinaryStream>(inL); const BinaryStream tmpR = readContainer<BinaryStream>(inR); - auto output = std::make_shared<InSyncDir>(InSyncDir::STATUS_IN_SYNC); - StreamParser(decompStream(tmpL, filenameL), - decompStream(tmpR, filenameR), - decompStream(tmpB, filenameL + Zstr("/") + filenameR), - *output); //throw UnexpectedEndOfStreamError + auto output = std::make_shared<InSyncDir>(InSyncDir::DIR_STATUS_IN_SYNC); + StreamParser parser(decompStream(tmpL, filenameL), + decompStream(tmpR, filenameR), + decompStream(tmpB, filenameL + Zstr("/") + filenameR)); + parser.recurse(*output); //throw UnexpectedEndOfStreamError return output; } catch (const UnexpectedEndOfStreamError&) @@ -332,43 +338,46 @@ public: } catch (const std::bad_alloc& e) //still required? { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR) + L"\n\n" + - _("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what())); + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR), + _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what())); } } private: StreamParser(const BinaryStream& bufferL, const BinaryStream& bufferR, - const BinaryStream& bufferB, - InSyncDir& container) : + const BinaryStream& bufferB) : inputLeft (bufferL), inputRight(bufferR), - inputBoth (bufferB) { recurse(container); } + inputBoth (bufferB) {} static Zstring readUtf8(BinStreamIn& input) { return utfCvrtTo<Zstring>(readContainer<Zbase<char>>(input)); } //throw UnexpectedEndOfStreamError - static void read(BinStreamIn& input, FileDescriptor& descr) + static InSyncDescrFile readFile(BinStreamIn& input, UInt64& fileSize) { //attention: order of function argument evaluation is undefined! So do it one after the other... - descr.lastWriteTimeRaw = readNumber<std::int64_t>(input); //throw UnexpectedEndOfStreamError - descr.fileSize = readNumber<std::uint64_t>(input); - descr.devId = static_cast<DeviceId >(readNumber<std::uint64_t>(input)); // - descr.fileIdx = static_cast<FileIndex>(readNumber<std::uint64_t>(input)); //silence "loss of precision" compiler warnings + auto lastWriteTimeRaw = readNumber<std::int64_t>(input); //throw UnexpectedEndOfStreamError + warn_static("implement proper migration!") + fileSize = readNumber<std::uint64_t>(input); + auto devId = static_cast<DeviceId >(readNumber<std::uint64_t>(input)); // + auto fileIdx = static_cast<FileIndex>(readNumber<std::uint64_t>(input)); //silence "loss of precision" compiler warnings + return InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx)); } - static void read(BinStreamIn& input, LinkDescriptor& descr) + static InSyncDescrLink readLink(BinStreamIn& input) { - descr.lastWriteTimeRaw = readNumber<std::int64_t>(input); + auto lastWriteTimeRaw = readNumber<std::int64_t>(input); warn_static("implement proper migration!") //descr.targetPath = readUtf8(input); readUtf8(input); //descr.type = static_cast<LinkDescriptor::LinkType>(readNumber<std::int32_t>(input)); readNumber<std::int32_t>(input); + + return InSyncDescrLink(lastWriteTimeRaw); } - static void read(BinStreamIn& input, InSyncDir::InSyncStatus& status) + static void readDir(BinStreamIn& input, InSyncDir::InSyncStatus& status) { status = static_cast<InSyncDir::InSyncStatus>(readNumber<std::int32_t>(input)); } @@ -378,15 +387,16 @@ private: size_t fileCount = readNumber<std::uint32_t>(inputBoth); while (fileCount-- != 0) { + warn_static("migrate from InSyncType to CompareVariant!!!") const Zstring shortName = readUtf8(inputBoth); const auto inSyncType = static_cast<InSyncType>(readNumber<std::int32_t>(inputBoth)); - FileDescriptor dataL; - FileDescriptor dataR; - read(inputLeft, dataL); - read(inputRight, dataR); + warn_static("implement proper migration: get rid of duplicate fileSize!") - container.addFile(shortName, dataL, dataR, inSyncType); + UInt64 fileSize; + const InSyncDescrFile dataL = readFile(inputLeft, fileSize); + const InSyncDescrFile dataR = readFile(inputRight, fileSize); + container.addFile(shortName, dataL, dataR, inSyncType, fileSize); } size_t linkCount = readNumber<std::uint32_t>(inputBoth); @@ -395,14 +405,11 @@ private: const Zstring shortName = readUtf8(inputBoth); warn_static("new parameter: imp proper migration!") - const auto inSyncType = IN_SYNC_BINARY_EQUAL; + const auto inSyncType = IN_SYNC_BINARY_EQUAL; //const auto inSyncType = static_cast<InSyncType>(readNumber<std::int32_t>(inputBoth)); - LinkDescriptor dataL; - LinkDescriptor dataR; - read(inputLeft, dataL); - read(inputRight, dataR); - + InSyncDescrLink dataL = readLink(inputLeft); + InSyncDescrLink dataR = readLink(inputRight); container.addSymlink(shortName, dataL, dataR, inSyncType); } @@ -411,8 +418,8 @@ private: { const Zstring shortName = readUtf8(inputBoth); - InSyncDir::InSyncStatus status = InSyncDir::STATUS_STRAW_MAN; - read(inputBoth, status); + InSyncDir::InSyncStatus status = InSyncDir::DIR_STATUS_STRAW_MAN; + readDir(inputBoth, status); InSyncDir& subDir = container.addDir(shortName, status); recurse(subDir); @@ -435,10 +442,10 @@ class UpdateLastSynchronousState => update all database entries! */ public: - static void execute(const BaseDirMapping& baseMapping, InSyncDir& dir) + static void execute(const BaseDirPair& baseDirObj, InSyncDir& dir) { bool binaryComparison = false; - switch (baseMapping.getCompVariant()) + switch (baseDirObj.getCompVariant()) { case CMP_BY_TIME_SIZE: break; @@ -447,8 +454,8 @@ public: break; } - UpdateLastSynchronousState updater(baseMapping.getFilter(), binaryComparison); - updater.recurse(baseMapping, dir); + UpdateLastSynchronousState updater(baseDirObj.getFilter(), binaryComparison); + updater.recurse(baseDirObj, dir); } private: @@ -469,7 +476,7 @@ private: auto rv = map.insert(typename M::value_type(key, value)); if (!rv.second) { -#if defined FFS_WIN || defined FFS_MAC //caveat: key must be updated, if there is a change in short name case!!! +#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); @@ -490,7 +497,7 @@ private: auto it = map.lower_bound(key); if (it != map.end() && !(map.key_comp()(key, it->first))) { - #if defined FFS_WIN || defined FFS_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! + #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 @@ -507,38 +514,40 @@ private: void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelativeNamePf, InSyncDir::FileList& dbFiles) { hash_set<const InSyncFile*> toPreserve; //referencing fixed-in-memory std::map elements - std::for_each(currentFiles.begin(), currentFiles.end(), [&](const FileMapping& fileMap) + std::for_each(currentFiles.begin(), currentFiles.end(), [&](const FilePair& fileObj) { - if (!fileMap.isEmpty()) + if (!fileObj.isEmpty()) { - if (fileMap.getCategory() == FILE_EQUAL) //data in sync: write current state + 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(fileMap.getShortName<LEFT_SIDE>() == fileMap.getShortName<RIGHT_SIDE>()); + assert(fileObj.getShortName<LEFT_SIDE>() == fileObj.getShortName<RIGHT_SIDE>()); + //this should be taken for granted: + assert(fileObj.getFileSize<LEFT_SIDE>() == fileObj.getFileSize<RIGHT_SIDE>()); //create or update new "in-sync" state - InSyncFile& file = updateItem(dbFiles, fileMap.getObjShortName(), - InSyncFile(FileDescriptor(fileMap.getLastWriteTime<LEFT_SIDE>(), - fileMap.getFileSize <LEFT_SIDE>(), - fileMap.getFileId <LEFT_SIDE>()), - FileDescriptor(fileMap.getLastWriteTime<RIGHT_SIDE>(), - fileMap.getFileSize <RIGHT_SIDE>(), - fileMap.getFileId <RIGHT_SIDE>()), + InSyncFile& file = updateItem(dbFiles, fileObj.getObjShortName(), + InSyncFile(InSyncDescrFile(fileObj.getLastWriteTime<LEFT_SIDE>(), + fileObj.getFileId <LEFT_SIDE>()), + InSyncDescrFile(fileObj.getLastWriteTime<RIGHT_SIDE>(), + fileObj.getFileId <RIGHT_SIDE>()), binaryComparison_ ? IN_SYNC_BINARY_EQUAL : - IN_SYNC_ATTRIBUTES_EQUAL)); + IN_SYNC_ATTRIBUTES_EQUAL, + fileObj.getFileSize<LEFT_SIDE>())); toPreserve.insert(&file); } else //not in sync: preserve last synchronous state { - auto it = dbFiles.find(fileMap.getObjShortName()); + 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 { @@ -553,18 +562,18 @@ private: void process(const HierarchyObject::SubLinkVec& currentLinks, const Zstring& parentRelativeNamePf, InSyncDir::LinkList& dbLinks) { hash_set<const InSyncSymlink*> toPreserve; - std::for_each(currentLinks.begin(), currentLinks.end(), [&](const SymLinkMapping& linkMap) + std::for_each(currentLinks.begin(), currentLinks.end(), [&](const SymlinkPair& linkObj) { - if (!linkMap.isEmpty()) + if (!linkObj.isEmpty()) { - if (linkMap.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state + if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state { - assert(linkMap.getShortName<LEFT_SIDE>() == linkMap.getShortName<RIGHT_SIDE>()); + assert(linkObj.getShortName<LEFT_SIDE>() == linkObj.getShortName<RIGHT_SIDE>()); //create or update new "in-sync" state - InSyncSymlink& link = updateItem(dbLinks, linkMap.getObjShortName(), - InSyncSymlink(LinkDescriptor(linkMap.getLastWriteTime<LEFT_SIDE>()), - LinkDescriptor(linkMap.getLastWriteTime<RIGHT_SIDE>()), + InSyncSymlink& link = updateItem(dbLinks, linkObj.getObjShortName(), + InSyncSymlink(InSyncDescrLink(linkObj.getLastWriteTime<LEFT_SIDE>()), + InSyncDescrLink(linkObj.getLastWriteTime<RIGHT_SIDE>()), binaryComparison_ ? IN_SYNC_BINARY_EQUAL : IN_SYNC_ATTRIBUTES_EQUAL)); @@ -572,7 +581,7 @@ private: } else //not in sync: preserve last synchronous state { - auto it = dbLinks.find(linkMap.getObjShortName()); + auto it = dbLinks.find(linkObj.getObjShortName()); if (it != dbLinks.end()) toPreserve.insert(&it->second); } @@ -593,22 +602,21 @@ private: void process(const HierarchyObject::SubDirVec& currentDirs, const Zstring& parentRelativeNamePf, InSyncDir::DirList& dbDirs) { hash_set<const InSyncDir*> toPreserve; - std::for_each(currentDirs.begin(), currentDirs.end(), [&](const DirMapping& dirMap) + std::for_each(currentDirs.begin(), currentDirs.end(), [&](const DirPair& dirObj) { - if (!dirMap.isEmpty()) - { - switch (dirMap.getDirCategory()) + if (!dirObj.isEmpty()) + switch (dirObj.getDirCategory()) { case DIR_EQUAL: { - assert(dirMap.getShortName<LEFT_SIDE>() == dirMap.getShortName<RIGHT_SIDE>()); + assert(dirObj.getShortName<LEFT_SIDE>() == dirObj.getShortName<RIGHT_SIDE>()); //update directory entry only (shallow), but do *not touch* exising child elements!!! - const Zstring& key = dirMap.getObjShortName(); - auto insertResult = dbDirs.insert(std::make_pair(key, InSyncDir(InSyncDir::STATUS_IN_SYNC))); //get or create + 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 FFS_WIN || defined FFS_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! +#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) { @@ -618,9 +626,9 @@ private: } #endif InSyncDir& dir = it->second; - dir.status = InSyncDir::STATUS_IN_SYNC; //update immediate directory entry + dir.status = InSyncDir::DIR_STATUS_IN_SYNC; //update immediate directory entry toPreserve.insert(&dir); - recurse(dirMap, dir); + recurse(dirObj, dir); } break; @@ -630,9 +638,9 @@ private: //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(dirMap.getObjShortName(), InSyncDir(InSyncDir::STATUS_STRAW_MAN))).first->second; + InSyncDir& dir = dbDirs.insert(std::make_pair(dirObj.getObjShortName(), InSyncDir(InSyncDir::DIR_STATUS_STRAW_MAN))).first->second; toPreserve.insert(&dir); - recurse(dirMap, dir); + recurse(dirObj, dir); } break; @@ -640,16 +648,15 @@ private: case DIR_LEFT_SIDE_ONLY: case DIR_RIGHT_SIDE_ONLY: { - auto it = dbDirs.find(dirMap.getObjShortName()); + auto it = dbDirs.find(dirObj.getObjShortName()); if (it != dbDirs.end()) { toPreserve.insert(&it->second); - recurse(dirMap, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! + 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 @@ -657,13 +664,12 @@ private: { if (toPreserve.find(&v.second) != toPreserve.end()) return false; - //all items not existing in "currentDirs" have either been deleted meanwhile or been excluded via filter: 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 - //-> we can't tell and need to preserve the old db entry -> all child db elements are 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! + //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! }); } @@ -674,17 +680,17 @@ private: //####################################################################################################################################### -std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirMapping& baseMapping) //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! +std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! { - const Zstring fileNameLeft = getDBFilename<LEFT_SIDE >(baseMapping); - const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseMapping); + const Zstring fileNameLeft = getDBFilename<LEFT_SIDE >(baseDirObj); + const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseDirObj); - if (!baseMapping.isExisting<LEFT_SIDE >() || - !baseMapping.isExisting<RIGHT_SIDE>()) + if (!baseDirObj.isExisting<LEFT_SIDE >() || + !baseDirObj.isExisting<RIGHT_SIDE>()) { //avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts! //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 - const Zstring filename = !baseMapping.isExisting<LEFT_SIDE>() ? fileNameLeft : fileNameRight; + const Zstring filename = !baseDirObj.isExisting<LEFT_SIDE>() ? fileNameLeft : fileNameRight; throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + //it could be due to a to-be-created target directory not yet existing => FileErrorDatabaseNotExisting replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename))); } @@ -710,14 +716,14 @@ std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirMapping& b } -void zen::saveLastSynchronousState(const BaseDirMapping& baseMapping) //throw FileError +void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError { //transactional behaviour! write to tmp files first - const Zstring dbNameLeftTmp = getDBFilename<LEFT_SIDE >(baseMapping, true); - const Zstring dbNameRightTmp = getDBFilename<RIGHT_SIDE>(baseMapping, true); + const Zstring dbNameLeftTmp = getDBFilename<LEFT_SIDE >(baseDirObj, true); + const Zstring dbNameRightTmp = getDBFilename<RIGHT_SIDE>(baseDirObj, true); - const Zstring dbNameLeft = getDBFilename<LEFT_SIDE >(baseMapping); - const Zstring dbNameRight = getDBFilename<RIGHT_SIDE>(baseMapping); + const Zstring dbNameLeft = getDBFilename<LEFT_SIDE >(baseDirObj); + const Zstring dbNameRight = getDBFilename<RIGHT_SIDE>(baseDirObj); //delete old tmp file, if necessary -> throws if deletion fails! removeFile(dbNameLeftTmp); // @@ -749,7 +755,7 @@ void zen::saveLastSynchronousState(const BaseDirMapping& baseMapping) //throw Fi } //load last synchrounous state - std::shared_ptr<InSyncDir> lastSyncState = std::make_shared<InSyncDir>(InSyncDir::STATUS_IN_SYNC); + std::shared_ptr<InSyncDir> lastSyncState = std::make_shared<InSyncDir>(InSyncDir::DIR_STATUS_IN_SYNC); if (streamIterLeftOld != streamListLeft .end() && streamIterRightOld != streamListRight.end()) try @@ -762,7 +768,7 @@ void zen::saveLastSynchronousState(const BaseDirMapping& baseMapping) //throw Fi catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! //update last synchrounous state - UpdateLastSynchronousState::execute(baseMapping, *lastSyncState); + UpdateLastSynchronousState::execute(baseDirObj, *lastSyncState); //serialize again BinaryStream updatedStreamLeft; diff --git a/lib/db_file.h b/lib/db_file.h index 181a433e..b352ba5d 100644 --- a/lib/db_file.h +++ b/lib/db_file.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DBFILE_H_INCLUDED -#define DBFILE_H_INCLUDED +#ifndef DBFILE_H_834275398588021574 +#define DBFILE_H_834275398588021574 #include <zen/file_error.h> #include "../file_hierarchy.h" @@ -20,31 +20,50 @@ enum InSyncType IN_SYNC_ATTRIBUTES_EQUAL, //only "looks" like they're equal }; +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 FileDescriptor& l, const FileDescriptor& r, InSyncType type) : left(l), right(r), inSyncType(type) {} - FileDescriptor left; - FileDescriptor right; + InSyncFile(const InSyncDescrFile& l, const InSyncDescrFile& r, InSyncType type, const UInt64& fileSizeIn) : left(l), right(r), inSyncType(type), fileSize(fileSizeIn) {} + InSyncDescrFile left; + InSyncDescrFile right; InSyncType inSyncType; + UInt64 fileSize; //file size must be identical on both sides! }; struct InSyncSymlink { - InSyncSymlink(const LinkDescriptor& l, const LinkDescriptor& r, InSyncType type) : left(l), right(r), inSyncType(type) {} - LinkDescriptor left; - LinkDescriptor right; + InSyncSymlink(const InSyncDescrLink& l, const InSyncDescrLink& r, InSyncType type) : left(l), right(r), inSyncType(type) {} + InSyncDescrLink left; + InSyncDescrLink right; InSyncType inSyncType; }; 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! + //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 { - STATUS_IN_SYNC, - STATUS_STRAW_MAN //there is no last synchronous state, but used as container only + DIR_STATUS_IN_SYNC, + DIR_STATUS_STRAW_MAN //there is no last synchronous state, but used as container only }; InSyncDir(InSyncStatus statusIn) : status(statusIn) {} @@ -61,18 +80,18 @@ struct InSyncDir LinkList symlinks; //non-followed symlinks //convenience - InSyncDir& addDir(const Zstring& shortName, InSyncStatus statusIn) + InSyncDir& addDir(const Zstring& shortName, InSyncStatus st) { //use C++11 emplace when available - return dirs.insert(std::make_pair(shortName, InSyncDir(statusIn))).first->second; + return dirs.insert(std::make_pair(shortName, InSyncDir(st))).first->second; } - void addFile(const Zstring& shortName, const FileDescriptor& dataL, const FileDescriptor& dataR, InSyncType type) + void addFile(const Zstring& shortName, const InSyncDescrFile& dataL, const InSyncDescrFile& dataR, InSyncType type, const UInt64& fileSize) { - files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, type))); + files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, type, fileSize))); } - void addSymlink(const Zstring& shortName, const LinkDescriptor& dataL, const LinkDescriptor& dataR, InSyncType type) + void addSymlink(const Zstring& shortName, const InSyncDescrLink& dataL, const InSyncDescrLink& dataR, InSyncType type) { symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR, type))); } @@ -81,9 +100,9 @@ struct InSyncDir DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); -std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirMapping& baseMapping); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! +std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! -void saveLastSynchronousState(const BaseDirMapping& baseMapping); //throw FileError +void saveLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError } -#endif //DBFILE_H_INCLUDED +#endif //DBFILE_H_834275398588021574 diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h index b96dc7e1..dd77a36a 100644 --- a/lib/dir_exist_async.h +++ b/lib/dir_exist_async.h @@ -34,7 +34,7 @@ std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::set<Zstring, { if (dirname.empty()) return false; -#ifdef FFS_WIN +#ifdef ZEN_WIN //1. login to network share, if necessary loginNetworkShare(dirname, allowUserInteraction); #endif diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp index f3a16677..60d83a67 100644 --- a/lib/dir_lock.cpp +++ b/lib/dir_lock.cpp @@ -7,7 +7,7 @@ #include <utility> #include <wx/log.h> #include <memory> -#include <zen/last_error.h> +#include <zen/sys_error.h> #include <zen/thread.h> //includes <boost/thread.hpp> #include <zen/scope_guard.h> #include <zen/guid.h> @@ -18,14 +18,14 @@ #include <zen/serialize.h> #include <zen/optional.h> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <tlhelp32.h> #include <zen/win.h> //includes "windows.h" #include <zen/long_path_prefix.h> #include <Sddl.h> //login sid #include <Lmcons.h> //UNLEN -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <fcntl.h> //open() #include <sys/stat.h> // #include <unistd.h> //getsid() @@ -68,14 +68,14 @@ public: } catch (const std::exception& e) //exceptions must be catched per thread { - wxSafeShowMessage(_("An exception occurred!") + L" (Dirlock)", utfCvrtTo<wxString>(e.what())); //simple wxMessageBox won't do for threads + wxSafeShowMessage(_("An exception occurred"), utfCvrtTo<wxString>(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads } } void emitLifeSign() const //try to append one byte...; throw() { const char buffer[1] = {' '}; -#ifdef FFS_WIN +#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!!! @@ -105,7 +105,7 @@ public: nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped return; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); if (fileHandle == -1) return; @@ -125,7 +125,7 @@ namespace { UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExisting { -#ifdef FFS_WIN +#ifdef ZEN_WIN WIN32_FIND_DATA fileInfo = {}; const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); if (searchHandle != INVALID_HANDLE_VALUE) @@ -133,20 +133,22 @@ UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExist ::FindClose(searchHandle); return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); } - -#elif defined FFS_LINUX || defined FFS_MAC + 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 errorMessage = replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError); + const std::wstring errorMsg = replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)); + const std::wstring errorDescr = formatSystemError(functionName, lastError); if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(errorMessage); + throw ErrorNotExisting(errorMsg, errorDescr); else - throw FileError(errorMessage); + throw FileError(errorMsg, errorDescr); } @@ -160,14 +162,14 @@ Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT } -#ifdef FFS_WIN +#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.") + L" (OpenProcessToken)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"OpenProcessToken", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); DWORD bufferSize = 0; @@ -179,7 +181,7 @@ Zstring getLoginSid() //throw FileError &buffer[0], //__out_opt LPVOID TokenInformation, bufferSize, //__in DWORD TokenInformationLength, &bufferSize)) //__out PDWORD ReturnLength - throw FileError(_("Cannot get process information.") + L" (GetTokenInformation)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"GetTokenInformation", getLastError())); auto groups = reinterpret_cast<const TOKEN_GROUPS*>(&buffer[0]); @@ -189,20 +191,19 @@ Zstring getLoginSid() //throw FileError LPTSTR sidStr = nullptr; if (!::ConvertSidToStringSid(groups->Groups[i].Sid, //__in PSID Sid, &sidStr)) //__out LPTSTR *StringSid - throw FileError(_("Cannot get process information.") + L" (ConvertSidToStringSid)" + L"\n\n" + getLastErrorFormatted()); + 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)" + L"\n\n" + getLastErrorFormatted()); //shouldn't happen + throw FileError(_("Cannot get process information."), L"no login found"); //shouldn't happen } #endif -#ifdef FFS_WIN +#ifdef ZEN_WIN typedef DWORD ProcessId; typedef DWORD SessionId; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC typedef pid_t ProcessId; typedef pid_t SessionId; #endif @@ -210,12 +211,12 @@ typedef pid_t SessionId; //return ppid on Windows, sid on Linux/Mac, "no value" if process corresponding to "processId" is not existing Opt<SessionId> getSessionId(ProcessId processId) //throw FileError { -#ifdef FFS_WIN +#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.") + L" (CreateToolhelp32Snapshot)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"CreateToolhelp32Snapshot", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot)); PROCESSENTRY32 processEntry = {}; @@ -223,7 +224,7 @@ Opt<SessionId> getSessionId(ProcessId processId) //throw FileError if (!::Process32First(snapshot, //__in HANDLE hSnapshot, &processEntry)) //__inout LPPROCESSENTRY32 lppe - throw FileError(_("Cannot get process information.") + L" (Process32First)" + L"\n\n" + getLastErrorFormatted()); //ERROR_NO_MORE_FILES not possible + 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 @@ -231,17 +232,17 @@ Opt<SessionId> getSessionId(ProcessId processId) //throw FileError } while (::Process32Next(snapshot, &processEntry)); if (::GetLastError() != ERROR_NO_MORE_FILES) //yes, they call it "files" - throw FileError(_("Cannot get process information.") + L" (Process32Next)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"Process32Next", getLastError())); return NoValue(); -#elif defined FFS_LINUX || defined FFS_MAC +#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.") + L" (getsid)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"getsid", getLastError())); return procSid; #endif @@ -255,7 +256,7 @@ struct LockInformation //throw FileError explicit LockInformation(FromCurrentProcess) : lockId(zen::generateGUID()), sessionId(), //dummy value -#ifdef FFS_WIN +#ifdef ZEN_WIN processId(::GetCurrentProcessId()) //never fails { DWORD bufferSize = 0; @@ -265,28 +266,29 @@ struct LockInformation //throw FileError if (!::GetComputerNameEx(ComputerNameDnsFullyQualified, //__in COMPUTER_NAME_FORMAT NameType, &buffer[0], //__out LPTSTR lpBuffer, &bufferSize)) //__inout LPDWORD lpnSize - throw FileError(_("Cannot get process information.") + L" (GetComputerNameEx)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"GetComputerNameEx", getLastError())); + computerName = "Windows." + utfCvrtTo<std::string>(&buffer[0]); bufferSize = UNLEN + 1; buffer.resize(bufferSize); if (!::GetUserName(&buffer[0], //__out LPTSTR lpBuffer, &bufferSize)) //__inout LPDWORD lpnSize - throw FileError(_("Cannot get process information.") + L" (GetUserName)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"GetUserName", getLastError())); userId = utfCvrtTo<std::string>(&buffer[0]); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC processId(::getpid()) //never fails { std::vector<char> buffer(10000); if (::gethostname(&buffer[0], buffer.size()) != 0) - throw FileError(_("Cannot get process information.") + L" (gethostname)" + L"\n\n" + getLastErrorFormatted()); + 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.") + L" (getdomainname)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"getdomainname", getLastError())); computerName += "."; computerName += &buffer[0]; @@ -298,15 +300,15 @@ struct LockInformation //throw FileError 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.") + L" (getpwuid_r)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), formatSystemError(L"getpwuid_r", getLastError())); if (!pwsEntry) - throw FileError(_("Cannot get process information.") + L" (no login found)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), L"no login found"); //should not happen? userId += pwsEntry->pw_name; #endif Opt<SessionId> sessionIdTmp = getSessionId(processId); //throw FileError if (!sessionIdTmp) - throw FileError(_("Cannot get process information.") + L" (getSessionId)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot get process information."), L"no session id found"); //should not happen? sessionId = *sessionIdTmp; } @@ -357,14 +359,6 @@ struct LockInformation //throw FileError //wxGetFullHostName() is a performance killer for some users, so don't touch! -void writeLockInfo(const Zstring& lockfilename) //throw FileError -{ - BinStreamOut streamOut; - LockInformation(FromCurrentProcess()).toStream(streamOut); - saveBinStream(lockfilename, streamOut.get()); //throw FileError -} - - LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, ErrorNotExisting { BinStreamIn streamIn = loadBinStream<BinaryStream>(lockfilename); //throw FileError, ErrorNotExisting @@ -374,7 +368,7 @@ LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, } catch (UnexpectedEndOfStreamError&) { - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(lockfilename)) + L" (unexpected end of stream)"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(lockfilename)), L"unexpected end of stream"); } } @@ -519,7 +513,7 @@ void releaseLock(const Zstring& lockfilename) //throw () bool tryLock(const Zstring& lockfilename) //throw FileError { -#ifdef FFS_WIN +#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, @@ -534,7 +528,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError 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)) + L"\n\n" + zen::getLastErrorFormatted()); + 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 @@ -542,7 +536,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError //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 FFS_LINUX || defined FFS_MAC +#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); @@ -551,7 +545,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError if (errno == EEXIST) return false; else - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + zen::getLastErrorFormatted()); + 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 @@ -675,7 +669,7 @@ DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw if (callback) callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtFileName(lockfilename))); -#ifdef FFS_WIN +#ifdef ZEN_WIN const DWORD bufferSize = 10000; std::vector<wchar_t> volName(bufferSize); if (::GetVolumePathName(lockfilename.c_str(), //__in LPCTSTR lpszFileName, diff --git a/lib/ffs_paths.cpp b/lib/ffs_paths.cpp index 5ee4a3eb..0f01b8d1 100644 --- a/lib/ffs_paths.cpp +++ b/lib/ffs_paths.cpp @@ -9,7 +9,7 @@ #include <wx/stdpaths.h> #include <wx+/string_conv.h> -#ifdef FFS_MAC +#ifdef ZEN_MAC #include <vector> #include <zen/scope_guard.h> #include <zen/osx_string.h> @@ -22,7 +22,7 @@ using namespace zen; namespace { -#if defined FFS_WIN || defined FFS_LINUX +#if defined ZEN_WIN || defined ZEN_LINUX inline Zstring getExecutableDir() //directory containing executable WITH path separator at end { @@ -30,7 +30,7 @@ Zstring getExecutableDir() //directory containing executable WITH path separator } #endif -#ifdef FFS_WIN +#ifdef ZEN_WIN inline Zstring getInstallDir() //root install directory WITH path separator at end { @@ -39,10 +39,10 @@ Zstring getInstallDir() //root install directory WITH path separator at end #endif -#ifdef FFS_WIN +#ifdef ZEN_WIN inline bool isPortableVersion() { return !fileExists(getInstallDir() + L"uninstall.exe"); } //this check is a bit lame... -#elif defined FFS_LINUX +#elif defined ZEN_LINUX inline bool isPortableVersion() { return !endsWith(getExecutableDir(), "/bin/"); } //this check is a bit lame... #endif @@ -51,9 +51,9 @@ bool isPortableVersion() { return !endsWith(getExecutableDir(), "/bin/"); } //th bool zen::manualProgramUpdateRequired() { -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC return true; -#elif defined FFS_LINUX +#elif defined ZEN_LINUX return isPortableVersion(); //locally installed version is updated by system #endif } @@ -61,14 +61,14 @@ bool zen::manualProgramUpdateRequired() Zstring zen::getResourceDir() { -#ifdef FFS_WIN +#ifdef ZEN_WIN return getInstallDir(); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX if (isPortableVersion()) return getExecutableDir(); else //use OS' standard paths return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); -#elif defined FFS_MAC +#elif defined ZEN_MAC return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); //if packaged, used "Contents/Resources", else the executable directory #endif } @@ -76,13 +76,13 @@ Zstring zen::getResourceDir() Zstring zen::getConfigDir() { -#ifdef FFS_WIN +#ifdef ZEN_WIN if (isPortableVersion()) return getInstallDir(); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX if (isPortableVersion()) return getExecutableDir(); -#elif defined FFS_MAC +#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 @@ -102,13 +102,13 @@ Zstring zen::getConfigDir() //this function is called by RealtimeSync!!! Zstring zen::getFreeFileSyncLauncher() { -#ifdef FFS_WIN +#ifdef ZEN_WIN return getInstallDir() + Zstr("FreeFileSync.exe"); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX return getExecutableDir() + Zstr("FreeFileSync"); -#elif defined FFS_MAC +#elif defined ZEN_MAC CFURLRef appURL = nullptr; ZEN_ON_SCOPE_EXIT(if (appURL) ::CFRelease(appURL)); diff --git a/lib/hard_filter.cpp b/lib/hard_filter.cpp index bb94b25d..39cb07f6 100644 --- a/lib/hard_filter.cpp +++ b/lib/hard_filter.cpp @@ -69,10 +69,10 @@ void addFilterEntry(const Zstring& filtername, std::vector<Zstring>& fileFilter, { Zstring filterFormatted = filtername; -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case makeUpper(filterFormatted); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case: nothing to do here #endif if (startsWith(filterFormatted, FILE_NAME_SEPARATOR)) // \abc @@ -289,10 +289,10 @@ NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilte bool NameFilter::passFileFilter(const Zstring& relFilename) const { -#if defined FFS_WIN || defined FFS_MAC //Windows does NOT distinguish between upper/lower-case +#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case Zstring nameFormatted = relFilename; makeUpper(nameFormatted); -#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case +#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case const Zstring& nameFormatted = relFilename; //nothing to do here #endif @@ -305,10 +305,10 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch { assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage -#if defined FFS_WIN || defined FFS_MAC //Windows does NOT distinguish between upper/lower-case +#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case Zstring nameFormatted = relDirname; makeUpper(nameFormatted); -#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case +#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case const Zstring& nameFormatted = relDirname; //nothing to do here #endif diff --git a/lib/help_provider.h b/lib/help_provider.h index 040eb33c..8ddc34c7 100644 --- a/lib/help_provider.h +++ b/lib/help_provider.h @@ -7,11 +7,11 @@ #ifndef HELPPROVIDER_H_INCLUDED #define HELPPROVIDER_H_INCLUDED -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <zen/zstring.h> #include <wx/msw/helpchm.h> -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <wx/html/helpctrl.h> #endif @@ -35,7 +35,7 @@ void displayHelpEntry(const wxString& section, wxWindow* parent); namespace impl { //finish wxWidgets' job -#ifdef FFS_WIN +#ifdef ZEN_WIN class FfsHelpController { public: @@ -55,7 +55,7 @@ private: wxCHMHelpController chmHlp; }; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC class FfsHelpController { public: diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp index 04364b32..cf916174 100644 --- a/lib/icon_buffer.cpp +++ b/lib/icon_buffer.cpp @@ -10,16 +10,16 @@ #include <zen/thread.h> //includes <boost/thread.hpp> #include <zen/scope_guard.h> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <zen/dll.h> #include <zen/win_ver.h> #include <wx/image.h> #include "Thumbnail/thumbnail.h" -#elif defined FFS_LINUX +#elif defined ZEN_LINUX #include <gtk/gtk.h> -#elif defined FFS_MAC +#elif defined ZEN_MAC #include "osx_file_icon.h" #endif @@ -34,7 +34,7 @@ const size_t BUFFER_SIZE_MAX = 600; //maximum number of icons to hold in buffer boost::thread::id mainThreadId = boost::this_thread::get_id(); #endif -#ifdef FFS_WIN +#ifdef ZEN_WIN const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found" #define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<thumb::FunType_##name>(thumb::getDllName(), thumb::funName_##name) : DllFun<thumb::FunType_##name>(); @@ -46,11 +46,11 @@ DEF_DLL_FUN(releaseImageData); // class IconHolder //handle HICON/GdkPixbuf ownership supporting thread-safe usage (in contrast to wxIcon/wxBitmap) { public: -#ifdef FFS_WIN +#ifdef ZEN_WIN typedef const thumb::ImageData* HandleType; -#elif defined FFS_LINUX +#elif defined ZEN_LINUX typedef GdkPixbuf* HandleType; -#elif defined FFS_MAC +#elif defined ZEN_MAC typedef osx::ImageData* HandleType; #endif @@ -67,11 +67,11 @@ public: ~IconHolder() { if (handle_ != nullptr) -#ifdef FFS_WIN +#ifdef ZEN_WIN releaseImageData(handle_); //should be checked already before creating IconHolder! -#elif defined FFS_LINUX +#elif defined ZEN_LINUX ::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"! -#elif defined FFS_MAC +#elif defined ZEN_MAC delete handle_; #endif } @@ -93,7 +93,7 @@ public: if (!handle_) return wxNullBitmap; -#ifdef FFS_WIN +#ifdef ZEN_WIN ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction //let wxImage reference data without taking ownership: @@ -101,7 +101,7 @@ public: fileIcon.SetAlpha(handle_->alpha, true); return wxBitmap(fileIcon); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX #if wxCHECK_VERSION(2, 9, 4) return wxBitmap(release()); //ownership passed! #else @@ -110,7 +110,7 @@ public: return newIcon; #endif -#elif defined FFS_MAC +#elif defined ZEN_MAC ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction //let wxImage reference data without taking ownership: @@ -137,7 +137,7 @@ public: }; -#ifdef FFS_WIN +#ifdef ZEN_WIN Zstring getFileExtension(const Zstring& filename) { const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! @@ -217,7 +217,7 @@ IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize return getIconByAttribute((L"dummy." + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); } -#elif defined FFS_LINUX +#elif defined ZEN_LINUX IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz) { if (gicon) @@ -237,11 +237,11 @@ IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz) IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return 0 on failure { -#ifdef FFS_WIN +#ifdef ZEN_WIN if (getThumbnail && releaseImageData) return IconHolder(getThumbnail(filename.c_str(), requestedSize)); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX gint width = 0; gint height = 0; if (GdkPixbufFormat* fmt = ::gdk_pixbuf_get_file_info(filename.c_str(), &width, &height)) @@ -263,12 +263,12 @@ IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return } } -#elif defined FFS_MAC +#elif defined ZEN_MAC try { - return IconHolder(new osx::ImageData(osx::getThumbnail(filename.c_str(), requestedSize))); //throw OsxError + return IconHolder(new osx::ImageData(osx::getThumbnail(filename.c_str(), requestedSize))); //throw SysError } - catch (osx::OsxError&) {} + catch (zen::SysError&) {} #endif return IconHolder(); } @@ -277,10 +277,10 @@ IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return IconHolder getGenericFileIcon(IconBuffer::IconSize sz) { //we're called by getAssociatedIcon()! -> avoid endless recursion! -#ifdef FFS_WIN +#ifdef ZEN_WIN return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX const char* mimeFileIcons[] = { "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes @@ -296,12 +296,12 @@ IconHolder getGenericFileIcon(IconBuffer::IconSize sz) return IconHolder(pixBuf); //pass ownership return IconHolder(); -#elif defined FFS_MAC +#elif defined ZEN_MAC try { - return IconHolder(new osx::ImageData(osx::getDefaultFileIcon(IconBuffer::getSize(sz)))); //throw OsxError + return IconHolder(new osx::ImageData(osx::getDefaultFileIcon(IconBuffer::getSize(sz)))); //throw SysError } - catch (osx::OsxError&) {} + catch (zen::SysError&) {} return IconHolder(); #endif } @@ -309,20 +309,20 @@ IconHolder getGenericFileIcon(IconBuffer::IconSize sz) IconHolder getGenericDirectoryIcon(IconBuffer::IconSize sz) { -#ifdef FFS_WIN +#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 FFS_LINUX +#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 FFS_MAC +#elif defined ZEN_MAC try { - return IconHolder(new osx::ImageData(osx::getDefaultFolderIcon(IconBuffer::getSize(sz)))); //throw OsxError + return IconHolder(new osx::ImageData(osx::getDefaultFolderIcon(IconBuffer::getSize(sz)))); //throw SysError } - catch (osx::OsxError&) { return IconHolder(); } + catch (zen::SysError&) { return IconHolder(); } #endif } @@ -345,7 +345,7 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) warn_static("problem: für folder links ist getThumbnail erfolgreich => SFGAO_LINK nicht gecheckt!") //2. retrieve file icons -#ifdef FFS_WIN +#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 @@ -381,7 +381,7 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) return IconHolder(imgData); } -#elif defined FFS_LINUX +#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);) @@ -393,12 +393,12 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) } //need fallback: icon lookup may fail because some icons are currently not present on system -#elif defined FFS_MAC +#elif defined ZEN_MAC try { - return IconHolder(new osx::ImageData(osx::getFileIcon(filename.c_str(), IconBuffer::getSize(sz)))); //throw OsxError + return IconHolder(new osx::ImageData(osx::getFileIcon(filename.c_str(), IconBuffer::getSize(sz)))); //throw SysError } - catch (osx::OsxError&) {} + catch (zen::SysError&) {} #endif return ::getGenericFileIcon(sz); //make sure this does not internally call getAssociatedIcon("someDefaultFile.txt")!!! => endless recursion! } @@ -548,7 +548,7 @@ private: 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 FFS_WIN +#ifdef ZEN_WIN //Prerequisites, see thumbnail.h //1. Initialize COM @@ -611,13 +611,13 @@ int IconBuffer::getSize(IconSize icoSize) switch (icoSize) { case IconBuffer::SIZE_SMALL: -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC return 16; -#elif defined FFS_LINUX +#elif defined ZEN_LINUX return 24; #endif case IconBuffer::SIZE_MEDIUM: -#ifdef FFS_WIN +#ifdef ZEN_WIN if (!wereVistaOrLater) return 32; //48x48 doesn't look sharp on XP #endif return 48; @@ -632,7 +632,7 @@ int IconBuffer::getSize(IconSize icoSize) bool IconBuffer::readyForRetrieval(const Zstring& filename) { -#ifdef FFS_WIN +#ifdef ZEN_WIN if (iconSizeType == IconBuffer::SIZE_SMALL) if (isCheapExtension(getFileExtension(filename))) return true; @@ -643,7 +643,7 @@ bool IconBuffer::readyForRetrieval(const Zstring& filename) Opt<wxBitmap> IconBuffer::retrieveFileIcon(const Zstring& filename) { -#ifdef FFS_WIN +#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! { diff --git a/lib/localization.cpp b/lib/localization.cpp index f050e255..5526cdce 100644 --- a/lib/localization.cpp +++ b/lib/localization.cpp @@ -10,18 +10,19 @@ #include <iterator> #include <zen/string_tools.h> #include <zen/file_traverser.h> -#include <zenxml/io.h> +#include <zen/serialize.h> #include <zen/i18n.h> #include <zen/format_unit.h> #include <wx/intl.h> +#include <wx/log.h> #include "parse_plural.h" #include "parse_lng.h" #include "ffs_paths.h" -#ifdef FFS_LINUX +#ifdef ZEN_LINUX #include <wchar.h> //wcscasecmp -#elif defined FFS_MAC +#elif defined ZEN_MAC #include <CoreServices/CoreServices.h> #endif @@ -33,7 +34,7 @@ namespace class FFSTranslation : public TranslationHandler { public: - FFSTranslation(const std::wstring& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError + FFSTranslation(const Zstring& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError wxLanguage langId() const { return langId_; } @@ -69,16 +70,16 @@ private: }; -FFSTranslation::FFSTranslation(const std::wstring& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError +FFSTranslation::FFSTranslation(const Zstring& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError { std::string inputStream; try { - inputStream = loadStream(filename); //throw XmlFileError + inputStream = loadBinStream<std::string>(filename); //throw FileError } - catch (const XmlFileError&) + catch (const FileError& e) { - throw lngfile::ParsingError(0, 0); + 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; @@ -122,7 +123,7 @@ public: } virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } - virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { return nullptr; } + virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) { return nullptr; } virtual HandleError reportDirError (const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // @@ -136,7 +137,7 @@ struct LessTranslation : public std::binary_function<ExistingTranslations::Entry bool operator()(const ExistingTranslations::Entry& lhs, const ExistingTranslations::Entry& rhs) const { //use a more "natural" sort: ignore case and diacritics -#ifdef FFS_WIN +#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, @@ -148,11 +149,11 @@ struct LessTranslation : public std::binary_function<ExistingTranslations::Entry else return rv == CSTR_LESS_THAN; //convert to C-style string compare result -#elif defined FFS_LINUX +#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 FFS_MAC +#elif defined ZEN_MAC auto allocCFStringRef = [](const std::wstring& str) -> CFStringRef //output not owned! { return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, @@ -182,7 +183,7 @@ ExistingTranslations::ExistingTranslations() newEntry.languageName = L"English (US)"; newEntry.languageFile = L""; newEntry.translatorName = L"Zenju"; - newEntry.languageFlag = L"usa.png"; + newEntry.languageFlag = L"flag_usa.png"; locMapping.push_back(newEntry); } @@ -196,30 +197,33 @@ ExistingTranslations::ExistingTranslations() for (auto it = lngFiles.begin(); it != lngFiles.end(); ++it) try { - std::string stream = loadStream(*it); //throw XmlFileError - try + const std::string stream = loadBinStream<std::string>(utfCvrtTo<Zstring>(*it)); //throw FileError + + lngfile::TransHeader lngHeader; + lngfile::parseHeader(stream, lngHeader); //throw ParsingError + + assert(!lngHeader.languageName .empty()); + assert(!lngHeader.translatorName.empty()); + assert(!lngHeader.localeName .empty()); + assert(!lngHeader.flagFile .empty()); + /* + There is some buggy behavior in wxWidgets which maps "zh_TW" to simplified chinese. + Fortunately locales can be also entered as description. I changed to "Chinese (Traditional)" which works fine. + */ + if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utfCvrtTo<wxString>(lngHeader.localeName))) { - lngfile::TransHeader lngHeader; - lngfile::parseHeader(stream, lngHeader); //throw ParsingError - - /* - There is some buggy behavior in wxWidgets which maps "zh_TW" to simplified chinese. - Fortunately locales can be also entered as description. I changed to "Chinese (Traditional)" which works fine. - */ - if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utfCvrtTo<wxString>(lngHeader.localeName))) - { - ExistingTranslations::Entry newEntry; - newEntry.languageID = locInfo->Language; - newEntry.languageName = utfCvrtTo<std::wstring>(lngHeader.languageName); - newEntry.languageFile = utfCvrtTo<std::wstring>(*it); - newEntry.translatorName = utfCvrtTo<std::wstring>(lngHeader.translatorName); - newEntry.languageFlag = utfCvrtTo<std::wstring>(lngHeader.flagFile); - locMapping.push_back(newEntry); - } + ExistingTranslations::Entry newEntry; + newEntry.languageID = locInfo->Language; + newEntry.languageName = utfCvrtTo<std::wstring>(lngHeader.languageName); + newEntry.languageFile = utfCvrtTo<std::wstring>(*it); + newEntry.translatorName = utfCvrtTo<std::wstring>(lngHeader.translatorName); + newEntry.languageFlag = utfCvrtTo<std::wstring>(lngHeader.flagFile); + locMapping.push_back(newEntry); } - catch (lngfile::ParsingError&) { assert(false); } //better not show an error message here; scenario: batch jobs + else assert(false); } - catch (...) { 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()); } @@ -397,6 +401,9 @@ public: 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 @@ -444,18 +451,19 @@ void zen::setLanguage(int language) //throw FileError else try { - zen::setTranslator(new FFSTranslation(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError + zen::setTranslator(new FFSTranslation(utfCvrtTo<Zstring>(languageFile), static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError } catch (lngfile::ParsingError& e) { throw FileError(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), L"%x", fmtFileName(utfCvrtTo<Zstring>(languageFile))), - L"%y", numberTo<std::wstring>(e.row + 1)), - L"%z", numberTo<std::wstring>(e.col + 1))); + L"%y", numberTo<std::wstring>(e.row_ + 1)), + L"%z", numberTo<std::wstring>(e.col_ + 1)) + + L"\n\n" + e.msg_); } catch (parse_plural::ParsingError&) { - throw FileError(L"Invalid Plural Form"); + throw FileError(L"Invalid plural form definition"); //user should never see this! } //handle RTL swapping: we need wxWidgets to do this diff --git a/lib/osx_file_icon.h b/lib/osx_file_icon.h index e9b17988..5edfd740 100644 --- a/lib/osx_file_icon.h +++ b/lib/osx_file_icon.h @@ -8,7 +8,7 @@ #define OSX_FILE_ICON_8427508422345342 #include <vector> -#include <zen/osx_error.h> +#include <zen/sys_error.h> namespace osx { @@ -27,10 +27,10 @@ private: ImageData& operator=(const ImageData&); }; -ImageData getThumbnail(const char* filename, int requestedSize); //throw OsxError -ImageData getFileIcon (const char* filename, int requestedSize); //throw OsxError -ImageData getDefaultFileIcon (int requestedSize); //throw OsxError -ImageData getDefaultFolderIcon(int requestedSize); //throw OsxError +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 index 11fb053f..4db6642a 100644 --- a/lib/osx_file_icon.mm +++ b/lib/osx_file_icon.mm @@ -11,7 +11,7 @@ namespace { -osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw OsxError; NSException? +osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw SysError; NSException? { /* wxBitmap(NSImage*) is not good enough: it calls "[NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]" @@ -93,53 +93,53 @@ osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw OsxError; } -osx::ImageData osx::getThumbnail(const char* filename, int requestedSize) //throw OsxError +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 OsxError; can this fail? not documented + 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 OsxError + return extractBytes(nsImg, requestedSize); //throw SysError } } - @catch (NSException* e) + @catch(NSException* e) { - throwOsxError(e); //throw OsxError + throwSysError(e); //throw SysError } } -osx::ImageData osx::getFileIcon(const char* filename, int requestedSize) //throw OsxError +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 OsxError; can this fail? not documented + 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 OsxError + return extractBytes(nsImg, requestedSize); //throw SysError } } @catch (NSException* e) { - throwOsxError(e); //throw OsxError + throwSysError(e); //throw SysError } } -osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw OsxError +osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw SysError { @try { @@ -149,17 +149,17 @@ osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw OsxError //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:@"dat"]; ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented - return extractBytes(nsImg, requestedSize); //throw OsxError + return extractBytes(nsImg, requestedSize); //throw SysError } } @catch (NSException* e) { - throwOsxError(e); //throw OsxError + throwSysError(e); //throw SysError } } -osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw OsxError +osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw SysError { @try { @@ -169,11 +169,11 @@ osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw OsxError //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)]; ZEN_OSX_ASSERT(nsImg != nil); //may fail - return extractBytes(nsImg, requestedSize); //throw OsxError + return extractBytes(nsImg, requestedSize); //throw SysError } } @catch (NSException* e) { - throwOsxError(e); //throw OsxError + throwSysError(e); //throw SysError } } diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp index 33d8174f..df8ff095 100644 --- a/lib/parallel_scan.cpp +++ b/lib/parallel_scan.cpp @@ -20,7 +20,7 @@ using namespace zen; namespace { /* -#ifdef FFS_WIN +#ifdef ZEN_WIN struct DiskInfo { @@ -288,7 +288,7 @@ public: handleSymlinks_(handleSymlinks), filterInstance(filter), failedDirReads_(failedDirReads), - failedItemReads_(failedItemReads), + failedItemReads_(failedItemReads), acb_(acb), threadID_(threadID) {} @@ -296,7 +296,7 @@ public: const HardFilter::FilterRef filterInstance; //always bound! std::set<Zstring>& failedDirReads_; - std::set<Zstring>& failedItemReads_; + std::set<Zstring>& failedItemReads_; AsyncCallback& acb_; const long threadID_; @@ -313,10 +313,11 @@ public: relNameParentPf_(relNameParentPf), output_(output) {} - virtual std::shared_ptr<TraverseCallback> - onDir (const Zchar* shortName, const Zstring& fullName); 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); virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName); @@ -357,7 +358,7 @@ void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) */ - output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTime, details.fileSize, details.id)); + output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTime, details.fileSize, details.id, details.symlinkInfo != nullptr)); cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator } @@ -398,7 +399,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const Zchar* shortName, const Zst } -std::shared_ptr<TraverseCallback> DirCallback::onDir(const Zchar* shortName, const Zstring& fullName) +TraverseCallback* DirCallback::onDir(const Zchar* shortName, const Zstring& fullName) { boost::this_thread::interruption_point(); @@ -419,7 +420,14 @@ std::shared_ptr<TraverseCallback> DirCallback::onDir(const Zchar* shortName, con if (passFilter) cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator - return std::make_shared<DirCallback>(cfg, relName + FILE_NAME_SEPARATOR, subDir); + return new DirCallback(cfg, relName + 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; } @@ -428,7 +436,7 @@ DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg) switch (cfg.acb_.reportError(msg)) { case FillBufferCallback::ON_ERROR_IGNORE: - cfg.failedDirReads_.insert(relNameParentPf_); + cfg.failedDirReads_.insert(relNameParentPf_); return ON_ERROR_IGNORE; case FillBufferCallback::ON_ERROR_RETRY: @@ -444,7 +452,7 @@ DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, c switch (cfg.acb_.reportError(msg)) { case FillBufferCallback::ON_ERROR_IGNORE: - cfg.failedItemReads_.insert(relNameParentPf_ + shortName); + cfg.failedItemReads_.insert(relNameParentPf_ + shortName); return ON_ERROR_IGNORE; case FillBufferCallback::ON_ERROR_RETRY: @@ -455,7 +463,7 @@ DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, c } -#ifdef FFS_WIN +#ifdef ZEN_WIN class DstHackCallbackImpl : public DstHackCallback { public: @@ -501,7 +509,7 @@ public: dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy dirKey_.filter_, dirOutput_.failedDirReads, - dirOutput_.failedItemReads, + dirOutput_.failedItemReads, *acb_); DirCallback traverser(travCfg, @@ -509,7 +517,7 @@ public: dirOutput_.dirCont); DstHackCallback* dstCallbackPtr = nullptr; -#ifdef FFS_WIN +#ifdef ZEN_WIN DstHackCallbackImpl dstCallback(*acb_, threadID_); dstCallbackPtr = &dstCallback; #endif diff --git a/lib/parallel_scan.h b/lib/parallel_scan.h index b7518428..c3806373 100644 --- a/lib/parallel_scan.h +++ b/lib/parallel_scan.h @@ -47,7 +47,7 @@ struct DirectoryValue { DirContainer dirCont; std::set<Zstring> failedDirReads; //relative postfixed names (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop - std::set<Zstring> failedItemReads; //relative postfixed names (never empty) for failure to read single file/dir/symlink + std::set<Zstring> failedItemReads; //relative postfixed names (never empty) for failure to read single file/dir/symlink }; diff --git a/lib/parse_lng.h b/lib/parse_lng.h index 48c7044b..8cd8e943 100644 --- a/lib/parse_lng.h +++ b/lib/parse_lng.h @@ -10,7 +10,6 @@ #include <algorithm> #include <cctype> #include <functional> -//#include <list> #include <memory> #include <map> #include <set> @@ -18,6 +17,7 @@ #include <stdexcept> #include <string> #include <vector> +#include <list> #include <zen/utf.h> #include <zen/string_tools.h> #include "parse_plural.h" @@ -47,15 +47,16 @@ struct TransHeader struct ParsingError { - ParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {} - size_t row; //starting with 0 - size_t col; // + 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 TranslationList; //unordered list of unique translation items -std::string generateLng(const TranslationList& in, const TransHeader& header); +class TranslationUnorderedList; //unordered list of unique translation items +std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header); @@ -76,45 +77,56 @@ std::string generateLng(const TranslationList& in, const TransHeader& header); //--------------------------- implementation --------------------------- -class TranslationList //unordered list of unique translation items +class TranslationUnorderedList //unordered list of unique translation items { public: - void addItem(const std::string& orig, const std::string& trans) + TranslationUnorderedList(TranslationMap&& transOld, TranslationPluralMap&& transPluralOld) : transOld_(std::move(transOld)), transPluralOld_(std::move(transPluralOld)) {} + + void addItem(const std::string& orig) { if (!transUnique.insert(orig).second) return; - sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, trans))); + auto it = transOld_.find(orig); + if (it != transOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing + sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, it->second))); + else + sequence.push_front(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); //put untranslated items to the front of the .lng file } - void addPluralItem(const SingularPluralPair& orig, const PluralForms& trans) + + void addItem(const SingularPluralPair& orig) { if (!pluralUnique.insert(orig).second) return; - sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, trans))); + auto it = transPluralOld_.find(orig); + if (it != transPluralOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing + sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, it->second))); + else + sequence.push_front(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); //put untranslated items to the front of the .lng file } - bool untranslatedTextExists() const + bool untranslatedTextExists() const { return std::any_of(sequence.begin(), sequence.end(), [](const std::shared_ptr<Item>& item) { return !item->hasTranslation(); }); } + + template <class Function, class Function2> + void visitItems(Function onTrans, Function2 onPluralTrans) const //onTrans takes (const TranslationMap::value_type&), onPluralTrans takes (const TranslationPluralMap::value_type&) { for (auto it = sequence.begin(); it != sequence.end(); ++it) - if (const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(it->get())) - { - if (regular->value.second.empty()) - return true; - } - else if (const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(it->get())) - if (plural->value.second.empty()) - return true; - return false; + if (auto regular = dynamic_cast<const RegularItem*>(it->get())) + onTrans(regular->value); + else if (auto plural = dynamic_cast<const PluralItem*>(it->get())) + onPluralTrans(plural->value); + else assert(false); } private: - friend std::string generateLng(const TranslationList& in, const TransHeader& header); + 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; }; - struct Item { virtual ~Item() {} }; - struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} TranslationMap ::value_type value; }; - struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} TranslationPluralMap::value_type value; }; - - std::vector<std::shared_ptr<Item>> sequence; //ordered list of translation elements + std::list<std::shared_ptr<Item>> sequence; //ordered list of translation elements std::set<TranslationMap ::key_type> transUnique; //check uniqueness std::set<TranslationPluralMap::key_type> pluralUnique; // + + const TranslationMap transOld_; //reuse existing translation + const TranslationPluralMap transPluralOld_; // }; @@ -179,18 +191,18 @@ private: //header information tokens.insert(std::make_pair(Token::TK_HEADER_BEGIN, "<header>")); tokens.insert(std::make_pair(Token::TK_HEADER_END, "</header>")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "<language name>")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "</language name>")); + tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "<language>")); + tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "</language>")); tokens.insert(std::make_pair(Token::TK_TRANS_NAME_BEGIN, "<translator>")); tokens.insert(std::make_pair(Token::TK_TRANS_NAME_END, "</translator>")); tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_BEGIN, "<locale>")); tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "</locale>")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "<flag file>")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "</flag file>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "<plural forms>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "</plural forms>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "<plural definition>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "</plural definition>")); + tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "<flag_image>")); + tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "</flag_image>")); + tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "<plural_form_count>")); + tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "</plural_form_count>")); + tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "<plural_definition>")); + tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "</plural_definition>")); //item level tokens.insert(std::make_pair(Token::TK_SRC_BEGIN, "<source>")); @@ -317,7 +329,7 @@ public: } catch (const parse_plural::InvalidPluralForm&) { - throw ParsingError(scn.posRow(), scn.posCol()); + throw ParsingError(L"Invalid plural form definition", scn.posRow(), scn.posCol()); } } @@ -421,7 +433,7 @@ private: void validateTranslation(const std::string& original, const std::string& translation) //throw ParsingError { if (original.empty()) - throw ParsingError(scn.posRow(), scn.posCol()); + throw ParsingError(L"Source translation is empty", scn.posRow(), scn.posCol()); if (!translation.empty()) { @@ -430,31 +442,36 @@ private: { if (zen::contains(original, placeholder) && !zen::contains(translation, placeholder)) - throw ParsingError(scn.posRow(), scn.posCol()); + throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol()); }; checkPlaceholder("%x"); checkPlaceholder("%y"); checkPlaceholder("%z"); + + //if source contains ampersand to mark menu accellerator key, so must translation + if (hasSingleAmpersand(original) && !hasSingleAmpersand(translation)) + throw ParsingError(L"Translation is missing the & character to mark an access key for the menu item", scn.posRow(), scn.posCol()); } } void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError { + using namespace zen; //check the primary placeholder is existing at least for the second english text - if (!zen::contains(original.second, "%x")) - throw ParsingError(scn.posRow(), scn.posCol()); + if (!contains(original.second, "%x")) + throw ParsingError(L"Plural form source does not contain %x placeholder", scn.posRow(), scn.posCol()); if (!translation.empty()) { //check for invalid number of plural forms if (pluralInfo.getCount() != static_cast<int>(translation.size())) - throw ParsingError(scn.posRow(), scn.posCol()); + throw ParsingError(replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo<std::wstring>(translation.size())), L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn.posRow(), scn.posCol()); //ensure the placeholder is used when needed int pos = 0; for (auto it = translation.begin(); it != translation.end(); ++it, ++pos) - if (!pluralInfo.isSingleNumberForm(pos) && !zen::contains(*it, "%x")) - throw ParsingError(scn.posRow(), scn.posCol()); + if (!pluralInfo.isSingleNumberForm(pos) && !contains(*it, "%x")) + throw ParsingError(replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn.posRow(), scn.posCol()); auto checkSecondaryPlaceholder = [&](const std::string& placeholder) { @@ -464,11 +481,11 @@ private: { if (!zen::contains(original.first, placeholder) || !zen::contains(original.second, placeholder)) - throw ParsingError(scn.posRow(), scn.posCol()); + throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol()); //secondary placeholder is required for all plural forms if (!std::all_of(translation.begin(), translation.end(), [&](const std::string& pform) { return zen::contains(pform, placeholder); })) - throw ParsingError(scn.posRow(), scn.posCol()); + throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol()); } }; @@ -477,6 +494,24 @@ private: } } + 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; } @@ -489,7 +524,7 @@ private: void expectToken(Token::Type t) //throw ParsingError { if (token().type != t) - throw ParsingError(scn.posRow(), scn.posCol()); + throw ParsingError(L"Unexpected token", scn.posRow(), scn.posCol()); } Scanner scn; @@ -529,7 +564,7 @@ void formatMultiLineText(std::string& text) } -std::string generateLng(const TranslationList& in, const TransHeader& header) +std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header) { std::string out; //header @@ -564,66 +599,55 @@ std::string generateLng(const TranslationList& in, const TransHeader& header) out += '\n'; - //items - for (auto it = in.sequence.begin(); it != in.sequence.end(); ++it) + in.visitItems([&](const TranslationMap::value_type& trans) { - const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(it->get()); - const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(it->get()); + std::string original = trans.first; + std::string translation = trans.second; - if (regular) - { - std::string original = regular->value.first; - std::string translation = regular->value.second; + formatMultiLineText(original); + formatMultiLineText(translation); - 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_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'; - - } - else if (plural) + 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); + if (!forms.empty()) out += '\n'; + + for (PluralForms::const_iterator j = forms.begin(); j != forms.end(); ++j) { - std::string engSingular = plural->value.first.first; - std::string engPlural = plural->value.first.second; - const PluralForms& forms = plural->value.second; - - formatMultiLineText(engSingular); - formatMultiLineText(engPlural); + std::string plForm = *j; + formatMultiLineText(plForm); - out += KnownTokens::text(Token::TK_SRC_BEGIN) + '\n'; out += KnownTokens::text(Token::TK_PLURAL_BEGIN); - out += engSingular; + out += plForm; 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); - if (!forms.empty()) out += '\n'; - - for (PluralForms::const_iterator j = forms.begin(); j != forms.end(); ++j) - { - std::string plForm = *j; - 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'; - } - else - { - throw std::logic_error("that's what you get for brittle design ;)"); } - } + 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 } diff --git a/lib/parse_plural.h b/lib/parse_plural.h index bb32f81f..e9e04dbc 100644 --- a/lib/parse_plural.h +++ b/lib/parse_plural.h @@ -460,7 +460,7 @@ PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) / inline -PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError +PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError } #endif // PARSE_PLURAL_H_INCLUDED
\ No newline at end of file diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp index 78a40159..2422b2ef 100644 --- a/lib/process_xml.cpp +++ b/lib/process_xml.cpp @@ -47,17 +47,16 @@ XmlType getXmlType(const zen::XmlDoc& doc) //throw() XmlType xmlAccess::getXmlType(const Zstring& filename) //throw() { - XmlDoc doc; try { //do NOT use zen::loadStream as it will superfluously load even huge files! - loadXmlDocument(filename, doc); //throw FfsXmlError, quick exit if file is not an FFS XML + 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; } - return ::getXmlType(doc); } @@ -1122,8 +1121,7 @@ bool needsMigration(const XmlDoc& doc, int currentXmlFormatVer) template <class ConfigType> void readConfig(const Zstring& filename, XmlType type, ConfigType& cfg, int currentXmlFormatVer, bool& needMigration) //throw FfsXmlError { - XmlDoc doc; - loadXmlDocument(filename, doc); //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))); @@ -1209,8 +1207,7 @@ void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfi const Zstring& filename = *it; const bool firstItem = it == filenames.begin(); //init all non-"mainCfg" settings with first config file - XmlDoc doc; - loadXmlDocument(filename, doc); //throw FfsXmlError + XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError //do NOT use zen::loadStream as it will superfluously load even huge files! switch (::getXmlType(doc)) @@ -1220,8 +1217,7 @@ void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfi XmlGuiConfig guiCfg = parseConfig<XmlGuiConfig>(doc, filename, XML_FORMAT_VER_FFS_GUI, warning); //nothrow if (firstItem) config = guiCfg; - else - mainCfgs.push_back(guiCfg.mainCfg); + mainCfgs.push_back(guiCfg.mainCfg); } break; @@ -1230,8 +1226,7 @@ void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfi XmlBatchConfig batchCfg = parseConfig<XmlBatchConfig>(doc, filename, XML_FORMAT_VER_FFS_BATCH, warning); //nothrow if (firstItem) config = convertBatchToGui(batchCfg); - else - mainCfgs.push_back(batchCfg.mainCfg); + mainCfgs.push_back(batchCfg.mainCfg); } break; @@ -1240,7 +1235,6 @@ void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfi throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); } } - mainCfgs.push_back(config.mainCfg); //save cfg from first line config.mainCfg = merge(mainCfgs); diff --git a/lib/process_xml.h b/lib/process_xml.h index 626fafe0..95fa644d 100644 --- a/lib/process_xml.h +++ b/lib/process_xml.h @@ -175,17 +175,17 @@ struct XmlGlobalSettings cfgFileHistMax(30), folderHistMax(15), onCompletionHistoryMax(8), -#ifdef FFS_WIN +#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 FFS_LINUX +#elif defined ZEN_LINUX defaultExclusionFilter(Zstr("/.Trash-*/") Zstr("\n") Zstr("/.recycle/")), -#elif defined FFS_MAC +#elif defined ZEN_MAC defaultExclusionFilter(Zstr("/.fseventsd/") Zstr("\n") Zstr("/.Spotlight-V100/") Zstr("\n") Zstr("/.Trashes/") Zstr("\n") @@ -194,9 +194,9 @@ struct XmlGlobalSettings #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 FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC textSearchRespectCase(false), -#elif defined FFS_LINUX +#elif defined ZEN_LINUX textSearchRespectCase(true), #endif showIcons(true), @@ -204,16 +204,16 @@ struct XmlGlobalSettings lastUpdateCheck(0) { //default external apps will be translated "on the fly"!!! First entry will be used for [Enter] or mouse double-click! -#ifdef FFS_WIN +#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 FFS_LINUX +#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 FFS_MAC +#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 diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp index bf6f99a2..035e1d77 100644 --- a/lib/resolve_path.cpp +++ b/lib/resolve_path.cpp @@ -4,9 +4,10 @@ #include <zen/time.h> #include <zen/thread.h> #include <zen/utf.h> +#include <zen/scope_guard.h> #include <wx/utils.h> //wxGetEnv -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <zen/long_path_prefix.h> #include <zen/file_handling.h> #include <zen/win.h> //includes "windows.h" @@ -16,8 +17,9 @@ #pragma comment(lib, "Mpr.lib") #endif -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <stdlib.h> //getenv() +#include <unistd.h> //getcwd #endif using namespace zen; @@ -25,24 +27,25 @@ using namespace zen; namespace { -#ifdef FFS_WIN +#ifdef ZEN_WIN Zstring resolveRelativePath(const Zstring& relativeName) //note: ::GetFullPathName() is documented not threadsafe! { - const DWORD bufferSize = 10000; - std::vector<wchar_t> buffer(bufferSize); - //don't use long path prefix! does not work with relative paths "." and ".." - const DWORD charsWritten = ::GetFullPathName(relativeName.c_str(), //__in LPCTSTR lpFileName, - bufferSize, //__in DWORD nBufferLength, - &buffer[0], //__out LPTSTR lpBuffer, - nullptr); //__out LPTSTR *lpFilePart - if (charsWritten == 0 || charsWritten >= bufferSize) //theoretically, charsWritten cannot be == "bufferSize" - return relativeName; //ERROR! Don't do anything - - return Zstring(&buffer[0], charsWritten); + const DWORD bufferSize = ::GetFullPathName(relativeName.c_str(), 0, nullptr, nullptr); + if (bufferSize > 0) + { + std::vector<wchar_t> buffer(bufferSize); + const DWORD charsWritten = ::GetFullPathName(relativeName.c_str(), //__in LPCTSTR lpFileName, + bufferSize, //__in DWORD nBufferLength, + &buffer[0], //__out LPTSTR lpBuffer, + nullptr); //__out LPTSTR *lpFilePart + if (0 < charsWritten && charsWritten < bufferSize) //theoretically, charsWritten can never be == "bufferSize" + return Zstring(&buffer[0], charsWritten); + } + return relativeName; //ERROR! Don't do anything } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC Zstring resolveRelativePath(const Zstring& relativeName) { //http://linux.die.net/man/2/path_resolution @@ -69,16 +72,18 @@ Zstring resolveRelativePath(const Zstring& relativeName) } //we cannot use ::realpath() since it resolves *existing* relative paths only! - std::vector<char> buffer(10000); - if (::getcwd(&buffer[0], buffer.size()) != nullptr) - return appendSeparator(&buffer[0]) + relativeName; + if (char* dirpath = ::getcwd(nullptr, 0)) + { + ZEN_ON_SCOPE_EXIT(::free(dirpath)); + return appendSeparator(dirpath) + relativeName; + } } return relativeName; } #endif -#ifdef FFS_WIN +#ifdef ZEN_WIN class CsidlConstants { public: @@ -273,7 +278,7 @@ std::unique_ptr<Zstring> resolveMacro(const Zstring& macro, //macro without %-ch if (std::unique_ptr<Zstring> value = getEnvironmentVar(macro)) return value; -#ifdef FFS_WIN +#ifdef ZEN_WIN //try to resolve as CSIDL value { const auto& csidlMap = CsidlConstants::get(); @@ -316,8 +321,8 @@ Zstring zen::expandMacros(const Zstring& text) { return ::expandMacros(text, std namespace { -#ifdef FFS_WIN -//networks and cdrom excluded - this should not block +#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! @@ -412,11 +417,11 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna rest = afterFirst(rest, Zstr(':')); if (startsWith(rest, FILE_NAME_SEPARATOR)) rest = afterFirst(rest, FILE_NAME_SEPARATOR); -#ifdef FFS_WIN +#ifdef ZEN_WIN //[.*] pattern was found... if (!volname.empty()) { - Zstring volPath = getPathByVolumenName(volname); //should not block?! + Zstring volPath = getPathByVolumenName(volname); //may block for slow USB sticks! if (!volPath.empty()) return appendSeparator(volPath) + rest; //successfully replaced pattern } @@ -430,7 +435,7 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna C:\Program Files\FreeFileSync\[FFS USB]\FreeFileSync\ */ return L"?:\\[" + volname + L"]\\" + rest; -#elif defined FFS_LINUX || defined FFS_MAC //neither supported nor needed +#elif defined ZEN_LINUX || defined ZEN_MAC //neither supported nor needed return "/.../[" + volname + "]/" + rest; #endif } @@ -442,8 +447,8 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, LessFilename>& output) { -#ifdef FFS_WIN - //1. replace volume path by volume name: c:\dirname -> [SYSTEM]\dirname +#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':' && @@ -473,7 +478,7 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less if (std::unique_ptr<Zstring> value = getEnvironmentVar(envName)) envToDir.insert(std::make_pair(envName, *value)); }; -#ifdef FFS_WIN +#ifdef ZEN_WIN addEnvVar(L"AllUsersProfile"); // C:\ProgramData addEnvVar(L"AppData"); // C:\Users\<user>\AppData\Roaming addEnvVar(L"LocalAppData"); // C:\Users\<user>\AppData\Local @@ -491,19 +496,19 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less const auto& csidlMap = CsidlConstants::get(); envToDir.insert(csidlMap.begin(), csidlMap.end()); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC addEnvVar("HOME"); //Linux: /home/<user> Mac: /Users/<user> #endif //substitute paths by symbolic names auto pathStartsWith = [](const Zstring& path, const Zstring& prefix) -> bool { -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC Zstring tmp = path; Zstring tmp2 = prefix; ::makeUpper(tmp); ::makeUpper(tmp2); return startsWith(tmp, tmp2); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX return startsWith(path, prefix); #endif }; @@ -557,7 +562,7 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw() return Zstring(); dirname = expandMacros(dirname); - dirname = expandVolumeName(dirname); //should not block + dirname = expandVolumeName(dirname); //may block for slow USB sticks! /* need to resolve relative paths: @@ -575,7 +580,7 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw() } -#ifdef FFS_WIN +#ifdef ZEN_WIN void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteraction) //throw() - user interaction: show OS password prompt { /* diff --git a/lib/resolve_path.h b/lib/resolve_path.h index 4e85c8ee..b9c7196f 100644 --- a/lib/resolve_path.h +++ b/lib/resolve_path.h @@ -19,14 +19,14 @@ FULL directory format: - convert relative paths into absolute - trim whitespace and append file name separator */ -Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - non-blocking! no I/O! not thread-safe!!!(see ::GetFullPathName()) +Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - may still block for slow USB sticks! not thread-safe!!!(see ::GetFullPathName()) //macro substitution only Zstring expandMacros(const Zstring& text); -std::vector<Zstring> getDirectoryAliases(const Zstring& dirString); +std::vector<Zstring> getDirectoryAliases(const Zstring& dirString); //may block for slow USB sticks when resolving [<volume name>] -#ifdef FFS_WIN +#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 diff --git a/lib/resources.cpp b/lib/resources.cpp index 6f48e2e1..6deaf0ec 100644 --- a/lib/resources.cpp +++ b/lib/resources.cpp @@ -69,19 +69,19 @@ GlobalResources::GlobalResources() else if (name == L"wink.gif") loadAnimFromZip(resourceFile, aniWink); else if (name == L"working.gif") - loadAnimFromZip(resourceFile, aniSync); + loadAnimFromZip(resourceFile, aniWorking); } } -#ifdef FFS_WIN +#ifdef ZEN_WIN //for compatibility it seems we need to stick with a "real" icon programIconFFS = wxIcon(L"A_FFS_ICON"); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX //attention: make sure to not implicitly call "instance()" again => deadlock on Linux programIconFFS.CopyFromBitmap(getImage(L"FreeFileSync")); //use big logo bitmap for better quality -#elif defined FFS_MAC +#elif defined ZEN_MAC assert(getImage(L"FreeFileSync").GetWidth () == getImage(L"FreeFileSync").GetHeight() && getImage(L"FreeFileSync").GetWidth() % 128 == 0); //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes diff --git a/lib/resources.h b/lib/resources.h index df651eaa..1cb05f20 100644 --- a/lib/resources.h +++ b/lib/resources.h @@ -22,7 +22,7 @@ public: //global image resource objects wxAnimation aniWink; - wxAnimation aniSync; + wxAnimation aniWorking; wxIcon programIconFFS; private: diff --git a/lib/shadow.cpp b/lib/shadow.cpp index 4bb299ac..1161646e 100644 --- a/lib/shadow.cpp +++ b/lib/shadow.cpp @@ -53,20 +53,20 @@ public: //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 Volume Shadow Copy Service.") + L"\n" + + throw FileError(_("Cannot access 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 Volume Shadow Copy Service.") + L"\n" + + throw FileError(_("Cannot access 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 Volume Shadow Copy Service.") + L"\n" + - getLastError() + L" Volume: " + fmtFileName(volumeNamePf)); + throw FileError(_("Cannot access Volume Shadow Copy Service."), + getLastError() + std::wstring(L" Volume: ") + fmtFileName(volumeNamePf)); shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash } @@ -96,7 +96,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function //try to resolve symlinks and junctions: //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked! - //2. junctions: C:\Users\<username> is a junction that may link to e.g. D:\Users\<username>, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y!" + //2. junctions: C:\Users\<username> is a junction that may link to e.g. D:\Users\<username>, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y." if (wereVistaOrLater) filenameFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later! //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix @@ -106,7 +106,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function if (!::GetVolumePathName(filenameFinal.c_str(), //__in LPCTSTR lpszFileName, &volBuffer[0], //__out LPTSTR lpszVolumePathName, bufferSize)) //__in DWORD cchBufferLength - throw FileError(replaceCpy(_("Path %x does not contain a volume name."), L"%x", fmtFileName(filenameFinal))); + 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! @@ -114,7 +114,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider for example \\?\ prefix! if (pos == Zstring::npos) { - std::wstring msg = _("Volume name %x not part of file name %y!"); + std::wstring msg = _("Volume name %x not part of file name %y."); replace(msg, L"%x", fmtFileName(volumeNamePf), false); replace(msg, L"%y", fmtFileName(filenameFinal), false); throw FileError(msg); diff --git a/lib/shadow.h b/lib/shadow.h index f59d7753..36c72c25 100644 --- a/lib/shadow.h +++ b/lib/shadow.h @@ -13,7 +13,6 @@ #include <zen/zstring.h> #include <zen/file_error.h> - namespace shadow { class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as needed diff --git a/lib/versioning.cpp b/lib/versioning.cpp index a5bd17be..1bf5a65e 100644 --- a/lib/versioning.cpp +++ b/lib/versioning.cpp @@ -73,7 +73,7 @@ namespace - create target super directories if missing */ template <class Function> -void moveItemToVersioning(const Zstring& sourceObj, //throw FileError +void moveItemToVersioning(const Zstring& fullName, //throw FileError const Zstring& relativeName, const Zstring& versioningDirectory, const Zstring& timestamp, @@ -82,37 +82,36 @@ void moveItemToVersioning(const Zstring& sourceObj, //throw FileError { assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); assert(!endsWith (relativeName, FILE_NAME_SEPARATOR)); - assert(endsWith(sourceObj, relativeName)); //usually, yes, but we might relax this in the future - Zstring targetObj; + Zstring targetName; switch (versioningStyle) { case VER_STYLE_REPLACE: - targetObj = appendSeparator(versioningDirectory) + relativeName; + targetName = appendSeparator(versioningDirectory) + relativeName; break; case VER_STYLE_ADD_TIMESTAMP: //assemble time-stamped version name - targetObj = appendSeparator(versioningDirectory) + relativeName + Zstr(' ') + timestamp + getExtension(relativeName); - assert(impl::isMatchingVersion(afterLast(relativeName, FILE_NAME_SEPARATOR), afterLast(targetObj, FILE_NAME_SEPARATOR))); //paranoid? no! + 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(sourceObj, targetObj); //throw FileError + moveObj(fullName, targetName); //throw FileError } catch (FileError&) //expected to fail if target directory is not yet existing! { - if (!somethingExists(sourceObj)) //no source at all is not an error (however a directory as source when a file is expected, *is* an error!) + 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(targetObj, FILE_NAME_SEPARATOR); + const Zstring targetDir = beforeLast(targetName, FILE_NAME_SEPARATOR); if (!dirExists(targetDir)) //->(minor) file system race condition! { makeDirectory(targetDir); //throw FileError - moveObj(sourceObj, targetObj); //throw FileError -> this should work now! + moveObj(fullName, targetName); //throw FileError -> this should work now! } else throw; @@ -126,36 +125,45 @@ void moveItemToVersioning(const Zstring& sourceObj, //throw FileError template <class Function> void moveObject(const Zstring& sourceFile, //throw FileError const Zstring& targetFile, - Function copyDelete) //fallback if move failed; may throw FileError + Function copyDelete) //throw FileError; fallback if move failed { assert(!dirExists(sourceFile) || symlinkExists(sourceFile)); //we process files and symlinks only + auto removeTarget = [&]() + { + //remove target object + if (fileExists(targetFile)) //file or symlink + removeFile(targetFile); //throw FileError + else if (dirExists(targetFile)) //directory or symlink + removeDirectory(targetFile); //throw FileError + //we do not expect targetFile to be a directory in general => no callback required + else assert(false); + }; + //first try to move directly without copying - bool targetExisting = false; 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&) {} - catch (const ErrorTargetExisting&) { targetExisting = true; } - - if (!targetExisting) - targetExisting = somethingExists(targetFile); - - //remove target object - if (targetExisting) + catch (const ErrorDifferentVolume&) { - if (fileExists(targetFile)) //file or symlink - removeFile(targetFile); //throw FileError - else if (dirExists(targetFile)) //directory or symlink - removeDirectory(targetFile); //throw FileError - //we do not expect targetFile to be a directory in general => no callback required - else assert(false); + removeTarget(); //throw FileError + copyDelete(); // + } + catch (const ErrorTargetExisting&) + { + removeTarget(); //throw FileError + try + { + renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + } + catch (const ErrorDifferentVolume&) + { + copyDelete(); //throw FileError + } } - - copyDelete(); } @@ -219,7 +227,7 @@ private: return LINK_SKIP; } - virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) + virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) { dirs_.push_back(shortName); return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively! @@ -234,7 +242,7 @@ private: } -bool FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError +bool FileVersioner::revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError { struct CallbackMoveFileImpl : public CallbackMoveDir { @@ -246,15 +254,15 @@ bool FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relat CallbackMoveFile& callback_; } cb(callback); - return revisionFileImpl(sourceFile, relativeName, cb); //throw FileError + return revisionFileImpl(fullName, relativeName, cb); //throw FileError } -bool FileVersioner::revisionFileImpl(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +bool FileVersioner::revisionFileImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError { bool moveSuccessful = false; - moveItemToVersioning(sourceFile, //throw FileError + moveItemToVersioning(fullName, //throw FileError relativeName, versioningDirectory_, timeStamp_, @@ -280,23 +288,23 @@ bool FileVersioner::revisionFileImpl(const Zstring& sourceFile, const Zstring& r } -void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +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(sourceDir)) + if (!somethingExists(fullName)) return; //neither directory nor any other object (e.g. broken symlink) with that name existing - revisionDirImpl(sourceDir, relativeName, callback); //throw FileError + revisionDirImpl(fullName, relativeName, callback); //throw FileError } -void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +void FileVersioner::revisionDirImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError { - assert(somethingExists(sourceDir)); //[!] + assert(somethingExists(fullName)); //[!] //create target - if (symlinkExists(sourceDir)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! + 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(sourceDir, //throw FileError + moveItemToVersioning(fullName, //throw FileError relativeName, versioningDirectory_, timeStamp_, @@ -310,7 +318,7 @@ void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& rel else { assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); - assert(endsWith(sourceDir, relativeName)); //usually, yes, but we might relax this in the future + 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 @@ -320,17 +328,17 @@ void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& rel std::vector<Zstring> dirList; // { TraverseFilesOneLevel tol(fileList, dirList); //throw FileError - traverseFolder(sourceDir, tol); // + traverseFolder(fullName, tol); // } - const Zstring sourceDirPf = appendSeparator(sourceDir); + const Zstring fullNamePf = appendSeparator(fullName); const Zstring relnamePf = appendSeparator(relativeName); //move files std::for_each(fileList.begin(), fileList.end(), [&](const Zstring& shortname) { - revisionFileImpl(sourceDirPf + shortname, //throw FileError + revisionFileImpl(fullNamePf + shortname, //throw FileError relnamePf + shortname, callback); }); @@ -339,14 +347,14 @@ void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& rel std::for_each(dirList.begin(), dirList.end(), [&](const Zstring& shortname) { - revisionDirImpl(sourceDirPf + shortname, //throw FileError + revisionDirImpl(fullNamePf + shortname, //throw FileError relnamePf + shortname, callback); }); //delete source - callback.onBeforeDirMove(sourceDir, targetDir); - removeDirectory(sourceDir); //throw FileError + callback.onBeforeDirMove(fullName, targetDir); + removeDirectory(fullName); //throw FileError } } @@ -427,7 +435,7 @@ void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileEr } catch (FileError&) { -#ifdef FFS_WIN //if it's a directory symlink: +#ifdef ZEN_WIN //if it's a directory symlink: if (symlinkExists(fullnameVer) && dirExists(fullnameVer)) removeDirectory(fullnameVer); //throw FileError else diff --git a/lib/versioning.h b/lib/versioning.h index faa96359..33dc31c4 100644 --- a/lib/versioning.h +++ b/lib/versioning.h @@ -46,14 +46,14 @@ public: throw FileError(_("Failure to create timestamp for versioning:") + L" \'" + timeStamp_ + L"\'"); } - bool revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError; return "false" if file is not existing - void revisionDir (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + bool revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError; return "false" if file is not existing + void revisionDir (const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError //void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning! private: - bool revisionFileImpl(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError - void revisionDirImpl (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + 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_; diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp index f5091cd6..123060f4 100644 --- a/lib/xml_base.cpp +++ b/lib/xml_base.cpp @@ -14,7 +14,7 @@ using namespace zen; //loadXmlDocument vs loadStream: //1. better error reporting //2. quick exit if (potentially large) input file is not an XML -void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw FfsXmlError +XmlDoc xmlAccess::loadXmlDocument(const Zstring& filename) //throw FfsXmlError { std::string stream; try @@ -51,7 +51,7 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff try { - zen::parse(stream, doc); //throw XmlParsingError + return zen::parse(stream); //throw XmlParsingError } catch (const XmlParsingError& e) { diff --git a/lib/xml_base.h b/lib/xml_base.h index ceaf609b..85d4dfa1 100644 --- a/lib/xml_base.h +++ b/lib/xml_base.h @@ -11,7 +11,7 @@ #include <zen/zstring.h> #include <zen/file_error.h> -//bind zenxml and zen file handling together +//bind zen::Xml and zen file handling together namespace xmlAccess { @@ -34,7 +34,7 @@ private: }; void saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename); //throw FfsXmlError -void loadXmlDocument(const Zstring& filename, zen::XmlDoc& doc); //throw FfsXmlError +zen::XmlDoc loadXmlDocument(const Zstring& filename); //throw FfsXmlError const std::wstring getErrorMessageFormatted(const std::vector<std::wstring>& failedElements); } |