summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/cmp_filetime.h4
-rw-r--r--lib/db_file.cpp104
-rw-r--r--lib/db_file.h23
-rw-r--r--lib/dir_exist_async.h7
-rw-r--r--lib/dir_lock.cpp39
-rw-r--r--lib/ffs_paths.cpp2
-rw-r--r--lib/ffs_paths.h6
-rw-r--r--lib/generate_logfile.h9
-rw-r--r--lib/help_provider.h2
-rw-r--r--lib/icon_buffer.cpp21
-rw-r--r--lib/localization.cpp118
-rw-r--r--lib/localization.h20
-rw-r--r--lib/parallel_scan.cpp49
-rw-r--r--lib/parallel_scan.h3
-rw-r--r--lib/parse_lng.h128
-rw-r--r--lib/parse_plural.h47
-rw-r--r--lib/process_xml.cpp8
-rw-r--r--lib/process_xml.h12
-rw-r--r--lib/resolve_path.cpp14
-rw-r--r--lib/resolve_path.h2
-rw-r--r--lib/resources.cpp6
-rw-r--r--lib/resources.h8
-rw-r--r--lib/return_codes.h2
-rw-r--r--lib/shadow.cpp33
-rw-r--r--lib/shadow.h5
-rw-r--r--lib/status_handler_impl.h30
-rw-r--r--lib/versioning.cpp25
27 files changed, 494 insertions, 233 deletions
diff --git a/lib/cmp_filetime.h b/lib/cmp_filetime.h
index eb595ace..4e75675b 100644
--- a/lib/cmp_filetime.h
+++ b/lib/cmp_filetime.h
@@ -11,9 +11,9 @@ inline
bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance)
{
if (a < b)
- return b <= a + static_cast<int>(tolerance);
+ return b <= a + static_cast<ptrdiff_t>(tolerance);
else
- return a <= b + static_cast<int>(tolerance);
+ return a <= b + static_cast<ptrdiff_t>(tolerance);
}
//---------------------------------------------------------------------------------------------------------------
diff --git a/lib/db_file.cpp b/lib/db_file.cpp
index aa893711..e4e3d748 100644
--- a/lib/db_file.cpp
+++ b/lib/db_file.cpp
@@ -38,8 +38,8 @@ typedef std::map<UniqueId, BinaryStream> StreamMapping; //list of streams ordere
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!?
+ //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
@@ -49,13 +49,11 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false)
//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;
}
//#######################################################################################################################################
-//save/load streams
void saveStreams(const StreamMapping& streamList, const Zstring& filename) //throw FileError
{
BinStreamOut streamOut;
@@ -75,9 +73,11 @@ void saveStreams(const StreamMapping& streamList, const Zstring& filename) //thr
writeContainer<BinaryStream>(streamOut, it->second);
}
+ assert(!somethingExists(filename)); //orphan tmp files should be cleaned up already at this point!
saveBinStream(filename, streamOut.get()); //throw FileError
#ifdef FFS_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
}
@@ -97,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 != DB_FILE_FORMAT_VER) //read file format version#
+ if (version != DB_FILE_FORMAT_VER) //read file format version number
throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename)));
//read stream lists
@@ -228,8 +228,12 @@ private:
static void write(BinStreamOut& output, const LinkDescriptor& descr)
{
writeNumber<std::int64_t>(output, to<std:: int64_t>(descr.lastWriteTimeRaw));
- writeUtf8(output, descr.targetPath);
- writeNumber<std::int32_t>(output, descr.type);
+
+ warn_static("implement proper migration!")
+ //writeUtf8(output, descr.targetPath);
+ writeUtf8(output, Zstring());
+ //writeNumber<std::int32_t>(output, descr.type);
+ writeNumber<std::int32_t>(output, 0);
}
static void write(BinStreamOut& output, const InSyncDir::InSyncStatus& status)
@@ -249,6 +253,10 @@ private:
void process(const std::pair<Zstring, InSyncSymlink>& symlinkPair)
{
writeUtf8(outputBoth, symlinkPair.first);
+
+ warn_static("new parameter: imp proper migration!")
+ //writeNumber<std::int32_t>(outputBoth, symlinkPair.second.inSyncType);
+
write(outputLeft, symlinkPair.second.left);
write(outputRight, symlinkPair.second.right);
}
@@ -352,8 +360,12 @@ private:
static void read(BinStreamIn& input, LinkDescriptor& descr)
{
descr.lastWriteTimeRaw = readNumber<std::int64_t>(input);
- descr.targetPath = readUtf8(input); //file name
- descr.type = static_cast<LinkDescriptor::LinkType>(readNumber<std::int32_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);
}
static void read(BinStreamIn& input, InSyncDir::InSyncStatus& status)
@@ -364,10 +376,10 @@ private:
void recurse(InSyncDir& container)
{
size_t fileCount = readNumber<std::uint32_t>(inputBoth);
- while (fileCount-- != 0)
+ while (fileCount-- != 0)
{
const Zstring shortName = readUtf8(inputBoth);
- const auto inSyncType = static_cast<InSyncFile::InSyncType>(readNumber<std::int32_t>(inputBoth));
+ const auto inSyncType = static_cast<InSyncType>(readNumber<std::int32_t>(inputBoth));
FileDescriptor dataL;
FileDescriptor dataR;
@@ -378,20 +390,24 @@ private:
}
size_t linkCount = readNumber<std::uint32_t>(inputBoth);
- while (linkCount-- != 0)
+ while (linkCount-- != 0)
{
const Zstring shortName = readUtf8(inputBoth);
+ warn_static("new parameter: imp proper migration!")
+ 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);
- container.addSymlink(shortName, dataL, dataR);
+ container.addSymlink(shortName, dataL, dataR, inSyncType);
}
size_t dirCount = readNumber<std::uint32_t>(inputBoth);
- while (dirCount-- != 0)
+ while (dirCount-- != 0)
{
const Zstring shortName = readUtf8(inputBoth);
@@ -448,25 +464,44 @@ private:
}
template <class M, class V>
- static V& updateItem(M& map, const Zstring& key, const V& value) //efficient create or update without "default-constructible" requirement (Effective STL, item 24)
+ static V& updateItem(M& map, const Zstring& key, const V& value)
{
+ auto rv = map.insert(typename M::value_type(key, value));
+ if (!rv.second)
+ {
+#if defined FFS_WIN || defined FFS_MAC //caveat: key must be updated, if there is a change in short name case!!!
+ if (rv.first->first != key)
+ {
+ map.erase(rv.first);
+ return map.insert(typename M::value_type(key, value)).first->second;
+ }
+#endif
+ rv.first->second = value;
+ }
+ return rv.first->second;
+
+ //www.cplusplus.com claims that hint position for map<>::insert(iterator position, const value_type& val) changed with C++11 -> standard is unclear in [map.modifiers]
+ // => let's use the more generic and potentially less performant version above!
+
+ /*
+ //efficient create or update without "default-constructible" requirement (Effective STL, item 24)
+
+ //first check if key already exists (if yes, we're saving a value construction/destruction compared to std::map<>::insert
auto it = map.lower_bound(key);
if (it != map.end() && !(map.key_comp()(key, it->first)))
{
-#if defined FFS_WIN || defined FFS_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!!
+ #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 (it->first != key)
{
map.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly
return map.insert(typename M::value_type(key, value)).first->second;
}
- else
-#endif
- {
- it->second = value;
- return it->second;
- }
+ #endif
+ it->second = value;
+ return it->second;
}
return map.insert(it, typename M::value_type(key, value))->second;
+ */
}
void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelativeNamePf, InSyncDir::FileList& dbFiles)
@@ -478,6 +513,10 @@ private:
{
if (fileMap.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>());
+
//create or update new "in-sync" state
InSyncFile& file = updateItem(dbFiles, fileMap.getObjShortName(),
InSyncFile(FileDescriptor(fileMap.getLastWriteTime<LEFT_SIDE>(),
@@ -487,10 +526,8 @@ private:
fileMap.getFileSize <RIGHT_SIDE>(),
fileMap.getFileId <RIGHT_SIDE>()),
binaryComparison_ ?
- InSyncFile::IN_SYNC_BINARY_EQUAL :
- InSyncFile::IN_SYNC_ATTRIBUTES_EQUAL)); //efficient add or update (Effective STL, item 24)
- //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!!!
+ IN_SYNC_BINARY_EQUAL :
+ IN_SYNC_ATTRIBUTES_EQUAL));
toPreserve.insert(&file);
}
else //not in sync: preserve last synchronous state
@@ -522,14 +559,15 @@ private:
{
if (linkMap.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state
{
+ assert(linkMap.getShortName<LEFT_SIDE>() == linkMap.getShortName<RIGHT_SIDE>());
+
//create or update new "in-sync" state
InSyncSymlink& link = updateItem(dbLinks, linkMap.getObjShortName(),
- InSyncSymlink(LinkDescriptor(linkMap.getLastWriteTime<LEFT_SIDE>(),
- linkMap.getTargetPath <LEFT_SIDE>(),
- linkMap.getLinkType <LEFT_SIDE>()),
- LinkDescriptor(linkMap.getLastWriteTime<RIGHT_SIDE>(),
- linkMap.getTargetPath <RIGHT_SIDE>(),
- linkMap.getLinkType <RIGHT_SIDE>()))); //efficient add or update (Effective STL, item 24)
+ InSyncSymlink(LinkDescriptor(linkMap.getLastWriteTime<LEFT_SIDE>()),
+ LinkDescriptor(linkMap.getLastWriteTime<RIGHT_SIDE>()),
+ binaryComparison_ ?
+ IN_SYNC_BINARY_EQUAL :
+ IN_SYNC_ATTRIBUTES_EQUAL));
toPreserve.insert(&link);
}
else //not in sync: preserve last synchronous state
@@ -563,6 +601,8 @@ private:
{
case DIR_EQUAL:
{
+ assert(dirMap.getShortName<LEFT_SIDE>() == dirMap.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
diff --git a/lib/db_file.h b/lib/db_file.h
index c080081c..181a433e 100644
--- a/lib/db_file.h
+++ b/lib/db_file.h
@@ -14,15 +14,15 @@ namespace zen
{
const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db");
+enum InSyncType
+{
+ IN_SYNC_BINARY_EQUAL, //checked file content
+ IN_SYNC_ATTRIBUTES_EQUAL, //only "looks" like they're equal
+};
+
//artificial hierarchy of last synchronous state:
struct InSyncFile
{
- enum InSyncType
- {
- IN_SYNC_BINARY_EQUAL, //checked file content
- IN_SYNC_ATTRIBUTES_EQUAL, //only "looks" like they're equal
- };
-
InSyncFile(const FileDescriptor& l, const FileDescriptor& r, InSyncType type) : left(l), right(r), inSyncType(type) {}
FileDescriptor left;
FileDescriptor right;
@@ -31,9 +31,10 @@ struct InSyncFile
struct InSyncSymlink
{
- InSyncSymlink(const LinkDescriptor& l, const LinkDescriptor& r) : left(l), right(r) {}
+ InSyncSymlink(const LinkDescriptor& l, const LinkDescriptor& r, InSyncType type) : left(l), right(r), inSyncType(type) {}
LinkDescriptor left;
LinkDescriptor right;
+ InSyncType inSyncType;
};
struct InSyncDir
@@ -66,14 +67,14 @@ struct InSyncDir
return dirs.insert(std::make_pair(shortName, InSyncDir(statusIn))).first->second;
}
- void addFile(const Zstring& shortName, const FileDescriptor& dataL, const FileDescriptor& dataR, InSyncFile::InSyncType type)
+ void addFile(const Zstring& shortName, const FileDescriptor& dataL, const FileDescriptor& dataR, InSyncType type)
{
files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, type)));
}
- void addSymlink(const Zstring& shortName, const LinkDescriptor& dataL, const LinkDescriptor& dataR)
+ void addSymlink(const Zstring& shortName, const LinkDescriptor& dataL, const LinkDescriptor& dataR, InSyncType type)
{
- symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR)));
+ symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR, type)));
}
};
@@ -85,4 +86,4 @@ std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirMapping& baseMa
void saveLastSynchronousState(const BaseDirMapping& baseMapping); //throw FileError
}
-#endif // DBFILE_H_INCLUDED
+#endif //DBFILE_H_INCLUDED
diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h
index 678a0235..b96dc7e1 100644
--- a/lib/dir_exist_async.h
+++ b/lib/dir_exist_async.h
@@ -20,7 +20,8 @@ namespace
//directory existence checking may hang for non-existent network drives => run asynchronously and update UI!
//- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable)
//- add reasonable time-out time!
-std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::vector<Zstring>& dirnames, bool allowUserInteraction, ProcessCallback& procCallback)
+//- avoid checking duplicate entries by design: set<Zstring, LessFilename>
+std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::set<Zstring, LessFilename>& dirnames, bool allowUserInteraction, ProcessCallback& procCallback)
{
using namespace zen;
@@ -64,8 +65,8 @@ std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::vector<Zstrin
inline //also silences Clang "unused function" for compilation units depending from getExistingDirsUpdating() only
bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, ProcessCallback& procCallback)
{
- std::vector<Zstring> dirnames;
- dirnames.push_back(dirname);
+ std::set<Zstring, LessFilename> dirnames;
+ dirnames.insert(dirname);
std::set<Zstring, LessFilename> dirsEx = getExistingDirsUpdating(dirnames, allowUserInteraction, procCallback);
return dirsEx.find(dirname) != dirsEx.end();
}
diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp
index 328b87d3..f3a16677 100644
--- a/lib/dir_lock.cpp
+++ b/lib/dir_lock.cpp
@@ -75,7 +75,6 @@ public:
void emitLifeSign() const //try to append one byte...; throw()
{
const char buffer[1] = {' '};
-
#ifdef FFS_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!!!
@@ -99,16 +98,16 @@ public:
return;
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,
- 1, //__in DWORD nNumberOfBytesToWrite,
- &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten,
- nullptr); //__inout_opt LPOVERLAPPED lpOverlapped
+ if (!::WriteFile(fileHandle, //_In_ HANDLE hFile,
+ buffer, //_In_ LPCVOID lpBuffer,
+ 1, //_In_ DWORD nNumberOfBytesToWrite,
+ &bytesWritten, //_Out_opt_ LPDWORD lpNumberOfBytesWritten,
+ nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
+ return;
#elif defined FFS_LINUX || defined FFS_MAC
const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND);
- if (fileHandle < 0)
+ if (fileHandle == -1)
return;
ZEN_ON_SCOPE_EXIT(::close(fileHandle));
@@ -537,13 +536,15 @@ bool tryLock(const Zstring& lockfilename) //throw FileError
else
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + zen::getLastErrorFormatted());
}
- ::CloseHandle(fileHandle);
+ ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); });
+ FileOutput fileOut(fileHandle, lockfilename); //pass handle ownership
- ::SetFileAttributes(applyLongPathPrefix(lockfilename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it
+ //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
- //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open
::umask(0); //important! -> why?
+ //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open
const int fileHandle = ::open(lockfilename.c_str(), O_CREAT | O_WRONLY | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO);
if (fileHandle == -1)
{
@@ -552,13 +553,19 @@ bool tryLock(const Zstring& lockfilename) //throw FileError
else
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + zen::getLastErrorFormatted());
}
- ::close(fileHandle);
-#endif
-
ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); });
+ FileOutputUnbuffered fileOut(fileHandle, lockfilename); //pass handle ownership
+#endif
//write housekeeping info: user, process info, lock GUID
- writeLockInfo(lockfilename); //throw FileError
+ BinaryStream binStream;
+ {
+ BinStreamOut streamOut;
+ LockInformation(FromCurrentProcess()).toStream(streamOut);
+ binStream = streamOut.get();
+ }
+ if (!binStream.empty())
+ fileOut.write(&*binStream.begin(), binStream.size()); //throw FileError
guardLockFile.dismiss(); //lockfile created successfully
return true;
@@ -639,6 +646,8 @@ public:
private:
LockAdmin() {}
+ LockAdmin(const LockAdmin&); //=delete
+ LockAdmin& operator=(const LockAdmin&); //=delete
typedef std::string UniqueId;
typedef std::map<Zstring, UniqueId, LessFilename> FileToGuidMap; //n:1 handle uppper/lower case correctly
diff --git a/lib/ffs_paths.cpp b/lib/ffs_paths.cpp
index 82232b5c..5ee4a3eb 100644
--- a/lib/ffs_paths.cpp
+++ b/lib/ffs_paths.cpp
@@ -69,7 +69,7 @@ Zstring zen::getResourceDir()
else //use OS' standard paths
return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir()));
#elif defined FFS_MAC
- return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir()));
+ return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); //if packaged, used "Contents/Resources", else the executable directory
#endif
}
diff --git a/lib/ffs_paths.h b/lib/ffs_paths.h
index cb0b9c3c..28516a3f 100644
--- a/lib/ffs_paths.h
+++ b/lib/ffs_paths.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef STANDARDPATHS_H_INCLUDED
-#define STANDARDPATHS_H_INCLUDED
+#ifndef STANDARDPATHS_H_84275908342534253425
+#define STANDARDPATHS_H_84275908342534253425
#include <zen/zstring.h>
@@ -22,4 +22,4 @@ Zstring getFreeFileSyncLauncher(); //full path to application launcher C:\...\Fr
bool manualProgramUpdateRequired();
}
-#endif // STANDARDPATHS_H_INCLUDED
+#endif //STANDARDPATHS_H_84275908342534253425
diff --git a/lib/generate_logfile.h b/lib/generate_logfile.h
index c441de66..31f7bd43 100644
--- a/lib/generate_logfile.h
+++ b/lib/generate_logfile.h
@@ -76,16 +76,17 @@ std::wstring generateLogHeader(const SummaryInfo& s)
results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(s.totalTime).Format()));
- //calculate max width, this considers UTF-16 only, not true Unicode...
+ //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway!
size_t sepLineLen = 0;
std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { sepLineLen = std::max(sepLineLen, str.size()); });
- for (size_t i = 0; i < sepLineLen; ++i) output += L'_'; //this considers UTF-16 only, not true Unicode!!!
+ output.resize(output.size() + sepLineLen + 1, L'_');
output += L'\n';
- std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += str; output += L'\n'; });
+ std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += L'|'; output += str; output += L'\n'; });
- for (size_t i = 0; i < sepLineLen; ++i) output += L'_';
+ output += L'|';
+ output.resize(output.size() + sepLineLen, L'_');
output += L'\n';
return output;
diff --git a/lib/help_provider.h b/lib/help_provider.h
index 8227efb5..040eb33c 100644
--- a/lib/help_provider.h
+++ b/lib/help_provider.h
@@ -95,4 +95,4 @@ void displayHelpEntry(wxWindow* parent)
}
}
-#endif // HELPPROVIDER_H_INCLUDED
+#endif //HELPPROVIDER_H_INCLUDED
diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp
index 3912849e..04364b32 100644
--- a/lib/icon_buffer.cpp
+++ b/lib/icon_buffer.cpp
@@ -35,8 +35,9 @@ boost::thread::id mainThreadId = boost::this_thread::get_id();
#endif
#ifdef FFS_WIN
-#define DEF_DLL_FUN(name) DllFun<thumb::FunType_##name> name(thumb::getDllName(), thumb::funName_##name);
+const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found"
+#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<thumb::FunType_##name>(thumb::getDllName(), thumb::funName_##name) : DllFun<thumb::FunType_##name>();
DEF_DLL_FUN(getIconByIndex); //
DEF_DLL_FUN(getThumbnail); //let's spare the boost::call_once hustle and allocate statically
DEF_DLL_FUN(releaseImageData); //
@@ -67,7 +68,7 @@ public:
{
if (handle_ != nullptr)
#ifdef FFS_WIN
- releaseImageData(handle_);
+ releaseImageData(handle_); //should be checked already before creating IconHolder!
#elif defined FFS_LINUX
::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"!
#elif defined FFS_MAC
@@ -203,10 +204,10 @@ IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffe
if (!imgList) //no need to IUnknown::Release() imgList!
return IconHolder();
- if (!getIconByIndex)
- return IconHolder();
+ if (getIconByIndex && releaseImageData)
+ return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz)));
- return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz)));
+ return IconHolder();
}
@@ -237,7 +238,7 @@ IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz)
IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return 0 on failure
{
#ifdef FFS_WIN
- if (getThumbnail)
+ if (getThumbnail && releaseImageData)
return IconHolder(getThumbnail(filename.c_str(), requestedSize));
#elif defined FFS_LINUX
@@ -375,13 +376,13 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz)
const bool isLink = (fileInfo.dwAttributes & SFGAO_LINK) != 0;
- if (getIconByIndex)
+ if (getIconByIndex && releaseImageData)
if (const thumb::ImageData* imgData = getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz)))
return IconHolder(imgData);
}
#elif defined FFS_LINUX
- GFile* file = ::g_file_new_for_path(filename.c_str()); //never fails
+ GFile* file = ::g_file_new_for_path(filename.c_str()); //documented to "never fail"
ZEN_ON_SCOPE_EXIT(::g_object_unref(file);)
if (GFileInfo* fileInfo = ::g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr))
@@ -462,7 +463,7 @@ public:
//must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!)
Opt<wxBitmap> retrieveFileIcon(const Zstring& fileName)
{
- assert(boost::this_thread::get_id() == mainThreadId );
+ assert(boost::this_thread::get_id() == mainThreadId);
boost::lock_guard<boost::mutex> dummy(lockIconList);
auto it = iconList.find(fileName);
if (it == iconList.end())
@@ -481,7 +482,7 @@ public:
//call at an appropriate time, e.g. after Workload::setWorkload()
void limitBufferSize() //critical because GDI resources are limited (e.g. 10000 on XP per process)
{
- assert(boost::this_thread::get_id() == mainThreadId );
+ assert(boost::this_thread::get_id() == mainThreadId);
boost::lock_guard<boost::mutex> dummy(lockIconList);
while (iconList.size() > BUFFER_SIZE_MAX)
{
diff --git a/lib/localization.cpp b/lib/localization.cpp
index c29860f7..f050e255 100644
--- a/lib/localization.cpp
+++ b/lib/localization.cpp
@@ -18,7 +18,10 @@
#include "parse_lng.h"
#include "ffs_paths.h"
-#ifdef FFS_MAC
+#ifdef FFS_LINUX
+#include <wchar.h> //wcscasecmp
+
+#elif defined FFS_MAC
#include <CoreServices/CoreServices.h>
#endif
@@ -27,10 +30,10 @@ using namespace zen;
namespace
{
-class FFSLocale : public TranslationHandler
+class FFSTranslation : public TranslationHandler
{
public:
- FFSLocale(const wxString& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError
+ FFSTranslation(const std::wstring& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError
wxLanguage langId() const { return langId_; }
@@ -48,8 +51,8 @@ public:
auto it = transMappingPl.find(std::make_pair(singular, plural));
if (it != transMappingPl.end())
{
- const int formNo = pluralParser->getForm(n);
- if (0 <= formNo && formNo < static_cast<int>(it->second.size()))
+ const size_t formNo = pluralParser->getForm(n);
+ if (formNo < it->second.size())
return it->second[formNo];
}
return n == 1 ? singular : plural; //fallback
@@ -57,7 +60,7 @@ public:
private:
typedef hash_map<std::wstring, std::wstring> Translation; //hash_map is 15% faster than std::map on GCC
- typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring> > TranslationPlural;
+ typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring>> TranslationPlural;
Translation transMapping; //map original text |-> translation
TranslationPlural transMappingPl;
@@ -66,7 +69,7 @@ private:
};
-FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError
+FFSTranslation::FFSTranslation(const std::wstring& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError
{
std::string inputStream;
try
@@ -120,7 +123,8 @@ 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 HandleError onError(const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context
+ 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; } //
private:
std::vector<Zstring>& lngFiles_;
@@ -145,10 +149,11 @@ struct LessTranslation : public std::binary_function<ExistingTranslations::Entry
return rv == CSTR_LESS_THAN; //convert to C-style string compare result
#elif defined FFS_LINUX
- return lhs.languageName.CmpNoCase(rhs.languageName) < 0;
+ 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
- auto allocCFStringRef = [](const wxString& str) -> CFStringRef //output not owned!
+ auto allocCFStringRef = [](const std::wstring& str) -> CFStringRef //output not owned!
{
return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc,
utfCvrtTo<std::string>(str).c_str(), //const char *cStr,
@@ -205,10 +210,10 @@ ExistingTranslations::ExistingTranslations()
{
ExistingTranslations::Entry newEntry;
newEntry.languageID = locInfo->Language;
- newEntry.languageName = utfCvrtTo<wxString>(lngHeader.languageName);
- newEntry.languageFile = utfCvrtTo<wxString>(*it);
- newEntry.translatorName = utfCvrtTo<wxString>(lngHeader.translatorName);
- newEntry.languageFlag = utfCvrtTo<wxString>(lngHeader.flagFile);
+ 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);
}
}
@@ -253,6 +258,21 @@ wxLanguage mapLanguageDialect(wxLanguage language)
case wxLANGUAGE_ARABIC_YEMEN:
return wxLANGUAGE_ARABIC;
+ //variants of wxLANGUAGE_CHINESE_SIMPLIFIED
+ case wxLANGUAGE_CHINESE:
+ case wxLANGUAGE_CHINESE_SINGAPORE:
+ return wxLANGUAGE_CHINESE_SIMPLIFIED;
+
+ //variants of wxLANGUAGE_CHINESE_TRADITIONAL
+ case wxLANGUAGE_CHINESE_TAIWAN:
+ case wxLANGUAGE_CHINESE_HONGKONG:
+ case wxLANGUAGE_CHINESE_MACAU:
+ return wxLANGUAGE_CHINESE_TRADITIONAL;
+
+ //variants of wxLANGUAGE_DUTCH
+ case wxLANGUAGE_DUTCH_BELGIAN:
+ return wxLANGUAGE_DUTCH;
+
//variants of wxLANGUAGE_ENGLISH_UK
case wxLANGUAGE_ENGLISH_AUSTRALIA:
case wxLANGUAGE_ENGLISH_NEW_ZEALAND:
@@ -273,14 +293,6 @@ wxLanguage mapLanguageDialect(wxLanguage language)
case wxLANGUAGE_ENGLISH_PHILIPPINES:
return wxLANGUAGE_ENGLISH_US;
- //variants of wxLANGUAGE_GERMAN
- case wxLANGUAGE_GERMAN_AUSTRIAN:
- case wxLANGUAGE_GERMAN_BELGIUM:
- case wxLANGUAGE_GERMAN_LIECHTENSTEIN:
- case wxLANGUAGE_GERMAN_LUXEMBOURG:
- case wxLANGUAGE_GERMAN_SWISS:
- return wxLANGUAGE_GERMAN;
-
//variants of wxLANGUAGE_FRENCH
case wxLANGUAGE_FRENCH_BELGIAN:
case wxLANGUAGE_FRENCH_CANADIAN:
@@ -289,29 +301,36 @@ wxLanguage mapLanguageDialect(wxLanguage language)
case wxLANGUAGE_FRENCH_SWISS:
return wxLANGUAGE_FRENCH;
- //variants of wxLANGUAGE_DUTCH
- case wxLANGUAGE_DUTCH_BELGIAN:
- return wxLANGUAGE_DUTCH;
+ //variants of wxLANGUAGE_GERMAN
+ case wxLANGUAGE_GERMAN_AUSTRIAN:
+ case wxLANGUAGE_GERMAN_BELGIUM:
+ case wxLANGUAGE_GERMAN_LIECHTENSTEIN:
+ case wxLANGUAGE_GERMAN_LUXEMBOURG:
+ case wxLANGUAGE_GERMAN_SWISS:
+ return wxLANGUAGE_GERMAN;
//variants of wxLANGUAGE_ITALIAN
case wxLANGUAGE_ITALIAN_SWISS:
return wxLANGUAGE_ITALIAN;
- //variants of wxLANGUAGE_CHINESE_SIMPLIFIED
- case wxLANGUAGE_CHINESE:
- case wxLANGUAGE_CHINESE_SINGAPORE:
- return wxLANGUAGE_CHINESE_SIMPLIFIED;
+ //variants of wxLANGUAGE_NORWEGIAN_BOKMAL
+ case wxLANGUAGE_NORWEGIAN_NYNORSK:
+ return wxLANGUAGE_NORWEGIAN_BOKMAL;
- //variants of wxLANGUAGE_CHINESE_TRADITIONAL
- case wxLANGUAGE_CHINESE_TAIWAN:
- case wxLANGUAGE_CHINESE_HONGKONG:
- case wxLANGUAGE_CHINESE_MACAU:
- return wxLANGUAGE_CHINESE_TRADITIONAL;
+ //variants of wxLANGUAGE_ROMANIAN
+ case wxLANGUAGE_MOLDAVIAN:
+ return wxLANGUAGE_ROMANIAN;
//variants of wxLANGUAGE_RUSSIAN
case wxLANGUAGE_RUSSIAN_UKRAINE:
return wxLANGUAGE_RUSSIAN;
+ //variants of wxLANGUAGE_SERBIAN
+ case wxLANGUAGE_SERBIAN_CYRILLIC:
+ case wxLANGUAGE_SERBIAN_LATIN:
+ case wxLANGUAGE_SERBO_CROATIAN:
+ return wxLANGUAGE_SERBIAN;
+
//variants of wxLANGUAGE_SPANISH
case wxLANGUAGE_SPANISH_ARGENTINA:
case wxLANGUAGE_SPANISH_BOLIVIA:
@@ -339,26 +358,24 @@ wxLanguage mapLanguageDialect(wxLanguage language)
case wxLANGUAGE_SWEDISH_FINLAND:
return wxLANGUAGE_SWEDISH;
- //variants of wxLANGUAGE_NORWEGIAN_BOKMAL
- case wxLANGUAGE_NORWEGIAN_NYNORSK:
- return wxLANGUAGE_NORWEGIAN_BOKMAL;
-
//languages without variants:
+ //case wxLANGUAGE_CROATIAN:
//case wxLANGUAGE_CZECH:
//case wxLANGUAGE_DANISH:
//case wxLANGUAGE_FINNISH:
//case wxLANGUAGE_GREEK:
+ //case wxLANGUAGE_HEBREW:
+ //case wxLANGUAGE_HUNGARIAN:
//case wxLANGUAGE_JAPANESE:
+ //case wxLANGUAGE_KOREAN:
//case wxLANGUAGE_LITHUANIAN:
//case wxLANGUAGE_POLISH:
- //case wxLANGUAGE_SLOVENIAN:
- //case wxLANGUAGE_HUNGARIAN:
//case wxLANGUAGE_PORTUGUESE:
//case wxLANGUAGE_PORTUGUESE_BRAZILIAN:
//case wxLANGUAGE_SCOTS_GAELIC:
- //case wxLANGUAGE_KOREAN:
+ //case wxLANGUAGE_SLOVENIAN:
+ //case wxLANGUAGE_TURKISH:
//case wxLANGUAGE_UKRAINIAN:
- //case wxLANGUAGE_CROATIAN:
default:
return language;
}
@@ -387,6 +404,8 @@ public:
locLng = lng;
}
+ static void release() { locale.reset(); locLng = wxLANGUAGE_UNKNOWN; }
+
static wxLanguage getLanguage() { return locLng; }
private:
@@ -394,7 +413,13 @@ private:
static wxLanguage locLng;
};
std::unique_ptr<wxLocale> wxWidgetsLocale::locale;
-wxLanguage wxWidgetsLocale::locLng = wxLANGUAGE_UNKNOWN;
+wxLanguage wxWidgetsLocale::locLng = wxLANGUAGE_UNKNOWN;
+}
+
+
+void zen::releaseWxLocale()
+{
+ wxWidgetsLocale::release();
}
@@ -404,7 +429,7 @@ void zen::setLanguage(int language) //throw FileError
return; //support polling
//(try to) retrieve language file
- wxString languageFile;
+ std::wstring languageFile;
for (auto it = ExistingTranslations::get().begin(); it != ExistingTranslations::get().end(); ++it)
if (it->languageID == language)
@@ -419,7 +444,7 @@ void zen::setLanguage(int language) //throw FileError
else
try
{
- zen::setTranslator(new FFSLocale(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError
+ zen::setTranslator(new FFSTranslation(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError
}
catch (lngfile::ParsingError& e)
{
@@ -438,10 +463,9 @@ void zen::setLanguage(int language) //throw FileError
}
-
int zen::getLanguage()
{
- const FFSLocale* loc = dynamic_cast<const FFSLocale*>(zen::getTranslator());
+ const FFSTranslation* loc = dynamic_cast<const FFSTranslation*>(zen::getTranslator());
return loc ? loc->langId() : wxLANGUAGE_ENGLISH_US;
}
diff --git a/lib/localization.h b/lib/localization.h
index 125be0fd..2d871dd7 100644
--- a/lib/localization.h
+++ b/lib/localization.h
@@ -4,12 +4,12 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef MISC_H_INCLUDED
-#define MISC_H_INCLUDED
+#ifndef LOCALIZATION_H_8917342083178321534
+#define LOCALIZATION_H_8917342083178321534
#include <vector>
#include <zen/file_error.h>
-#include <wx/string.h>
+//#include <wx/string.h>
namespace zen
{
@@ -19,10 +19,10 @@ public:
struct Entry
{
int languageID;
- wxString languageName;
- wxString languageFile;
- wxString translatorName;
- wxString languageFlag;
+ std::wstring languageName;
+ std::wstring languageFile;
+ std::wstring translatorName;
+ std::wstring languageFlag;
};
static const std::vector<Entry>& get();
@@ -34,9 +34,13 @@ private:
std::vector<Entry> locMapping;
};
+
void setLanguage(int language); //throw FileError
int getLanguage();
int retrieveSystemLanguage();
+
+void releaseWxLocale(); //wxLocale crashes miserably on wxGTK when destructor runs during global cleanup => call in wxApp::OnExit
+//"You should delete all wxWidgets object that you created by the time OnExit finishes. In particular, do not destroy them from application class' destructor!"
}
-#endif // MISC_H_INCLUDED
+#endif //LOCALIZATION_H_8917342083178321534
diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp
index 37dd350e..33d8174f 100644
--- a/lib/parallel_scan.cpp
+++ b/lib/parallel_scan.cpp
@@ -5,15 +5,14 @@
// **************************************************************************
#include "parallel_scan.h"
-#include <boost/detail/atomic_count.hpp>
-#include "db_file.h"
-#include "lock_holder.h"
#include <zen/file_traverser.h>
#include <zen/file_error.h>
#include <zen/thread.h> //includes <boost/thread.hpp>
#include <zen/scope_guard.h>
#include <zen/fixed_list.h>
#include <boost/detail/atomic_count.hpp>
+#include "db_file.h"
+#include "lock_holder.h"
using namespace zen;
@@ -283,21 +282,24 @@ public:
TraverserShared(long threadID,
SymLinkHandling handleSymlinks,
const HardFilter::FilterRef& filter,
- std::set<Zstring>& failedReads,
+ std::set<Zstring>& failedDirReads,
+ std::set<Zstring>& failedItemReads,
AsyncCallback& acb) :
handleSymlinks_(handleSymlinks),
filterInstance(filter),
- failedReads_(failedReads),
+ failedDirReads_(failedDirReads),
+ failedItemReads_(failedItemReads),
acb_(acb),
threadID_(threadID) {}
const SymLinkHandling handleSymlinks_;
const HardFilter::FilterRef filterInstance; //always bound!
- std::set<Zstring>& failedReads_; //relative postfixed names of directories that could not be read (empty for root)
+ std::set<Zstring>& failedDirReads_;
+ std::set<Zstring>& failedItemReads_;
AsyncCallback& acb_;
- long threadID_;
+ const long threadID_;
};
@@ -315,7 +317,8 @@ public:
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 HandleError onError (const std::wstring& msg);
+ virtual HandleError reportDirError (const std::wstring& msg);
+ virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName);
private:
TraverserShared& cfg;
@@ -343,9 +346,7 @@ void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const
if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort))
return;
- // std::string fileId = details.fileSize >= 1024 * 1024U ?
- // util::retrieveFileID(fullName) :
- // std::string();
+ // std::string fileId = details.fileSize >= 1024 * 1024U ? util::retrieveFileID(fullName) : std::string();
/*
Perf test Windows 7, SSD, 350k files, 50k dirs, files > 1MB: 7000
regular: 6.9s
@@ -356,7 +357,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.lastWriteTimeRaw, details.fileSize, details.id));
+ output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTime, details.fileSize, details.id));
cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator
}
@@ -382,7 +383,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const Zchar* shortName, const Zst
//apply filter before processing (use relative name!)
if (cfg.filterInstance->passFileFilter(relName)) //always use file filter: Link type may not be "stable" on Linux!
{
- output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTimeRaw, details.targetPath, details.dirLink ? LinkDescriptor::TYPE_DIR : LinkDescriptor::TYPE_FILE));
+ output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTime));
cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator
}
}
@@ -422,18 +423,33 @@ std::shared_ptr<TraverseCallback> DirCallback::onDir(const Zchar* shortName, con
}
-DirCallback::HandleError DirCallback::onError(const std::wstring& msg)
+DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg)
{
switch (cfg.acb_.reportError(msg))
{
case FillBufferCallback::ON_ERROR_IGNORE:
- cfg.failedReads_.insert(relNameParentPf_);
+ cfg.failedDirReads_.insert(relNameParentPf_);
return ON_ERROR_IGNORE;
case FillBufferCallback::ON_ERROR_RETRY:
return ON_ERROR_RETRY;
}
+ assert(false);
+ return ON_ERROR_IGNORE;
+}
+
+DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, const Zchar* shortName)
+{
+ switch (cfg.acb_.reportError(msg))
+ {
+ case FillBufferCallback::ON_ERROR_IGNORE:
+ cfg.failedItemReads_.insert(relNameParentPf_ + shortName);
+ return ON_ERROR_IGNORE;
+
+ case FillBufferCallback::ON_ERROR_RETRY:
+ return ON_ERROR_RETRY;
+ }
assert(false);
return ON_ERROR_IGNORE;
}
@@ -484,7 +500,8 @@ public:
TraverserShared travCfg(threadID_,
dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy
dirKey_.filter_,
- dirOutput_.failedReads,
+ dirOutput_.failedDirReads,
+ dirOutput_.failedItemReads,
*acb_);
DirCallback traverser(travCfg,
diff --git a/lib/parallel_scan.h b/lib/parallel_scan.h
index 5a52e44e..b7518428 100644
--- a/lib/parallel_scan.h
+++ b/lib/parallel_scan.h
@@ -46,7 +46,8 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs)
struct DirectoryValue
{
DirContainer dirCont;
- std::set<Zstring> failedReads; //relative postfixed names of directories that could not be read completely (empty string for root), e.g. access denied, or temporal network drop
+ 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
};
diff --git a/lib/parse_lng.h b/lib/parse_lng.h
index b6af9b18..48c7044b 100644
--- a/lib/parse_lng.h
+++ b/lib/parse_lng.h
@@ -10,7 +10,8 @@
#include <algorithm>
#include <cctype>
#include <functional>
-#include <list>
+//#include <list>
+#include <memory>
#include <map>
#include <set>
#include <sstream>
@@ -19,6 +20,8 @@
#include <vector>
#include <zen/utf.h>
#include <zen/string_tools.h>
+#include "parse_plural.h"
+//#include <zen/perf.h>
namespace lngfile
{
@@ -28,7 +31,7 @@ typedef std::map <std::string, std::string> TranslationMap; //orig |-> translat
//plural forms
typedef std::pair<std::string, std::string> SingularPluralPair; //1 house| n houses
typedef std::vector<std::string> PluralForms; //1 dom | 2 domy | 5 domów
-typedef std::map <SingularPluralPair, PluralForms> TranslationPluralMap; //(sing/plu) |-> pluralforms
+typedef std::map<SingularPluralPair, PluralForms> TranslationPluralMap; //(sing/plu) |-> pluralforms
struct TransHeader
{
@@ -79,39 +82,36 @@ public:
void addItem(const std::string& orig, const std::string& trans)
{
if (!transUnique.insert(orig).second) return;
-
- dump.push_back(RegularItem(std::make_pair(orig, trans)));
- sequence.push_back(&dump.back());
+ sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, trans)));
}
void addPluralItem(const SingularPluralPair& orig, const PluralForms& trans)
{
if (!pluralUnique.insert(orig).second) return;
-
- dumpPlural.push_back(PluralItem(std::make_pair(orig, trans)));
- sequence.push_back(&dumpPlural.back());
+ sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, trans)));
}
bool untranslatedTextExists() const
{
- for (auto it = dump.begin(); it != dump.end(); ++it)
- if (it->value.second.empty())
- return true;
- for (auto it = dumpPlural.begin(); it != dumpPlural.end(); ++it)
- if (it->value.second.empty())
- return true;
+ 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;
}
private:
friend std::string generateLng(const TranslationList& in, const TransHeader& header);
- struct Item {virtual ~Item() {} };
+ 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<Item*> sequence; //dynamic list of translation elements
- std::list<RegularItem> dump; //manage memory
- std::list<PluralItem> dumpPlural; //manage memory
+ std::vector<std::shared_ptr<Item>> sequence; //ordered list of translation elements
std::set<TranslationMap ::key_type> transUnique; //check uniqueness
std::set<TranslationPluralMap::key_type> pluralUnique; //
@@ -307,9 +307,18 @@ public:
{
parseHeader(header);
- //items
- while (token().type != Token::TK_END)
- parseRegular(out, pluralOut, header.pluralCount);
+ try
+ {
+ parse_plural::PluralFormInfo pi(header.pluralDefinition, header.pluralCount);
+
+ //items
+ while (token().type != Token::TK_END)
+ parseRegular(out, pluralOut, pi);
+ }
+ catch (const parse_plural::InvalidPluralForm&)
+ {
+ throw ParsingError(scn.posRow(), scn.posCol());
+ }
}
void parseHeader(TransHeader& header)
@@ -350,12 +359,12 @@ public:
}
private:
- void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, int formCount)
+ void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo)
{
consumeToken(Token::TK_SRC_BEGIN);
if (token().type == Token::TK_PLURAL_BEGIN)
- return parsePlural(pluralOut, formCount);
+ return parsePlural(pluralOut, pluralInfo);
std::string original = tk.text;
consumeToken(Token::TK_TEXT);
@@ -369,10 +378,12 @@ private:
nextToken();
}
consumeToken(Token::TK_TRG_END);
+
+ validateTranslation(original, translation); //throw throw ParsingError
out.insert(std::make_pair(original, translation));
}
- void parsePlural(TranslationPluralMap& pluralOut, int formCount)
+ void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo)
{
//Token::TK_SRC_BEGIN already consumed
@@ -398,16 +409,73 @@ private:
consumeToken(Token::TK_TEXT);
consumeToken(Token::TK_PLURAL_END);
pluralList.push_back(pluralForm);
-
}
- if (!pluralList.empty() && static_cast<int>(pluralList.size()) != formCount) //invalid number of plural forms
+ consumeToken(Token::TK_TRG_END);
+
+ const SingularPluralPair original(engSingular, engPlural);
+ validateTranslation(original, pluralList, pluralInfo);
+ pluralOut.insert(std::make_pair(original, pluralList));
+ }
+
+ void validateTranslation(const std::string& original, const std::string& translation) //throw ParsingError
+ {
+ if (original.empty())
throw ParsingError(scn.posRow(), scn.posCol());
- consumeToken(Token::TK_TRG_END);
- pluralOut.insert(std::make_pair(SingularPluralPair(engSingular, engPlural), pluralList));
+ if (!translation.empty())
+ {
+ //if original contains placeholder, so should translation!
+ auto checkPlaceholder = [&](const std::string& placeholder)
+ {
+ if (zen::contains(original, placeholder) &&
+ !zen::contains(translation, placeholder))
+ throw ParsingError(scn.posRow(), scn.posCol());
+ };
+ checkPlaceholder("%x");
+ checkPlaceholder("%y");
+ checkPlaceholder("%z");
+ }
}
+ void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError
+ {
+ //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 (!translation.empty())
+ {
+ //check for invalid number of plural forms
+ if (pluralInfo.getCount() != static_cast<int>(translation.size()))
+ throw ParsingError(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());
+
+ auto checkSecondaryPlaceholder = [&](const std::string& placeholder)
+ {
+ //make sure secondary placeholder is used in both source texts (or none)
+ if (zen::contains(original.first, placeholder) ||
+ zen::contains(original.second, placeholder))
+ {
+ if (!zen::contains(original.first, placeholder) ||
+ !zen::contains(original.second, placeholder))
+ throw ParsingError(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());
+ }
+ };
+
+ checkSecondaryPlaceholder("%y");
+ checkSecondaryPlaceholder("%z");
+ }
+ }
void nextToken() { tk = scn.nextToken(); }
const Token& token() const { return tk; }
@@ -499,8 +567,8 @@ std::string generateLng(const TranslationList& in, const TransHeader& header)
//items
for (auto it = in.sequence.begin(); it != in.sequence.end(); ++it)
{
- const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(*it);
- const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(*it);
+ const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(it->get());
+ const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(it->get());
if (regular)
{
diff --git a/lib/parse_plural.h b/lib/parse_plural.h
index c3591881..bb32f81f 100644
--- a/lib/parse_plural.h
+++ b/lib/parse_plural.h
@@ -38,7 +38,20 @@ private:
};
+//validate plural form
+class InvalidPluralForm {};
+class PluralFormInfo
+{
+public:
+ PluralFormInfo(const std::string& definition, int pluralCount); //throw InvalidPluralForm
+
+ int getCount() const { return static_cast<int>(formCount.size()); }
+ bool isSingleNumberForm(int n) const { return 0 <= n && n < static_cast<int>(formCount.size()) ? formCount[n] == 1 : false; }
+
+private:
+ std::vector<int> formCount;
+};
@@ -413,6 +426,40 @@ private:
inline
+PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) //throw InvalidPluralForm
+{
+ if (pluralCount < 1)
+ throw InvalidPluralForm();
+
+ formCount.resize(pluralCount);
+ try
+ {
+ parse_plural::PluralForm pf(definition); //throw parse_plural::ParsingError
+ //PERF_START
+
+ //perf: 80ns per iteration max (for arabic)
+ //=> 1000 iterations should be fast enough and still detect all "single number forms"
+ for (int j = 0; j < 1000; ++j)
+ {
+ int form = pf.getForm(j);
+ if (0 <= form && form < static_cast<int>(formCount.size()))
+ ++formCount[form];
+ else
+ throw InvalidPluralForm();
+ }
+ }
+ catch (const parse_plural::ParsingError&)
+ {
+ throw InvalidPluralForm();
+ }
+
+ //ensure each form is used at least once:
+ if (!std::all_of(formCount.begin(), formCount.end(), [](int count) { return count >= 1; }))
+ throw InvalidPluralForm();
+}
+
+
+inline
PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError
}
diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp
index 4974dcbc..78a40159 100644
--- a/lib/process_xml.cpp
+++ b/lib/process_xml.cpp
@@ -980,12 +980,12 @@ void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config)
warn_static("remove after migration?")
if (inGuiCfg["SyncPreviewActive"]) //obsolete name
- inGuiCfg["SyncPreviewActive"](config.showSyncAction);
+ inGuiCfg["SyncPreviewActive"](config.highlightSyncAction);
else
{
std::string val;
if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!?
- config.showSyncAction = val == "Action";
+ config.highlightSyncAction = val == "Action";
}
}
@@ -1124,7 +1124,7 @@ void readConfig(const Zstring& filename, XmlType type, ConfigType& cfg, int curr
{
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)));
@@ -1376,7 +1376,7 @@ void writeConfig(const XmlGuiConfig& config, XmlOut& out)
outGuiCfg["HideExcluded" ](config.hideExcludedItems);
outGuiCfg["HandleError" ](config.handleError);
- outGuiCfg["MiddleGridView"](config.showSyncAction ? "Action" : "Category"); //refactor into enum!?
+ outGuiCfg["MiddleGridView"](config.highlightSyncAction ? "Action" : "Category"); //refactor into enum!?
}
void writeConfig(const XmlBatchConfig& config, XmlOut& out)
diff --git a/lib/process_xml.h b/lib/process_xml.h
index 8a65d67a..626fafe0 100644
--- a/lib/process_xml.h
+++ b/lib/process_xml.h
@@ -50,23 +50,23 @@ struct XmlGuiConfig
XmlGuiConfig() :
hideExcludedItems(false),
handleError(ON_GUIERROR_POPUP),
- showSyncAction(true) {} //initialize values
+ highlightSyncAction(true) {} //initialize values
zen::MainConfiguration mainCfg;
bool hideExcludedItems;
OnGuiError handleError; //reaction on error situation during synchronization
- bool showSyncAction;
+ bool highlightSyncAction;
};
inline
bool operator==(const XmlGuiConfig& lhs, const XmlGuiConfig& rhs)
{
- return lhs.mainCfg == rhs.mainCfg &&
- lhs.hideExcludedItems == rhs.hideExcludedItems &&
- lhs.handleError == rhs.handleError &&
- lhs.showSyncAction == rhs.showSyncAction;
+ return lhs.mainCfg == rhs.mainCfg &&
+ lhs.hideExcludedItems == rhs.hideExcludedItems &&
+ lhs.handleError == rhs.handleError &&
+ lhs.highlightSyncAction == rhs.highlightSyncAction;
}
diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp
index bea62da3..bf6f99a2 100644
--- a/lib/resolve_path.cpp
+++ b/lib/resolve_path.cpp
@@ -31,14 +31,15 @@ Zstring resolveRelativePath(const Zstring& relativeName) //note: ::GetFullPathNa
const DWORD bufferSize = 10000;
std::vector<wchar_t> buffer(bufferSize);
- const DWORD charsWritten = ::GetFullPathName(applyLongPathPrefix(relativeName).c_str(), //__in LPCTSTR lpFileName,
+ //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 removeLongPathPrefix(Zstring(&buffer[0], charsWritten)); //GetFullPathName() preserves long path prefix -> a low-level detail we don't want to leak out!
+ return Zstring(&buffer[0], charsWritten);
}
#elif defined FFS_LINUX || defined FFS_MAC
@@ -545,11 +546,9 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw()
{
//formatting is needed since functions expect the directory to end with '\' to be able to split the relative names.
- Zstring dirname = expandMacros(dirString);
-
- dirname = expandVolumeName(dirname); //should not block
+ Zstring dirname = dirString;
- //remove leading/trailing whitespace
+ //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix()
trim(dirname, true, false);
while (endsWith(dirname, Zstr(' '))) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name
dirname.resize(dirname.size() - 1);
@@ -557,6 +556,9 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw()
if (dirname.empty()) //an empty string would later be resolved as "\"; this is not desired
return Zstring();
+ dirname = expandMacros(dirname);
+ dirname = expandVolumeName(dirname); //should not block
+
/*
need to resolve relative paths:
WINDOWS:
diff --git a/lib/resolve_path.h b/lib/resolve_path.h
index 975ed304..4e85c8ee 100644
--- a/lib/resolve_path.h
+++ b/lib/resolve_path.h
@@ -19,7 +19,7 @@ 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!
+Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - non-blocking! no I/O! not thread-safe!!!(see ::GetFullPathName())
//macro substitution only
Zstring expandMacros(const Zstring& text);
diff --git a/lib/resources.cpp b/lib/resources.cpp
index e6691458..6f48e2e1 100644
--- a/lib/resources.cpp
+++ b/lib/resources.cpp
@@ -75,17 +75,17 @@ GlobalResources::GlobalResources()
#ifdef FFS_WIN
//for compatibility it seems we need to stick with a "real" icon
- programIcon = wxIcon(L"A_PROGRAM_ICON");
+ programIconFFS = wxIcon(L"A_FFS_ICON");
#elif defined FFS_LINUX
//attention: make sure to not implicitly call "instance()" again => deadlock on Linux
- programIcon.CopyFromBitmap(getImage(L"FreeFileSync")); //use big logo bitmap for better quality
+ programIconFFS.CopyFromBitmap(getImage(L"FreeFileSync")); //use big logo bitmap for better quality
#elif defined FFS_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
- programIcon.CopyFromBitmap(getImage(L"FreeFileSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge"
+ programIconFFS.CopyFromBitmap(getImage(L"FreeFileSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge"
#endif
}
diff --git a/lib/resources.h b/lib/resources.h
index a8d9469c..df651eaa 100644
--- a/lib/resources.h
+++ b/lib/resources.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef RESOURCES_H_INCLUDED
-#define RESOURCES_H_INCLUDED
+#ifndef RESOURCES_H_8740257825342532457
+#define RESOURCES_H_8740257825342532457
#include <map>
#include <wx/bitmap.h>
@@ -23,7 +23,7 @@ public:
//global image resource objects
wxAnimation aniWink;
wxAnimation aniSync;
- wxIcon programIcon;
+ wxIcon programIconFFS;
private:
GlobalResources();
@@ -37,4 +37,4 @@ private:
inline
const wxBitmap& getResourceImage(const wxString& name) { return GlobalResources::instance().getImage(name); }
-#endif // RESOURCES_H_INCLUDED
+#endif //RESOURCES_H_8740257825342532457
diff --git a/lib/return_codes.h b/lib/return_codes.h
index 6742c975..a37e11f2 100644
--- a/lib/return_codes.h
+++ b/lib/return_codes.h
@@ -25,8 +25,6 @@ void raiseReturnCode(FfsReturnCode& rc, FfsReturnCode rcProposed)
if (rc < rcProposed)
rc = rcProposed;
}
-
}
-
#endif // RETURN_CODES_H_INCLUDED
diff --git a/lib/shadow.cpp b/lib/shadow.cpp
index d3106168..4bb299ac 100644
--- a/lib/shadow.cpp
+++ b/lib/shadow.cpp
@@ -8,7 +8,10 @@
#include <stdexcept>
#include <zen/win.h> //includes "windows.h"
#include <zen/dll.h>
+#include <zen/win_ver.h>
#include <zen/assert_static.h>
+#include <zen/long_path_prefix.h>
+#include <zen/symlink_target.h>
#include "ShadowCopy/shadow.h"
#include <zen/scope_guard.h>
@@ -31,6 +34,8 @@ bool runningWOW64() //test if process is running under WOW64 (reference http://m
}
return false;
}
+
+const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup
}
//#############################################################################################################
@@ -51,14 +56,13 @@ public:
throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" +
_("Please use FreeFileSync 64-bit version to create shadow copies on this system."));
-
//check if shadow copy dll was loaded correctly
if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume || !getLastError)
throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" +
replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName())));
//---------------------------------------------------------------------------------------------------------
- //start shadow volume copy service:
+ //start volume shadow copy service:
backupHandle = createShadowCopy(volumeNamePf.c_str());
if (!backupHandle)
throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" +
@@ -88,22 +92,31 @@ private:
Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy)
{
+ Zstring filenameFinal = inputFile;
+
+ //try to resolve symlinks and junctions:
+ //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked!
+ //2. junctions: C:\Users\<username> is a junction that may link to e.g. D:\Users\<username>, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y!"
+ if (wereVistaOrLater)
+ filenameFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later!
+ //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix
+
DWORD bufferSize = 10000;
std::vector<wchar_t> volBuffer(bufferSize);
- if (!::GetVolumePathName(inputFile.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(inputFile)));
+ 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)));
- const Zstring volumeNamePf = appendSeparator(&volBuffer[0]);
+ const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error!
//input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found.
- const size_t pos = inputFile.find(volumeNamePf); //inputFile needs NOT to begin with volumeNamePf: consider for example \\?\ prefix!
+ 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!");
replace(msg, L"%x", fmtFileName(volumeNamePf), false);
- replace(msg, L"%y", fmtFileName(inputFile), false);
+ replace(msg, L"%y", fmtFileName(filenameFinal), false);
throw FileError(msg);
}
@@ -117,5 +130,5 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function
}
//return filename alias on shadow copy volume
- return it->second->geNamePf() + Zstring(inputFile.c_str() + pos + volumeNamePf.length());
+ return it->second->geNamePf() + Zstring(filenameFinal.c_str() + pos + volumeNamePf.length());
}
diff --git a/lib/shadow.h b/lib/shadow.h
index 4a05c860..f59d7753 100644
--- a/lib/shadow.h
+++ b/lib/shadow.h
@@ -21,7 +21,8 @@ class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as neede
public:
ShadowCopy() {}
- Zstring makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy); //throw FileError; returns filename on shadow copy
+ //return filename on shadow copy volume - follows symlinks!
+ Zstring makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy); //throw FileError
private:
ShadowCopy(const ShadowCopy&);
@@ -33,4 +34,4 @@ private:
};
}
-#endif // SHADOW_H_INCLUDED
+#endif //SHADOW_H_INCLUDED
diff --git a/lib/status_handler_impl.h b/lib/status_handler_impl.h
index 615288d2..e1212b65 100644
--- a/lib/status_handler_impl.h
+++ b/lib/status_handler_impl.h
@@ -7,28 +7,52 @@
#ifndef STATUSHANDLER_IMPL_H_INCLUDED
#define STATUSHANDLER_IMPL_H_INCLUDED
+#include <zen/optional.h>
#include <zen/file_error.h>
#include "process_callback.h"
+//template <typename Function> inline
+//bool tryReportingError(Function cmd, ProcessCallback& handler) //return "true" on success, "false" if error was ignored
+//{
+// for (;;)
+// try
+// {
+// cmd(); //throw FileError
+// return true;
+// }
+// catch (zen::FileError& error)
+// {
+// switch (handler.reportError(error.toString())) //may throw!
+// {
+// case ProcessCallback::IGNORE_ERROR:
+// return false;
+// case ProcessCallback::RETRY:
+// break; //continue with loop
+// }
+// }
+//}
+
+
template <typename Function> inline
-bool tryReportingError(Function cmd, ProcessCallback& handler) //return "true" on success, "false" if error was ignored
+zen::Opt<std::wstring> tryReportingError2(Function cmd, ProcessCallback& handler) //return ignored error message if available
{
for (;;)
try
{
cmd(); //throw FileError
- return true;
+ return zen::NoValue();
}
catch (zen::FileError& error)
{
switch (handler.reportError(error.toString())) //may throw!
{
case ProcessCallback::IGNORE_ERROR:
- return false;
+ return error.toString();
case ProcessCallback::RETRY:
break; //continue with loop
}
}
}
+
#endif //STATUSHANDLER_IMPL_H_INCLUDED
diff --git a/lib/versioning.cpp b/lib/versioning.cpp
index a72433cc..a5bd17be 100644
--- a/lib/versioning.cpp
+++ b/lib/versioning.cpp
@@ -205,10 +205,17 @@ private:
virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
{
- if (details.dirLink)
- dirs_.push_back(shortName);
- else
- files_.push_back(shortName);
+ switch (getSymlinkType(fullName))
+ {
+ case SYMLINK_TYPE_DIR:
+ dirs_.push_back(shortName);
+ break;
+
+ case SYMLINK_TYPE_FILE:
+ case SYMLINK_TYPE_UNKNOWN:
+ files_.push_back(shortName);
+ break;
+ }
return LINK_SKIP;
}
@@ -218,7 +225,8 @@ private:
return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively!
}
- virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); }
std::vector<Zstring>& files_;
std::vector<Zstring>& dirs_;
@@ -286,7 +294,7 @@ void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& rel
assert(somethingExists(sourceDir)); //[!]
//create target
- if (symlinkExists(sourceDir)) //on Linux there is just one type of symlinks, and since we do revision file symlinks, we should revision dir symlinks as well!
+ 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!
{
moveItemToVersioning(sourceDir, //throw FileError
relativeName,
@@ -355,7 +363,8 @@ private:
virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) { files_.push_back(shortName); updateUI_(); }
virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { files_.push_back(shortName); updateUI_(); return LINK_SKIP; }
virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { updateUI_(); return nullptr; } //DON'T traverse into subdirs
- virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); }
std::vector<Zstring>& files_;
std::function<void()> updateUI_;
@@ -428,4 +437,4 @@ void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileEr
});
});
}
-*/ \ No newline at end of file
+*/
bgstack15