summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/db_file.cpp13
-rw-r--r--lib/dir_lock.cpp8
-rw-r--r--lib/generate_logfile.h3
-rw-r--r--lib/icon_buffer.cpp5
-rw-r--r--lib/lock_holder.h28
-rw-r--r--lib/osx_file_icon.h2
-rw-r--r--lib/osx_file_icon.mm14
-rw-r--r--lib/parallel_scan.h5
-rw-r--r--lib/process_xml.cpp400
-rw-r--r--lib/process_xml.h27
-rw-r--r--lib/shadow.cpp3
-rw-r--r--lib/shadow.h3
-rw-r--r--lib/soft_filter.h4
-rw-r--r--lib/xml_base.cpp34
-rw-r--r--lib/xml_base.h2
15 files changed, 337 insertions, 214 deletions
diff --git a/lib/db_file.cpp b/lib/db_file.cpp
index 368cf56b..aa893711 100644
--- a/lib/db_file.cpp
+++ b/lib/db_file.cpp
@@ -24,7 +24,7 @@ namespace
{
//-------------------------------------------------------------------------------------------------------------------------------
const char FILE_FORMAT_DESCR[] = "FreeFileSync";
-const int FILE_FORMAT_VER = 9;
+const int DB_FILE_FORMAT_VER = 9;
//-------------------------------------------------------------------------------------------------------------------------------
typedef std::string UniqueId;
@@ -39,6 +39,7 @@ template <SelectedSide side> inline
Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false)
{
//Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity?
+ //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
@@ -63,7 +64,7 @@ void saveStreams(const StreamMapping& streamList, const Zstring& filename) //thr
writeArray(streamOut, FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR));
//save file format version
- writeNumber<std::int32_t>(streamOut, FILE_FORMAT_VER);
+ writeNumber<std::int32_t>(streamOut, DB_FILE_FORMAT_VER);
//save stream list
writeNumber<std::uint32_t>(streamOut, static_cast<std::uint32_t>(streamList.size())); //number of streams, one for each sync-pair
@@ -96,7 +97,7 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError, FileErrorD
throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename)));
const int version = readNumber<std::int32_t>(streamIn); //throw UnexpectedEndOfStreamError
- if (version != FILE_FORMAT_VER) //read file format version#
+ if (version != DB_FILE_FORMAT_VER) //read file format version#
throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename)));
//read stream lists
@@ -363,7 +364,7 @@ private:
void recurse(InSyncDir& container)
{
size_t fileCount = readNumber<std::uint32_t>(inputBoth);
- while (fileCount-- != 0) //files
+ while (fileCount-- != 0)
{
const Zstring shortName = readUtf8(inputBoth);
const auto inSyncType = static_cast<InSyncFile::InSyncType>(readNumber<std::int32_t>(inputBoth));
@@ -377,7 +378,7 @@ private:
}
size_t linkCount = readNumber<std::uint32_t>(inputBoth);
- while (linkCount-- != 0) //files
+ while (linkCount-- != 0)
{
const Zstring shortName = readUtf8(inputBoth);
@@ -390,7 +391,7 @@ private:
}
size_t dirCount = readNumber<std::uint32_t>(inputBoth);
- while (dirCount-- != 0) //files
+ while (dirCount-- != 0)
{
const Zstring shortName = readUtf8(inputBoth);
diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp
index f263d4ac..328b87d3 100644
--- a/lib/dir_lock.cpp
+++ b/lib/dir_lock.cpp
@@ -98,7 +98,7 @@ public:
FILE_END)) //__in DWORD dwMoveMethod
return;
- DWORD bytesWritten = 0;
+ DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx
/*bool rv = */
::WriteFile(fileHandle, //__in HANDLE hFile,
buffer, //__out LPVOID lpBuffer,
@@ -375,7 +375,7 @@ LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError,
}
catch (UnexpectedEndOfStreamError&)
{
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(lockfilename)));
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(lockfilename)) + L" (unexpected end of stream)");
}
}
@@ -535,7 +535,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 set directory lock %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + zen::getLastErrorFormatted());
}
::CloseHandle(fileHandle);
@@ -550,7 +550,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError
if (errno == EEXIST)
return false;
else
- throw FileError(replaceCpy(_("Cannot set directory lock %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + zen::getLastErrorFormatted());
}
::close(fileHandle);
#endif
diff --git a/lib/generate_logfile.h b/lib/generate_logfile.h
index 875d5b98..c441de66 100644
--- a/lib/generate_logfile.h
+++ b/lib/generate_logfile.h
@@ -119,7 +119,6 @@ inline
void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
const ErrorLog& log,
size_t maxBytesToWrite) //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems!
-
{
const Zstring filename = getConfigDir() + Zstr("LastSyncs.log");
@@ -127,7 +126,7 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier
newStream += LINE_BREAK;
- //write log items one after the other instead of creating one big string: memory allocation might fail; think 1 million entries!
+ //check size of "newStream": memory allocation might fail - think 1 million entries!
for (auto iter = log.begin(); iter != log.end(); ++iter)
{
newStream += replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(*iter)), '\n', LINE_BREAK);
diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp
index dd80f6dd..3912849e 100644
--- a/lib/icon_buffer.cpp
+++ b/lib/icon_buffer.cpp
@@ -359,11 +359,12 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz)
sizeof(fileInfo), //UINT cbFileInfo,
SHGFI_SYSICONINDEX | SHGFI_ATTRIBUTES)) //UINT uFlags
{
+ (void)imgList;
//imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay
//another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP
- //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as
- // needed; for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList."
+ //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as needed;
+ // for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList."
//http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx
#ifndef SFGAO_LINK //Shobjidl.h
diff --git a/lib/lock_holder.h b/lib/lock_holder.h
index 9cde59a7..81b5632d 100644
--- a/lib/lock_holder.h
+++ b/lib/lock_holder.h
@@ -1,31 +1,29 @@
-#ifndef LOCK_HOLDER_H_INCLUDED
-#define LOCK_HOLDER_H_INCLUDED
+#ifndef LOCK_HOLDER_H_489572039485723453425
+#define LOCK_HOLDER_H_489572039485723453425
-#include <map>
+#include <set>
#include <zen/zstring.h>
#include <zen/stl_tools.h>
#include "dir_lock.h"
#include "status_handler.h"
-#include "dir_exist_async.h"
+//#include "dir_exist_async.h"
namespace zen
{
const Zstring LOCK_FILE_ENDING = Zstr(".ffs_lock"); //intermediate locks created by DirLock use this extension, too!
//hold locks for a number of directories without blocking during lock creation
+//call after having checked directory existence!
class LockHolder
{
public:
- LockHolder(const std::vector<Zstring>& dirnamesFmt, //resolved dirname ending with path separator
- ProcessCallback& procCallback,
- bool allowUserInteraction)
+ LockHolder(const std::set<Zstring, LessFilename>& dirnamesExisting, //resolved dirname ending with path separator
+ bool& warningDirectoryLockFailed,
+ ProcessCallback& procCallback)
{
- std::set<Zstring, LessFilename> existingDirs = getExistingDirsUpdating(dirnamesFmt, allowUserInteraction, procCallback);
-
- for (auto it = existingDirs.begin(); it != existingDirs.end(); ++it)
+ for (auto it = dirnamesExisting.begin(); it != dirnamesExisting.end(); ++it)
{
const Zstring& dirnameFmt = *it;
-
assert(endsWith(dirnameFmt, FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution
class WaitOnLockHandler : public DirLockCallback
@@ -45,8 +43,8 @@ public:
}
catch (const FileError& e)
{
- bool dummy = false; //this warning shall not be shown but logged only
- procCallback.reportWarning(e.toString(), dummy); //may throw!
+ const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtFileName(dirnameFmt)) + L"\n\n" + e.toString();
+ procCallback.reportWarning(msg, warningDirectoryLockFailed); //may throw!
}
}
}
@@ -54,8 +52,6 @@ public:
private:
std::vector<DirLock> lockHolder;
};
-
}
-
-#endif // LOCK_HOLDER_H_INCLUDED
+#endif //LOCK_HOLDER_H_489572039485723453425
diff --git a/lib/osx_file_icon.h b/lib/osx_file_icon.h
index 1d57a00e..e9b17988 100644
--- a/lib/osx_file_icon.h
+++ b/lib/osx_file_icon.h
@@ -14,7 +14,7 @@ namespace osx
{
struct ImageData
{
- ImageData(int w, int h) : width(w), height(h), rgb(w * h * 3), alpha(w * h) {}
+ ImageData(int w, int h) : width(w), height(h), rgb(w* h * 3), alpha(w* h) {}
ImageData(ImageData&& tmp) : width(tmp.width), height(tmp.height) { rgb.swap(tmp.rgb); alpha.swap(tmp.alpha); }
const int width;
diff --git a/lib/osx_file_icon.mm b/lib/osx_file_icon.mm
index 6a068998..11fb053f 100644
--- a/lib/osx_file_icon.mm
+++ b/lib/osx_file_icon.mm
@@ -31,8 +31,8 @@ osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw OsxError;
CGImageRef imgRef = [nsImg CGImageForProposedRect:&rectProposed context:nil hints:nil];
ZEN_OSX_ASSERT(imgRef != NULL); //can this fail? not documented; ownership?? not documented!
- const size_t width = CGImageGetWidth (imgRef);
- const size_t height = CGImageGetHeight(imgRef);
+ const size_t width = ::CGImageGetWidth (imgRef);
+ const size_t height = ::CGImageGetHeight(imgRef);
ZEN_OSX_ASSERT(width > 0 && height > 0 && requestedSize > 0);
@@ -46,14 +46,14 @@ osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw OsxError;
trgHeight = height * requestedSize / maxExtent;
}
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGColorSpaceRef colorSpace = ::CGColorSpaceCreateDeviceRGB();
ZEN_OSX_ASSERT(colorSpace != NULL); //may fail
- ZEN_ON_SCOPE_EXIT(CGColorSpaceRelease(colorSpace));
+ ZEN_ON_SCOPE_EXIT(::CGColorSpaceRelease(colorSpace));
std::vector<unsigned char> buf(trgWidth* trgHeight * 4); //32-bit ARGB, little endian byte order -> already initialized with 0 = fully transparent
//supported color spaces: https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
- CGContextRef ctxRef = CGBitmapContextCreate(&buf[0], //void *data,
+ CGContextRef ctxRef = ::CGBitmapContextCreate(&buf[0], //void *data,
trgWidth, //size_t width,
trgHeight, //size_t height,
8, //size_t bitsPerComponent,
@@ -61,9 +61,9 @@ osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw OsxError;
colorSpace, //CGColorSpaceRef colorspace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); //CGBitmapInfo bitmapInfo
ZEN_OSX_ASSERT(ctxRef != NULL);
- ZEN_ON_SCOPE_EXIT(CGContextRelease(ctxRef));
+ ZEN_ON_SCOPE_EXIT(::CGContextRelease(ctxRef));
- CGContextDrawImage(ctxRef, CGRectMake(0, 0, trgWidth, trgHeight), imgRef); //can this fail? not documented
+ ::CGContextDrawImage(ctxRef, CGRectMake(0, 0, trgWidth, trgHeight), imgRef); //can this fail? not documented
//CGContextFlush(ctxRef); //"If you pass [...] a bitmap context, this function does nothing."
diff --git a/lib/parallel_scan.h b/lib/parallel_scan.h
index f04d51b4..5a52e44e 100644
--- a/lib/parallel_scan.h
+++ b/lib/parallel_scan.h
@@ -35,8 +35,9 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs)
if (lhs.handleSymlinks_ != rhs.handleSymlinks_)
return lhs.handleSymlinks_ < rhs.handleSymlinks_;
- if (!EqualFilename()(lhs.dirnameFull_, rhs.dirnameFull_))
- return LessFilename()(lhs.dirnameFull_, rhs.dirnameFull_);
+ const int cmpName = cmpFileName(lhs.dirnameFull_, rhs.dirnameFull_);
+ if (cmpName != 0)
+ return cmpName < 0;
return *lhs.filter_ < *rhs.filter_;
}
diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp
index d41afc74..4974dcbc 100644
--- a/lib/process_xml.cpp
+++ b/lib/process_xml.cpp
@@ -1,4 +1,4 @@
-// **************************************************************************
+// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
@@ -17,6 +17,14 @@ using namespace xmlAccess; //functionally needed for correct overload resolution
using namespace std::rel_ops;
+namespace
+{
+//-------------------------------------------------------------------------------------------------------------------------------
+const int XML_FORMAT_VER_GLOBAL = 1;
+const int XML_FORMAT_VER_FFS_GUI = 1;
+const int XML_FORMAT_VER_FFS_BATCH = 1;
+//-------------------------------------------------------------------------------------------------------------------------------
+}
XmlType getXmlType(const zen::XmlDoc& doc) //throw()
{
@@ -73,9 +81,9 @@ void setXmlType(XmlDoc& doc, XmlType type) //throw()
}
//################################################################################################################
-wxString xmlAccess::getGlobalConfigFile()
+Zstring xmlAccess::getGlobalConfigFile()
{
- return utfCvrtTo<wxString>(zen::getConfigDir()) + L"GlobalSettings.xml";
+ return zen::getConfigDir() + Zstr("GlobalSettings.xml");
}
@@ -88,6 +96,8 @@ void xmlAccess::OptionalDialogs::resetDialogs()
warningUnresolvedConflicts = true;
warningDatabaseError = true;
warningRecyclerMissing = true;
+ warningInputFieldEmpty = true;
+ warningDirectoryLockFailed = true;
popupOnConfigChange = true;
confirmSyncStart = true;
}
@@ -180,67 +190,21 @@ xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<Zstring>& filenam
namespace
{
-template <class XmlCfg>
-XmlCfg readConfigNoWarnings(const Zstring& filename, std::unique_ptr<FfsXmlError>& warning) //throw FfsXmlError, but only if "FATAL"
+std::vector<Zstring> splitFilterByLines(const Zstring& filterPhrase)
{
- XmlCfg cfg;
- try
- {
- readConfig(filename, cfg); //throw xmlAccess::FfsXmlError
- }
- catch (const FfsXmlError& e)
- {
- if (e.getSeverity() == FfsXmlError::FATAL)
- throw;
- else if (!warning.get()) warning = make_unique<FfsXmlError>(e);
- }
- return cfg;
+ if (filterPhrase.empty())
+ return std::vector<Zstring>();
+ return split(filterPhrase, Zstr('\n'));
}
-}
-
-void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config) //throw FfsXmlError
+Zstring mergeFilterLines(const std::vector<Zstring>& filterLines)
{
- assert(!filenames.empty());
-
- std::vector<zen::MainConfiguration> mainCfgs;
- std::unique_ptr<FfsXmlError> savedWarning;
-
- for (auto it = filenames.begin(); it != filenames.end(); ++it)
- {
- const Zstring& filename = *it;
- const bool firstLine = it == filenames.begin(); //init all non-"mainCfg" settings with first config file
-
- switch (getXmlType(filename))
- {
- case XML_TYPE_GUI:
- if (firstLine)
- config = readConfigNoWarnings<XmlGuiConfig>(filename, savedWarning);
- else
- mainCfgs.push_back(readConfigNoWarnings<XmlGuiConfig>(filename, savedWarning).mainCfg); //throw FfsXmlError
- break;
-
- case XML_TYPE_BATCH:
- if (firstLine)
- config = convertBatchToGui(readConfigNoWarnings<XmlBatchConfig>(filename, savedWarning));
- else
- mainCfgs.push_back(readConfigNoWarnings<XmlBatchConfig>(filename, savedWarning).mainCfg); //throw FfsXmlError
- break;
-
- case XML_TYPE_GLOBAL:
- case XML_TYPE_OTHER:
- if (!fileExists(filename))
- throw FfsXmlError(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)));
- else
- 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);
-
- if (savedWarning.get()) //"re-throw" exception
- throw* savedWarning;
+ if (filterLines.empty())
+ return Zstring();
+ Zstring out = filterLines[0];
+ std::for_each(filterLines.begin() + 1, filterLines.end(), [&](const Zstring& line) { out += Zstr('\n'); out += line; });
+ return out;
+}
}
@@ -594,6 +558,9 @@ void writeText(const ColumnTypeNavi& value, std::string& output)
case COL_TYPE_NAVI_DIRECTORY:
output = "Tree";
break;
+ case COL_TYPE_NAVI_ITEM_COUNT:
+ output = "Count";
+ break;
}
}
@@ -606,6 +573,8 @@ bool readText(const std::string& input, ColumnTypeNavi& value)
value = COL_TYPE_NAVI_BYTES;
else if (tmp == "Tree")
value = COL_TYPE_NAVI_DIRECTORY;
+ else if (tmp == "Count")
+ value = COL_TYPE_NAVI_ITEM_COUNT;
else
return false;
return true;
@@ -870,8 +839,33 @@ void readConfig(const XmlIn& in, SyncConfig& syncCfg)
void readConfig(const XmlIn& in, FilterConfig& filter)
{
- in["Include"](filter.includeFilter);
- in["Exclude"](filter.excludeFilter);
+ warn_static("remove after migration?")
+ auto haveFilterAsSingleString = [&]() -> bool
+ {
+ if (in["Include"])
+ if (auto elem = in["Include"].get())
+ {
+ std::string tmp;
+ if (elem->getValue(tmp))
+ return !tmp.empty();
+ }
+ return false;
+ };
+ if (haveFilterAsSingleString()) //obsolete style
+ {
+ in["Include"](filter.includeFilter);
+ in["Exclude"](filter.excludeFilter);
+ }
+ else
+ {
+ std::vector<Zstring> tmp = splitFilterByLines(filter.includeFilter); //default value
+ in["Include"](tmp);
+ filter.includeFilter = mergeFilterLines(tmp);
+
+ std::vector<Zstring> tmp2 = splitFilterByLines(filter.excludeFilter); //default value
+ in["Exclude"](tmp2);
+ filter.excludeFilter = mergeFilterLines(tmp2);
+ }
in["TimeSpan"](filter.timeSpan);
warn_static("remove after migration?")
@@ -942,15 +936,15 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg)
//read all folder pairs
mainCfg.additionalPairs.clear();
- bool firstIter = true;
+ bool firstItem = true;
for (XmlIn inPair = inMain["FolderPairs"]["Pair"]; inPair; inPair.next())
{
FolderPairEnh newPair;
readConfig(inPair, newPair);
- if (firstIter)
+ if (firstItem)
{
- firstIter = false;
+ firstItem = false;
mainCfg.firstPair = newPair; //set first folder pair
}
else
@@ -966,7 +960,7 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg)
void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config)
{
- ::readConfig(in, config.mainCfg); //read main config
+ readConfig(in, config.mainCfg); //read main config
//read GUI specific config data
XmlIn inGuiCfg = in["GuiConfig"];
@@ -998,7 +992,7 @@ void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config)
void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config)
{
- ::readConfig(in, config.mainCfg); //read main config
+ readConfig(in, config.mainCfg); //read main config
//read GUI specific config data
XmlIn inBatchCfg = in["BatchConfig"];
@@ -1021,31 +1015,29 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
{
XmlIn inShared = in["Shared"];
- //try to read program language setting
- inShared["Language"](config.programLanguage);
-
- inShared["CopyLockedFiles" ](config.copyLockedFiles);
- inShared["CopyFilePermissions" ](config.copyFilePermissions);
- inShared["TransactionalFileCopy"](config.transactionalFileCopy);
- inShared["LockDirectoriesDuringSync"](config.createLockFile);
- inShared["VerifyCopiedFiles" ](config.verifyFileCopy);
- inShared["RunWithBackgroundPriority"](config.runWithBackgroundPriority);
+ inShared["Language"].attribute("id", config.programLanguage);
- //max. allowed file time deviation
- inShared["FileTimeTolerance"](config.fileTimeTolerance);
-
- inShared["LastSyncsFileSizeMax"](config.lastSyncsLogFileSizeMax);
+ inShared["FailSafeFileCopy" ].attribute("Enabled", config.transactionalFileCopy);
+ inShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles);
+ inShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions);
+ inShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority);
+ inShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile);
+ inShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy);
+ inShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance);
+ inShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax);
XmlIn inOpt = inShared["OptionalDialogs"];
- inOpt["WarnUnresolvedConflicts" ](config.optDialogs.warningUnresolvedConflicts);
- inOpt["WarnNotEnoughDiskSpace" ](config.optDialogs.warningNotEnoughDiskSpace);
- inOpt["WarnSignificantDifference" ](config.optDialogs.warningSignificantDifference);
- inOpt["WarnRecycleBinNotAvailable" ](config.optDialogs.warningRecyclerMissing);
- inOpt["WarnDatabaseError" ](config.optDialogs.warningDatabaseError);
- inOpt["WarnDependentFolders" ](config.optDialogs.warningDependentFolders);
- inOpt["WarnFolderPairRaceCondition"](config.optDialogs.warningFolderPairRaceCondition);
- inOpt["PromptSaveConfig" ](config.optDialogs.popupOnConfigChange);
- inOpt["ConfirmSyncStart" ](config.optDialogs.confirmSyncStart);
+ inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
+ inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace);
+ inOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference);
+ inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing);
+ inOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty);
+ inOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError);
+ inOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders);
+ inOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition);
+ inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
+ inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
+ inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
//gui specific global settings (optional)
XmlIn inGui = in["Gui"];
@@ -1062,24 +1054,25 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
//inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides);
inManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion);
- inWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase);
- inWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible);
+ inWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase);
+ inWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible);
//###########################################################
+
+ XmlIn inOverview = inWnd["OverviewPanel"];
+ inOverview.attribute("ShowPercentage", config.gui.showPercentBar);
+ inOverview.attribute("SortByColumn", config.gui.naviLastSortColumn);
+ inOverview.attribute("SortAscending", config.gui.naviLastSortAscending);
+
//read column attributes
- XmlIn inColNavi = inWnd["OverviewColumns"];
+ XmlIn inColNavi = inOverview["Columns"];
inColNavi(config.gui.columnAttribNavi);
- inColNavi.attribute("ShowPercentage", config.gui.showPercentBar);
- inColNavi.attribute("SortByColumn", config.gui.naviLastSortColumn);
- inColNavi.attribute("SortAscending", config.gui.naviLastSortAscending);
-
XmlIn inMainGrid = inWnd["MainGrid"];
inMainGrid.attribute("ShowIcons", config.gui.showIcons);
inMainGrid.attribute("IconSize", config.gui.iconSize);
inMainGrid.attribute("SashOffset", config.gui.sashOffset);
-
XmlIn inColLeft = inMainGrid["ColumnsLeft"];
inColLeft(config.gui.columnAttribLeft);
@@ -1088,11 +1081,14 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
//###########################################################
inWnd["ViewFilterDefault"](config.gui.viewFilterDefault);
- inWnd["Layout" ](config.gui.guiPerspectiveLast);
+ inWnd["Perspective" ](config.gui.guiPerspectiveLast);
- //load config file history
- inGui["LastUsedConfig"](config.gui.lastUsedConfigFiles);
+ std::vector<Zstring> tmp = splitFilterByLines(config.gui.defaultExclusionFilter); //default value
+ inGui["DefaultExclusionFilter"](tmp);
+ config.gui.defaultExclusionFilter = mergeFilterLines(tmp);
+ //load config file history
+ inGui["LastUsedConfig"](config.gui.lastUsedConfigFiles);
inGui["ConfigHistory"](config.gui.cfgFileHistory);
inGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax);
@@ -1114,44 +1110,146 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
}
+bool needsMigration(const XmlDoc& doc, int currentXmlFormatVer)
+{
+ //(try to) migrate old configuration if needed
+ int xmlFormatVer = 0;
+ /*bool success = */doc.root().getAttribute("XmlFormat", xmlFormatVer);
+ return xmlFormatVer < currentXmlFormatVer;
+}
+
+
template <class ConfigType>
-void readConfig(const Zstring& filename, XmlType type, ConfigType& config)
+void readConfig(const Zstring& filename, XmlType type, ConfigType& cfg, int currentXmlFormatVer, bool& needMigration) //throw FfsXmlError
{
XmlDoc doc;
loadXmlDocument(filename, doc); //throw FfsXmlError
-
+
if (getXmlType(doc) != type) //throw()
throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)));
XmlIn in(doc);
- ::readConfig(in, config);
+ ::readConfig(in, cfg);
if (in.errorsOccured())
throw FfsXmlError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" +
- getErrorMessageFormatted(in), FfsXmlError::WARNING);
+ getErrorMessageFormatted(in.getErrorsAs<std::wstring>()), FfsXmlError::WARNING);
+
+ //(try to) migrate old configuration if needed
+ needMigration = needsMigration(doc, currentXmlFormatVer);
}
}
-void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlGuiConfig& config)
+void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlGuiConfig& cfg)
+{
+ bool needMigration = false;
+ ::readConfig(filename, XML_TYPE_GUI, cfg, XML_FORMAT_VER_FFS_GUI, needMigration); //throw FfsXmlError
+
+ if (needMigration) //(try to) migrate old configuration
+ try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ }
+ catch (FfsXmlError&) { assert(false); } //don't bother user!
+}
+
+
+void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlBatchConfig& cfg)
{
- ::readConfig(filename, XML_TYPE_GUI, config);
+ bool needMigration = false;
+ ::readConfig(filename, XML_TYPE_BATCH, cfg, XML_FORMAT_VER_FFS_BATCH, needMigration); //throw FfsXmlError
+
+ if (needMigration) //(try to) migrate old configuration
+ try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ }
+ catch (FfsXmlError&) { assert(false); } //don't bother user!
}
-void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlBatchConfig& config)
+void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& cfg)
{
- ::readConfig(filename, XML_TYPE_BATCH, config);
+ bool needMigration = false;
+ ::readConfig(getGlobalConfigFile(), XML_TYPE_GLOBAL, cfg, XML_FORMAT_VER_GLOBAL, needMigration); //throw FfsXmlError
}
-void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config)
+namespace
+{
+template <class XmlCfg>
+XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filename, int currentXmlFormatVer, std::unique_ptr<FfsXmlError>& warning) //nothrow
{
- ::readConfig(utfCvrtTo<Zstring>(getGlobalConfigFile()), XML_TYPE_GLOBAL, config);
+ XmlCfg cfg;
+ XmlIn in(doc);
+ ::readConfig(in, cfg);
+
+ if (in.errorsOccured())
+ {
+ if (!warning)
+ warning = make_unique<FfsXmlError>(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" +
+ getErrorMessageFormatted(in.getErrorsAs<std::wstring>()), FfsXmlError::WARNING);
+ }
+ else
+ {
+ //(try to) migrate old configuration if needed
+ if (needsMigration(doc, currentXmlFormatVer))
+ try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ }
+ catch (FfsXmlError&) { assert(false); } //don't bother user!
+ }
+ return cfg;
+}
}
+void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config) //throw FfsXmlError
+{
+ assert(!filenames.empty());
+
+ std::vector<zen::MainConfiguration> mainCfgs;
+ std::unique_ptr<FfsXmlError> warning;
+
+ for (auto it = filenames.begin(); it != filenames.end(); ++it)
+ {
+ const Zstring& filename = *it;
+ const bool firstItem = it == filenames.begin(); //init all non-"mainCfg" settings with first config file
+
+ XmlDoc doc;
+ loadXmlDocument(filename, doc); //throw FfsXmlError
+ //do NOT use zen::loadStream as it will superfluously load even huge files!
+
+ switch (::getXmlType(doc))
+ {
+ case XML_TYPE_GUI:
+ {
+ XmlGuiConfig guiCfg = parseConfig<XmlGuiConfig>(doc, filename, XML_FORMAT_VER_FFS_GUI, warning); //nothrow
+ if (firstItem)
+ config = guiCfg;
+ else
+ mainCfgs.push_back(guiCfg.mainCfg);
+ }
+ break;
+
+ case XML_TYPE_BATCH:
+ {
+ XmlBatchConfig batchCfg = parseConfig<XmlBatchConfig>(doc, filename, XML_FORMAT_VER_FFS_BATCH, warning); //nothrow
+ if (firstItem)
+ config = convertBatchToGui(batchCfg);
+ else
+ mainCfgs.push_back(batchCfg.mainCfg);
+ }
+ break;
+
+ case XML_TYPE_GLOBAL:
+ case XML_TYPE_OTHER:
+ throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)));
+ }
+ }
+ mainCfgs.push_back(config.mainCfg); //save cfg from first line
+
+ config.mainCfg = merge(mainCfgs);
+
+ if (warning)
+ throw* warning;
+}
+
//################################################################################################
+
namespace
{
void writeConfig(const CompConfig& cmpConfig, XmlOut& out)
@@ -1188,8 +1286,8 @@ void writeConfig(const SyncConfig& syncCfg, XmlOut& out)
void writeConfig(const FilterConfig& filter, XmlOut& out)
{
- out["Include"](filter.includeFilter);
- out["Exclude"](filter.excludeFilter);
+ out["Include"](splitFilterByLines(filter.includeFilter));
+ out["Exclude"](splitFilterByLines(filter.excludeFilter));
out["TimeSpan"](filter.timeSpan);
out["TimeSpan"].attribute("Type", filter.unitTimeSpan);
@@ -1300,31 +1398,29 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
{
XmlOut outShared = out["Shared"];
- //write program language setting
- outShared["Language"](config.programLanguage);
+ outShared["Language"].attribute("id", config.programLanguage);
- outShared["CopyLockedFiles" ](config.copyLockedFiles);
- outShared["CopyFilePermissions" ](config.copyFilePermissions);
- outShared["TransactionalFileCopy"](config.transactionalFileCopy);
- outShared["LockDirectoriesDuringSync"](config.createLockFile);
- outShared["VerifyCopiedFiles" ](config.verifyFileCopy);
- outShared["RunWithBackgroundPriority"](config.runWithBackgroundPriority);
-
- //max. allowed file time deviation
- outShared["FileTimeTolerance"](config.fileTimeTolerance);
-
- outShared["LastSyncsFileSizeMax"](config.lastSyncsLogFileSizeMax);
+ outShared["FailSafeFileCopy" ].attribute("Enabled", config.transactionalFileCopy);
+ outShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles);
+ outShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions);
+ outShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority);
+ outShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile);
+ outShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy);
+ outShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance);
+ outShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax);
XmlOut outOpt = outShared["OptionalDialogs"];
- outOpt["WarnUnresolvedConflicts" ](config.optDialogs.warningUnresolvedConflicts);
- outOpt["WarnNotEnoughDiskSpace" ](config.optDialogs.warningNotEnoughDiskSpace);
- outOpt["WarnSignificantDifference" ](config.optDialogs.warningSignificantDifference);
- outOpt["WarnRecycleBinNotAvailable" ](config.optDialogs.warningRecyclerMissing);
- outOpt["WarnDatabaseError" ](config.optDialogs.warningDatabaseError);
- outOpt["WarnDependentFolders" ](config.optDialogs.warningDependentFolders);
- outOpt["WarnFolderPairRaceCondition"](config.optDialogs.warningFolderPairRaceCondition);
- outOpt["PromptSaveConfig" ](config.optDialogs.popupOnConfigChange);
- outOpt["ConfirmSyncStart" ](config.optDialogs.confirmSyncStart);
+ outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
+ outOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace);
+ outOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference);
+ outOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing);
+ outOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty);
+ outOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError);
+ outOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders);
+ outOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition);
+ outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
+ outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
+ outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
//gui specific global settings (optional)
XmlOut outGui = out["Gui"];
@@ -1341,18 +1437,20 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
//outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides);
outManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion);
- outWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase);
- outWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible);
+ outWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase);
+ outWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible);
//###########################################################
+
+ XmlOut outOverview = outWnd["OverviewPanel"];
+ outOverview.attribute("ShowPercentage", config.gui.showPercentBar);
+ outOverview.attribute("SortByColumn", config.gui.naviLastSortColumn);
+ outOverview.attribute("SortAscending", config.gui.naviLastSortAscending);
+
//write column attributes
- XmlOut outColNavi = outWnd["OverviewColumns"];
+ XmlOut outColNavi = outOverview["Columns"];
outColNavi(config.gui.columnAttribNavi);
- outColNavi.attribute("ShowPercentage", config.gui.showPercentBar);
- outColNavi.attribute("SortByColumn", config.gui.naviLastSortColumn);
- outColNavi.attribute("SortAscending", config.gui.naviLastSortAscending);
-
XmlOut outMainGrid = outWnd["MainGrid"];
outMainGrid.attribute("ShowIcons", config.gui.showIcons);
outMainGrid.attribute("IconSize", config.gui.iconSize);
@@ -1366,7 +1464,9 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
//###########################################################
outWnd["ViewFilterDefault"](config.gui.viewFilterDefault);
- outWnd["Layout" ](config.gui.guiPerspectiveLast);
+ outWnd["Perspective" ](config.gui.guiPerspectiveLast);
+
+ outGui["DefaultExclusionFilter"](splitFilterByLines(config.gui.defaultExclusionFilter));
//load config file history
outGui["LastUsedConfig"](config.gui.lastUsedConfigFiles);
@@ -1392,11 +1492,13 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
template <class ConfigType>
-void writeConfig(const ConfigType& config, XmlType type, const Zstring& filename)
+void writeConfig(const ConfigType& config, XmlType type, int xmlFormatVer, const Zstring& filename)
{
XmlDoc doc("FreeFileSync");
setXmlType(doc, type); //throw()
+ doc.root().setAttribute("XmlFormat", xmlFormatVer);
+
XmlOut out(doc);
writeConfig(config, out);
@@ -1404,21 +1506,21 @@ void writeConfig(const ConfigType& config, XmlType type, const Zstring& filename
}
}
-void xmlAccess::writeConfig(const XmlGuiConfig& config, const Zstring& filename)
+void xmlAccess::writeConfig(const XmlGuiConfig& cfg, const Zstring& filename)
{
- ::writeConfig(config, XML_TYPE_GUI, filename); //throw FfsXmlError
+ ::writeConfig(cfg, XML_TYPE_GUI, XML_FORMAT_VER_FFS_GUI, filename); //throw FfsXmlError
}
-void xmlAccess::writeConfig(const XmlBatchConfig& config, const Zstring& filename)
+void xmlAccess::writeConfig(const XmlBatchConfig& cfg, const Zstring& filename)
{
- ::writeConfig(config, XML_TYPE_BATCH, filename); //throw FfsXmlError
+ ::writeConfig(cfg, XML_TYPE_BATCH, XML_FORMAT_VER_FFS_BATCH, filename); //throw FfsXmlError
}
-void xmlAccess::writeConfig(const XmlGlobalSettings& config)
+void xmlAccess::writeConfig(const XmlGlobalSettings& cfg)
{
- ::writeConfig(config, XML_TYPE_GLOBAL, utfCvrtTo<Zstring>(getGlobalConfigFile())); //throw FfsXmlError
+ ::writeConfig(cfg, XML_TYPE_GLOBAL, XML_FORMAT_VER_GLOBAL, getGlobalConfigFile()); //throw FfsXmlError
}
diff --git a/lib/process_xml.h b/lib/process_xml.h
index 9dda330e..8a65d67a 100644
--- a/lib/process_xml.h
+++ b/lib/process_xml.h
@@ -99,6 +99,8 @@ struct OptionalDialogs
bool warningUnresolvedConflicts;
bool warningDatabaseError;
bool warningRecyclerMissing;
+ bool warningInputFieldEmpty;
+ bool warningDirectoryLockFailed;
bool popupOnConfigChange;
bool confirmSyncStart;
};
@@ -124,7 +126,7 @@ struct ViewFilterDefault
bool createLeft, createRight, updateLeft, updateRight, deleteLeft, deleteRight, doNothing; //action view
};
-wxString getGlobalConfigFile();
+Zstring getGlobalConfigFile();
struct XmlGlobalSettings
{
@@ -173,6 +175,23 @@ struct XmlGlobalSettings
cfgFileHistMax(30),
folderHistMax(15),
onCompletionHistoryMax(8),
+#ifdef FFS_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
+ defaultExclusionFilter(Zstr("/.Trash-*/") Zstr("\n")
+ Zstr("/.recycle/")),
+#elif defined FFS_MAC
+ defaultExclusionFilter(Zstr("/.fseventsd/") Zstr("\n")
+ Zstr("/.Spotlight-V100/") Zstr("\n")
+ Zstr("/.Trashes/") Zstr("\n")
+ Zstr("/._.Trashes") Zstr("\n")
+ Zstr("*/.DS_Store")),
+#endif
//deleteOnBothSides(false),
useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message
#if defined FFS_WIN || defined FFS_MAC
@@ -218,10 +237,10 @@ struct XmlGlobalSettings
ExternalApps externelApplications;
- std::vector<wxString> cfgFileHistory;
+ std::vector<Zstring> cfgFileHistory;
size_t cfgFileHistMax;
- std::vector<wxString> lastUsedConfigFiles;
+ std::vector<Zstring> lastUsedConfigFiles;
std::vector<Zstring> folderHistoryLeft;
std::vector<Zstring> folderHistoryRight;
@@ -230,6 +249,8 @@ struct XmlGlobalSettings
std::vector<std::wstring> onCompletionHistory;
size_t onCompletionHistoryMax;
+ Zstring defaultExclusionFilter;
+
//bool deleteOnBothSides;
bool useRecyclerForManualDeletion;
bool textSearchRespectCase;
diff --git a/lib/shadow.cpp b/lib/shadow.cpp
index 52b40a9e..d3106168 100644
--- a/lib/shadow.cpp
+++ b/lib/shadow.cpp
@@ -86,7 +86,7 @@ private:
//#############################################################################################################
-Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
+Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy)
{
DWORD bufferSize = 10000;
std::vector<wchar_t> volBuffer(bufferSize);
@@ -111,6 +111,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
VolNameShadowMap::const_iterator it = shadowVol.find(volumeNamePf);
if (it == shadowVol.end())
{
+ onBeforeMakeVolumeCopy(volumeNamePf); //notify client before (potentially) blocking some time
auto newEntry = std::make_shared<ShadowVolume>(volumeNamePf); //throw FileError
it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first;
}
diff --git a/lib/shadow.h b/lib/shadow.h
index cbe40dbb..4a05c860 100644
--- a/lib/shadow.h
+++ b/lib/shadow.h
@@ -9,6 +9,7 @@
#include <map>
#include <memory>
+#include <functional>
#include <zen/zstring.h>
#include <zen/file_error.h>
@@ -20,7 +21,7 @@ class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as neede
public:
ShadowCopy() {}
- Zstring makeShadowCopy(const Zstring& inputFile); //throw FileError; returns filename on shadow copy
+ Zstring makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy); //throw FileError; returns filename on shadow copy
private:
ShadowCopy(const ShadowCopy&);
diff --git a/lib/soft_filter.h b/lib/soft_filter.h
index 32e7888d..1073cd43 100644
--- a/lib/soft_filter.h
+++ b/lib/soft_filter.h
@@ -76,8 +76,8 @@ inline
SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan,
size_t sizeMin, UnitSize unitSizeMin,
size_t sizeMax, UnitSize unitSizeMax) :
- matchesFolder_(unitTimeSpan == UTIME_NONE &&
- unitSizeMin == USIZE_NONE &&
+ matchesFolder_(unitTimeSpan == UTIME_NONE&&
+ unitSizeMin == USIZE_NONE&&
unitSizeMax == USIZE_NONE) //exclude folders if size or date filter is active: avoids creating empty folders if not needed!
{
resolveUnits(timeSpan, unitTimeSpan,
diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp
index e26f73c2..f5091cd6 100644
--- a/lib/xml_base.cpp
+++ b/lib/xml_base.cpp
@@ -19,28 +19,30 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff
std::string stream;
try
{
+ FileInput inputFile(filename); //throw FileError
{
//quick test whether input is an XML: avoid loading large binary files up front!
const std::string xmlBegin = "<?xml version=";
- std::vector<char> buffer(xmlBegin.size() + sizeof(zen::BYTE_ORDER_MARK_UTF8));
-
- FileInput inputFile(filename); //throw FileError
- const size_t bytesRead = inputFile.read(&buffer[0], buffer.size()); //throw FileError
+ stream.resize(strLength(zen::BYTE_ORDER_MARK_UTF8) + xmlBegin.size());
- const std::string fileBegin(&buffer[0], bytesRead);
+ const size_t bytesRead = inputFile.read(&stream[0], stream.size()); //throw FileError
+ stream.resize(bytesRead);
- if (!startsWith(fileBegin, xmlBegin) &&
- !startsWith(fileBegin, zen::BYTE_ORDER_MARK_UTF8 + xmlBegin)) //respect BOM!
+ if (!startsWith(stream, xmlBegin) &&
+ !startsWith(stream, zen::BYTE_ORDER_MARK_UTF8 + xmlBegin)) //respect BOM!
throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)));
}
- const zen::UInt64 fs = zen::getFilesize(filename); //throw FileError
- stream.resize(to<size_t>(fs));
+ const size_t blockSize = 128 * 1024;
+ do
+ {
+ stream.resize(stream.size() + blockSize);
- FileInput inputFile(filename); //throw FileError
- const size_t bytesRead = inputFile.read(&stream[0], stream.size()); //throw FileError
- if (bytesRead < to<size_t>(fs))
- throw FfsXmlError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)));
+ const size_t bytesRead = inputFile.read(&*stream.begin() + stream.size() - blockSize, blockSize); //throw FileError
+ if (bytesRead < blockSize)
+ stream.resize(stream.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
+ }
+ while (!inputFile.eof());
}
catch (const FileError& error)
{
@@ -62,16 +64,14 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff
}
-const std::wstring xmlAccess::getErrorMessageFormatted(const XmlIn& in)
+const std::wstring xmlAccess::getErrorMessageFormatted(const std::vector<std::wstring>& failedElements)
{
std::wstring msg;
- const auto& failedElements = in.getErrorsAs<std::wstring>();
if (!failedElements.empty())
{
msg = _("Cannot read the following XML elements:") + L"\n";
- std::for_each(failedElements.begin(), failedElements.end(),
- [&](const std::wstring& elem) { msg += L"\n" + elem; });
+ std::for_each(failedElements.begin(), failedElements.end(), [&](const std::wstring& elem) { msg += L"\n" + elem; });
}
return msg;
diff --git a/lib/xml_base.h b/lib/xml_base.h
index 338f91f7..ceaf609b 100644
--- a/lib/xml_base.h
+++ b/lib/xml_base.h
@@ -36,7 +36,7 @@ private:
void saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename); //throw FfsXmlError
void loadXmlDocument(const Zstring& filename, zen::XmlDoc& doc); //throw FfsXmlError
-const std::wstring getErrorMessageFormatted(const zen::XmlIn& in);
+const std::wstring getErrorMessageFormatted(const std::vector<std::wstring>& failedElements);
}
#endif // XMLBASE_H_INCLUDED
bgstack15