diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:24:35 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:24:35 +0200 |
commit | 460091fb0b2ff114cc741372f15bb43b702ea3b1 (patch) | |
tree | 0562c2eda4c66969c6e6d0910080db9f5b0def3e /lib | |
parent | 5.15 (diff) | |
download | FreeFileSync-460091fb0b2ff114cc741372f15bb43b702ea3b1.tar.gz FreeFileSync-460091fb0b2ff114cc741372f15bb43b702ea3b1.tar.bz2 FreeFileSync-460091fb0b2ff114cc741372f15bb43b702ea3b1.zip |
5.16
Diffstat (limited to 'lib')
-rw-r--r-- | lib/cmp_filetime.h | 4 | ||||
-rw-r--r-- | lib/db_file.cpp | 104 | ||||
-rw-r--r-- | lib/db_file.h | 23 | ||||
-rw-r--r-- | lib/dir_exist_async.h | 7 | ||||
-rw-r--r-- | lib/dir_lock.cpp | 39 | ||||
-rw-r--r-- | lib/ffs_paths.cpp | 2 | ||||
-rw-r--r-- | lib/ffs_paths.h | 6 | ||||
-rw-r--r-- | lib/generate_logfile.h | 9 | ||||
-rw-r--r-- | lib/help_provider.h | 2 | ||||
-rw-r--r-- | lib/icon_buffer.cpp | 21 | ||||
-rw-r--r-- | lib/localization.cpp | 118 | ||||
-rw-r--r-- | lib/localization.h | 20 | ||||
-rw-r--r-- | lib/parallel_scan.cpp | 49 | ||||
-rw-r--r-- | lib/parallel_scan.h | 3 | ||||
-rw-r--r-- | lib/parse_lng.h | 128 | ||||
-rw-r--r-- | lib/parse_plural.h | 47 | ||||
-rw-r--r-- | lib/process_xml.cpp | 8 | ||||
-rw-r--r-- | lib/process_xml.h | 12 | ||||
-rw-r--r-- | lib/resolve_path.cpp | 14 | ||||
-rw-r--r-- | lib/resolve_path.h | 2 | ||||
-rw-r--r-- | lib/resources.cpp | 6 | ||||
-rw-r--r-- | lib/resources.h | 8 | ||||
-rw-r--r-- | lib/return_codes.h | 2 | ||||
-rw-r--r-- | lib/shadow.cpp | 33 | ||||
-rw-r--r-- | lib/shadow.h | 5 | ||||
-rw-r--r-- | lib/status_handler_impl.h | 30 | ||||
-rw-r--r-- | lib/versioning.cpp | 25 |
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 +*/ |