diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:36 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:36 +0200 |
commit | ecb1524f8da7901338b263384fed3c612f117b4c (patch) | |
tree | e7e06423fe27ea5ab45f27fc4b39ae597ba72490 /lib | |
parent | 5.10 (diff) | |
download | FreeFileSync-ecb1524f8da7901338b263384fed3c612f117b4c.tar.gz FreeFileSync-ecb1524f8da7901338b263384fed3c612f117b4c.tar.bz2 FreeFileSync-ecb1524f8da7901338b263384fed3c612f117b4c.zip |
5.11
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Batch.ico | bin | 116037 -> 117121 bytes | |||
-rw-r--r-- | lib/ShadowCopy/dll_main.cpp | 25 | ||||
-rw-r--r-- | lib/Thumbnail/dll_main.cpp | 25 | ||||
-rw-r--r-- | lib/db_file.cpp | 52 | ||||
-rw-r--r-- | lib/icon_buffer.cpp | 28 | ||||
-rw-r--r-- | lib/localization.cpp | 26 | ||||
-rw-r--r-- | lib/lock_holder.h | 4 | ||||
-rw-r--r-- | lib/parallel_scan.cpp | 96 | ||||
-rw-r--r-- | lib/parse_lng.h | 60 | ||||
-rw-r--r-- | lib/parse_plural.h | 12 | ||||
-rw-r--r-- | lib/perf_check.cpp | 14 | ||||
-rw-r--r-- | lib/perf_check.h | 4 | ||||
-rw-r--r-- | lib/process_xml.cpp | 157 | ||||
-rw-r--r-- | lib/process_xml.h | 28 | ||||
-rw-r--r-- | lib/resolve_path.cpp | 27 | ||||
-rw-r--r-- | lib/resources.cpp | 6 | ||||
-rw-r--r-- | lib/shadow.cpp | 8 | ||||
-rw-r--r-- | lib/versioning.cpp | 181 | ||||
-rw-r--r-- | lib/versioning.h | 20 | ||||
-rw-r--r-- | lib/xml_base.cpp | 4 |
20 files changed, 388 insertions, 389 deletions
diff --git a/lib/Batch.ico b/lib/Batch.ico Binary files differindex 1856b1fb..f742b9a3 100644 --- a/lib/Batch.ico +++ b/lib/Batch.ico diff --git a/lib/ShadowCopy/dll_main.cpp b/lib/ShadowCopy/dll_main.cpp deleted file mode 100644 index 4665154a..00000000 --- a/lib/ShadowCopy/dll_main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -//optional: add init/teardown logic here -BOOL APIENTRY DllMain(HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) -{ - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - case DLL_PROCESS_DETACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - break; - } - return TRUE; -} diff --git a/lib/Thumbnail/dll_main.cpp b/lib/Thumbnail/dll_main.cpp deleted file mode 100644 index 4665154a..00000000 --- a/lib/Thumbnail/dll_main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -//optional: add init/teardown logic here -BOOL APIENTRY DllMain(HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) -{ - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - case DLL_PROCESS_DETACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - break; - } - return TRUE; -} diff --git a/lib/db_file.cpp b/lib/db_file.cpp index f81ada21..7f8da45a 100644 --- a/lib/db_file.cpp +++ b/lib/db_file.cpp @@ -68,10 +68,10 @@ void saveStreams(const StreamMapping& streamList, const Zstring& filename) //thr //save stream list writeNumber<std::uint32_t>(streamOut, static_cast<std::uint32_t>(streamList.size())); //number of streams, one for each sync-pair - for (auto iter = streamList.begin(); iter != streamList.end(); ++iter) + for (auto it = streamList.begin(); it != streamList.end(); ++it) { - writeContainer<std::string >(streamOut, iter->first ); - writeContainer<BinaryStream>(streamOut, iter->second); + writeContainer<std::string >(streamOut, it->first ); + writeContainer<BinaryStream>(streamOut, it->second); } saveBinStream(filename, streamOut.get()); //throw FileError @@ -449,23 +449,23 @@ 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) { - auto iter = map.lower_bound(key); - if (iter != map.end() && !(map.key_comp()(key, iter->first))) + auto it = map.lower_bound(key); + if (it != map.end() && !(map.key_comp()(key, it->first))) { #ifdef FFS_WIN //caveat: key might need to be updated, too, if there is a change in short name case!!! - if (iter->first != key) + if (it->first != key) { - map.erase(iter); //don't fiddle with decrementing "iter"! - you might lose while optimizing pointlessly + 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 { - iter->second = value; - return iter->second; + it->second = value; + return it->second; } } - return map.insert(iter, typename M::value_type(key, value))->second; + return map.insert(it, typename M::value_type(key, value))->second; } void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelativeNamePf, InSyncDir::FileList& dbFiles) @@ -494,9 +494,9 @@ private: } else //not in sync: preserve last synchronous state { - auto iter = dbFiles.find(fileMap.getObjShortName()); - if (iter != dbFiles.end()) - toPreserve.insert(&iter->second); + auto it = dbFiles.find(fileMap.getObjShortName()); + if (it != dbFiles.end()) + toPreserve.insert(&it->second); } } }); @@ -533,9 +533,9 @@ private: } else //not in sync: preserve last synchronous state { - auto iter = dbLinks.find(linkMap.getObjShortName()); - if (iter != dbLinks.end()) - toPreserve.insert(&iter->second); + auto it = dbLinks.find(linkMap.getObjShortName()); + if (it != dbLinks.end()) + toPreserve.insert(&it->second); } } }); @@ -565,18 +565,18 @@ private: //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 - auto iter = insertResult.first; + auto it = insertResult.first; #ifdef FFS_WIN //caveat: key might need to be updated, too, if there is a change in short name case!!! const bool alreadyExisting = !insertResult.second; - if (alreadyExisting && iter->first != key) + if (alreadyExisting && it->first != key) { - auto oldValue = std::move(iter->second); - dbDirs.erase(iter); //don't fiddle with decrementing "iter"! - you might lose while optimizing pointlessly - iter = dbDirs.insert(InSyncDir::DirList::value_type(key, std::move(oldValue))).first; + auto oldValue = std::move(it->second); + dbDirs.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly + it = dbDirs.insert(InSyncDir::DirList::value_type(key, std::move(oldValue))).first; } #endif - InSyncDir& dir = iter->second; + InSyncDir& dir = it->second; dir.status = InSyncDir::STATUS_IN_SYNC; //update immediate directory entry toPreserve.insert(&dir); recurse(dirMap, dir); @@ -599,11 +599,11 @@ private: case DIR_LEFT_SIDE_ONLY: case DIR_RIGHT_SIDE_ONLY: { - auto iter = dbDirs.find(dirMap.getObjShortName()); - if (iter != dbDirs.end()) + auto it = dbDirs.find(dirMap.getObjShortName()); + if (it != dbDirs.end()) { - toPreserve.insert(&iter->second); - recurse(dirMap, iter->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! + toPreserve.insert(&it->second); + recurse(dirMap, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! } } break; diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp index 9274a0b4..1d391dfc 100644 --- a/lib/icon_buffer.cpp +++ b/lib/icon_buffer.cpp @@ -97,7 +97,6 @@ public: wxIcon newIcon; //attention: wxIcon uses reference counting! #ifdef FFS_WIN newIcon.SetHICON(clone.handle_); - { //this block costs ~0.04 ms ICONINFO icoInfo = {}; @@ -129,8 +128,14 @@ public: //no stretching for now //newIcon.SetSize(expectedSize, expectedSize); //icon is stretched to this size if referenced HICON differs -#elif defined FFS_LINUX // - newIcon.SetPixbuf(clone.handle_); // transfer ownership!! +#elif defined FFS_LINUX + // transfer ownership!! +#if wxCHECK_VERSION(2, 9, 4) + newIcon.CopyFromBitmap(wxBitmap(clone.handle_)); +#else + newIcon.SetPixbuf(clone.handle_); +#endif + #endif // clone.handle_ = nullptr; // return newIcon; @@ -273,8 +278,8 @@ IconHolder getGenericFileIcon(IconBuffer::IconSize sz) const int requestedSize = cvrtSize(sz); if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned! - for (auto iter = std::begin(mimeFileIcons); iter != std::end(mimeFileIcons); ++iter) - if (GdkPixbuf* pixBuf = gtk_icon_theme_load_icon(defaultTheme, *iter, requestedSize, GTK_ICON_LOOKUP_USE_BUILTIN, nullptr)) + for (auto it = std::begin(mimeFileIcons); it != std::end(mimeFileIcons); ++it) + if (GdkPixbuf* pixBuf = gtk_icon_theme_load_icon(defaultTheme, *it, requestedSize, GTK_ICON_LOOKUP_USE_BUILTIN, nullptr)) return IconHolder(pixBuf); //pass ownership (may be nullptr) return IconHolder(); #endif @@ -374,10 +379,8 @@ IconHolder getGenericDirectoryIcon(IconBuffer::IconSize sz) #endif } - //################################################################################################################################################ - //---------------------- Shared Data ------------------------- class WorkLoad { @@ -400,7 +403,8 @@ public: boost::unique_lock<boost::mutex> dummy(lockFiles); filesToLoad = newLoad; } - conditionNewFiles.notify_one(); + + conditionNewFiles.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref } @@ -411,7 +415,7 @@ private: }; -typedef std::map<Zstring, IconHolder, LessFilename> NameIconMap; //entryName/icon -> note: Zstring is thread-safe +typedef std::map<Zstring, IconHolder, LessFilename> NameIconMap; //entryName/icon -> note: Zstring is "thread-safe like an int" typedef std::queue<Zstring> IconDbSequence; //entryName class Buffer @@ -421,11 +425,11 @@ public: { boost::lock_guard<boost::mutex> dummy(lockBuffer); - auto iter = iconMappping.find(fileName); - if (iter != iconMappping.end()) + auto it = iconMappping.find(fileName); + if (it != iconMappping.end()) { if (icon != nullptr) - *icon = iter->second; + *icon = it->second; return true; } return false; diff --git a/lib/localization.cpp b/lib/localization.cpp index 5792b3d1..47ed1881 100644 --- a/lib/localization.cpp +++ b/lib/localization.cpp @@ -35,21 +35,21 @@ public: virtual std::wstring translate(const std::wstring& text) { //look for translation in buffer table - const Translation::const_iterator iter = transMapping.find(text); - if (iter != transMapping.end()) - return iter->second; + const Translation::const_iterator it = transMapping.find(text); + if (it != transMapping.end()) + return it->second; return text; //fallback } virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, int n) { - TranslationPlural::const_iterator iter = transMappingPl.find(std::make_pair(singular, plural)); - if (iter != transMappingPl.end()) + TranslationPlural::const_iterator 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>(iter->second.size())) - return iter->second[formNo]; + if (0 <= formNo && formNo < static_cast<int>(it->second.size())) + return it->second[formNo]; } return n == 1 ? singular : plural; //fallback } @@ -72,7 +72,7 @@ FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_( { inputStream = loadStream(filename); //throw XmlFileError } - catch (...) + catch (const XmlFileError&) { throw lngfile::ParsingError(0, 0); } @@ -378,10 +378,10 @@ void zen::setLanguage(int language) //throw FileError //(try to) retrieve language file wxString languageFile; - for (auto iter = ExistingTranslations::get().begin(); iter != ExistingTranslations::get().end(); ++iter) - if (iter->languageID == language) + for (auto it = ExistingTranslations::get().begin(); it != ExistingTranslations::get().end(); ++it) + if (it->languageID == language) { - languageFile = iter->languageFile; + languageFile = it->languageFile; break; } @@ -397,8 +397,8 @@ void zen::setLanguage(int language) //throw FileError { throw FileError(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), L"%x", fmtFileName(toZ(languageFile))), - L"%y", numberTo<std::wstring>(e.row)), - L"%z", numberTo<std::wstring>(e.col))); + L"%y", numberTo<std::wstring>(e.row + 1)), + L"%z", numberTo<std::wstring>(e.col + 1))); } catch (PluralForm::ParsingError&) { diff --git a/lib/lock_holder.h b/lib/lock_holder.h index 5ae2f8ae..dd997853 100644 --- a/lib/lock_holder.h +++ b/lib/lock_holder.h @@ -23,9 +23,9 @@ public: std::vector<Zstring> dirs = dirnamesFmt; vector_remove_if(dirs, [](const Zstring& dir) { return dir.empty(); }); - for (auto iter = dirs.begin(); iter != dirs.end(); ++iter) + for (auto it = dirs.begin(); it != dirs.end(); ++it) { - const Zstring& dirnameFmt = *iter; + const Zstring& dirnameFmt = *it; if (!dirExistsUpdating(dirnameFmt, allowUserInteraction_, procCallback)) continue; diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp index a31e30ee..94f6b0f4 100644 --- a/lib/parallel_scan.cpp +++ b/lib/parallel_scan.cpp @@ -138,30 +138,21 @@ Windows 7: Windows XP: 2 Threads: 42s | 11s 2 Threads: 38s | 8s => Traversing does not take any advantage of file locality so that even multiple threads operating on the same disk impose no performance overhead! (even faster on XP) -*/ - std::vector<std::set<DirectoryKey>> separateByDistinctDisk(const std::set<DirectoryKey>& dirkeys) { - //see perf note: use one thread per dirkey: - typedef std::map<int, std::set<DirectoryKey>> DiskKeyMapping; + //use one thread per physical disk: + typedef std::map<DiskInfo, std::set<DirectoryKey>> DiskKeyMapping; DiskKeyMapping tmp; - int index = 0; std::for_each(dirkeys.begin(), dirkeys.end(), - [&](const DirectoryKey& key) { tmp[++index].insert(key); }); + [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); }); - /* - //use one thread per physical disk: - typedef std::map<DiskInfo, std::set<DirectoryKey>> DiskKeyMapping; - DiskKeyMapping tmp; - std::for_each(dirkeys.begin(), dirkeys.end(), - [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); }); - */ std::vector<std::set<DirectoryKey>> buckets; std::transform(tmp.begin(), tmp.end(), std::back_inserter(buckets), [&](const DiskKeyMapping::value_type& diskToKey) { return diskToKey.second; }); return buckets; } +*/ //------------------------------------------------------------------------------------------ typedef Zbase<wchar_t, StorageRefCountThreadSafe> BasicWString; //thread safe string class for UI texts @@ -193,7 +184,7 @@ public: errorResponse.reset(); dummy.unlock(); //optimization for condition_variable::notify_one() - conditionCanReportError.notify_one(); + conditionCanReportError.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 return rv; } @@ -207,7 +198,7 @@ public: errorResponse.reset(new FillBufferCallback::HandleError(rv)); dummy.unlock(); //optimization for condition_variable::notify_one() - conditionGotResponse.notify_one(); + conditionGotResponse.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 } } @@ -470,57 +461,53 @@ private: const std::wstring textApplyingDstHack; }; #endif -//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ class WorkerThread { public: WorkerThread(size_t threadID, const std::shared_ptr<AsyncCallback>& acb, - const std::vector<std::pair<DirectoryKey, DirectoryValue*>>& workload) : + const DirectoryKey& dirKey, + DirectoryValue& dirOutput) : threadID_(threadID), acb_(acb), - workload_(workload) {} + dirKey_(dirKey), + dirOutput_(dirOutput) {} void operator()() //thread entry { acb_->incActiveWorker(); ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker();); - std::for_each(workload_.begin(), workload_.end(), - [&](std::pair<DirectoryKey, DirectoryValue*>& item) - { - const Zstring& directoryName = item.first.dirnameFull_; - DirectoryValue& dirVal = *item.second; - - acb_->reportCurrentFile(directoryName, threadID_); //just in case first directory access is blocking + acb_->reportCurrentFile(dirKey_.dirnameFull_, threadID_); //just in case first directory access is blocking - TraverserShared travCfg(threadID_, - item.first.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy - item.first.filter_, - dirVal.failedReads, - *acb_); + TraverserShared travCfg(threadID_, + dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy + dirKey_.filter_, + dirOutput_.failedReads, + *acb_); - DirCallback traverser(travCfg, - Zstring(), - dirVal.dirCont); + DirCallback traverser(travCfg, + Zstring(), + dirOutput_.dirCont); - DstHackCallback* dstCallbackPtr = nullptr; + DstHackCallback* dstCallbackPtr = nullptr; #ifdef FFS_WIN - DstHackCallbackImpl dstCallback(*acb_, threadID_); - dstCallbackPtr = &dstCallback; + DstHackCallbackImpl dstCallback(*acb_, threadID_); + dstCallbackPtr = &dstCallback; #endif - //get all files and folders from directoryPostfixed (and subdirectories) - traverseFolder(directoryName, traverser, dstCallbackPtr); //exceptions may be thrown! - }); + //get all files and folders from directoryPostfixed (and subdirectories) + traverseFolder(dirKey_.dirnameFull_, traverser, dstCallbackPtr); //exceptions may be thrown! } private: size_t threadID_; std::shared_ptr<AsyncCallback> acb_; - std::vector<std::pair<DirectoryKey, DirectoryValue*>> workload_; + const DirectoryKey dirKey_; + DirectoryValue& dirOutput_; }; } @@ -532,13 +519,11 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in { buf.clear(); - std::vector<std::set<DirectoryKey>> buckets = separateByDistinctDisk(keysToRead); //one bucket per physical device - FixedList<boost::thread> worker; //note: we cannot use std::vector<boost::thread>: compiler error on GCC 4.7, probably a boost screw-up zen::ScopeGuard guardWorker = zen::makeGuard([&] { - std::for_each(worker.begin(), worker.end(), [](boost::thread& wt) { wt.interrupt(); }); //interrupt all at once, then join + std::for_each(worker.begin(), worker.end(), [](boost::thread& wt) { wt.interrupt(); }); //interrupt all at once first, then join std::for_each(worker.begin(), worker.end(), [](boost::thread& wt) { if (wt.joinable()) //= precondition of thread::join(), which throws an exception if violated! @@ -549,28 +534,21 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in std::shared_ptr<AsyncCallback> acb = std::make_shared<AsyncCallback>(); //init worker threads - for (auto iter = buckets.begin(); iter != buckets.end(); ++iter) + std::for_each(keysToRead.begin(), keysToRead.end(), + [&](const DirectoryKey& key) { - const std::set<DirectoryKey>& bucket = *iter; - - std::vector<std::pair<DirectoryKey, DirectoryValue*>> workload; - std::for_each(bucket.begin(), bucket.end(), - [&](const DirectoryKey& key) - { - auto rv = buf.insert(std::make_pair(key, DirectoryValue())); - assert(rv.second); - workload.push_back(std::make_pair(key, &rv.first->second)); - }); + assert(buf.find(key) == buf.end()); + DirectoryValue& dirOutput = buf[key]; - const size_t threadId = iter - buckets.begin(); - worker.emplace_back(WorkerThread(threadId, acb, workload)); - } + const size_t threadId = worker.size(); + worker.emplace_back(WorkerThread(threadId, acb, key, dirOutput)); + }); //wait until done size_t threadId = 0; - for (auto iter = worker.begin(); iter != worker.end(); ++iter, ++threadId) + for (auto it = worker.begin(); it != worker.end(); ++it, ++threadId) { - boost::thread& wt = *iter; + boost::thread& wt = *it; acb->setNotifyingThread(threadId); //process info messages of first (active) thread only diff --git a/lib/parse_lng.h b/lib/parse_lng.h index 5eb135a3..92564a1e 100644 --- a/lib/parse_lng.h +++ b/lib/parse_lng.h @@ -44,8 +44,8 @@ struct TransHeader struct ParsingError { ParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {} - size_t row; - size_t col; + size_t row; //starting with 0 + size_t col; // }; void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut); //throw ParsingError void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError @@ -104,11 +104,11 @@ public: bool untranslatedTextExists() const { - for (std::list<RegularItem>::const_iterator i = dump.begin(); i != dump.end(); ++i) - if (i->value.second.empty()) + for (auto it = dump.begin(); it != dump.end(); ++it) + if (it->value.second.empty()) return true; - for (std::list<PluralItem>::const_iterator i = dumpPlural.begin(); i != dumpPlural.end(); ++i) - if (i->value.second.empty()) + for (auto it = dumpPlural.begin(); it != dumpPlural.end(); ++it) + if (it->value.second.empty()) return true; return false; } @@ -180,8 +180,8 @@ public: static std::string text(Token::Type t) { - TokenMap::const_iterator iter = asList().find(t); - return iter != asList().end() ? iter->second : std::string(); + TokenMap::const_iterator it = asList().find(t); + return it != asList().end() ? it->second : std::string(); } private: @@ -198,8 +198,8 @@ private: tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "</locale>")); tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "<flag file>")); tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "</flag file>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "<plural forms>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "</plural forms>")); + tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "<plural forms>")); + tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "</plural forms>")); tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "<plural definition>")); tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "</plural definition>")); @@ -252,28 +252,42 @@ public: return out; } - std::pair<size_t, size_t> position() const //current (row/col) beginning with 1 + size_t posRow() const //current row beginning with 0 { - //seek last line break - std::string::const_iterator iter = pos; - while (iter != stream.begin() && *iter != '\n') - --iter; + //count line endings + size_t crSum = 0; //carriage returns + size_t nlSum = 0; //new lines + for (auto it = stream.begin(); it != pos; ++it) + if (*it == '\r') + ++crSum; + else if (*it == '\n') + ++nlSum; + assert(crSum == 0 || nlSum == 0 || crSum == nlSum); + return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win + } - return std::make_pair(std::count(stream.begin(), pos, '\n') + 1, pos - iter); + size_t posCol() const //current col beginning with 0 + { + //seek beginning of line + for (auto it = pos; it != stream.begin(); ) + { + --it; + if (*it == '\r' || *it == '\n') + return pos - it - 1; + } + return pos - stream.begin(); } private: bool startsWithKnownTag() const { - for (KnownTokens::TokenMap::const_iterator i = KnownTokens::asList().begin(); i != KnownTokens::asList().end(); ++i) - if (startsWith(i->second)) - return true; - return false; + return std::any_of(KnownTokens::asList().begin(), KnownTokens::asList().end(), + [&](const KnownTokens::TokenMap::value_type& p) { return startsWith(p.second); }); } bool startsWith(const std::string& prefix) const { - if (stream.end() - pos < static_cast<int>(prefix.size())) + if (stream.end() - pos < static_cast<ptrdiff_t>(prefix.size())) return false; return std::equal(prefix.begin(), prefix.end(), pos); } @@ -420,7 +434,7 @@ private: } if (!pluralList.empty() && static_cast<int>(pluralList.size()) != formCount) //invalid number of plural forms - throw ParsingError(scn.position().first, scn.position().second); + throw ParsingError(scn.posRow(), scn.posCol()); consumeToken(Token::TK_TRG_END); @@ -435,7 +449,7 @@ private: void consumeToken(Token::Type t) { if (token().type != t) - throw ParsingError(scn.position().first, scn.position().second); + throw ParsingError(scn.posRow(), scn.posCol()); nextToken(); } diff --git a/lib/parse_plural.h b/lib/parse_plural.h index 2b78de89..7af6809e 100644 --- a/lib/parse_plural.h +++ b/lib/parse_plural.h @@ -152,6 +152,7 @@ private: }; Token(Type t) : type(t), number(0) {} + Token(int num) : type(TK_CONST_NUMBER), number(num) {} Type type; int number; //if type == TK_CONST_NUMBER @@ -195,13 +196,12 @@ private: } auto digitEnd = std::find_if(pos, stream.end(), [](char c) { return !zen::isDigit(c); }); - ptrdiff_t digitCount = digitEnd - pos; - if (digitCount != 0) + + if (digitEnd != pos) { - Token out(Token::TK_CONST_NUMBER); - out.number = zen::stringTo<int>(std::string(&*pos, digitCount)); - pos += digitCount; - return out; + int number = zen::stringTo<int>(std::string(&*pos, digitEnd - pos)); + pos = digitEnd; + return number; } throw ParsingError(); //unknown token diff --git a/lib/perf_check.cpp b/lib/perf_check.cpp index 878b41a2..c6a4e2d1 100644 --- a/lib/perf_check.cpp +++ b/lib/perf_check.cpp @@ -15,8 +15,8 @@ using namespace zen; -PerfCheck::PerfCheck(unsigned windowSizeRemainingTime, - unsigned windowSizeBytesPerSecond) : +PerfCheck::PerfCheck(unsigned int windowSizeRemainingTime, + unsigned int windowSizeBytesPerSecond) : windowSizeRemTime(windowSizeRemainingTime), windowSizeBPS(windowSizeBytesPerSecond), windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)) {} @@ -30,13 +30,13 @@ PerfCheck::~PerfCheck() outputFile.Write(wxT("Time(ms);Objects;Data\n")); - for (auto iter = samples.begin(); iter != samples.end(); ++iter) + for (auto it = samples.begin(); it != samples.end(); ++it) { - outputFile.Write(numberTo<wxString>(iter->first)); + outputFile.Write(numberTo<wxString>(it->first)); outputFile.Write(wxT(";")); - outputFile.Write(numberTo<wxString>(iter->second.objCount_)); + outputFile.Write(numberTo<wxString>(it->second.objCount_)); outputFile.Write(wxT(";")); - outputFile.Write(numberTo<wxString>(iter->second.data_)); + outputFile.Write(numberTo<wxString>(it->second.data_)); outputFile.Write(wxT("\n")); } */ @@ -108,6 +108,8 @@ wxString PerfCheck::getBytesPerSecond() const wxString PerfCheck::getOverallBytesPerSecond() const //for all samples { + warn_static("WTF!? tihs considers window only!") + if (!samples.empty()) { const auto& recordBack = *samples.rbegin(); diff --git a/lib/perf_check.h b/lib/perf_check.h index b8014582..b60c31c9 100644 --- a/lib/perf_check.h +++ b/lib/perf_check.h @@ -13,8 +13,8 @@ class PerfCheck { public: - PerfCheck(unsigned windowSizeRemainingTime, //unit: [ms] - unsigned windowSizeBytesPerSecond); // + PerfCheck(unsigned int windowSizeRemainingTime, //unit: [ms] + unsigned int windowSizeBytesPerSecond); // ~PerfCheck(); void addSample(int objectsCurrent, double dataCurrent, long timeMs); //timeMs must be ascending! diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp index aed9c35f..a11f841c 100644 --- a/lib/process_xml.cpp +++ b/lib/process_xml.cpp @@ -93,7 +93,7 @@ void xmlAccess::OptionalDialogs::resetDialogs() } -xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) +xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) //noexcept { XmlGuiConfig output; output.mainCfg = batchCfg.mainCfg; @@ -112,9 +112,10 @@ xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchCo } -xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceFile) +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) //noexcept { XmlBatchConfig output; //use default batch-settings + output.mainCfg = guiCfg.mainCfg; switch (guiCfg.handleError) { @@ -125,19 +126,25 @@ xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiCo output.handleError = ON_ERROR_IGNORE; break; } + return output; +} + - //try to take over batch-specific settings from reference - if (!referenceFile.empty() && getXmlType(referenceFile) == XML_TYPE_BATCH) +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile) //noexcept +{ + //try to take over batch-specific settings from reference file if possible + if (!referenceBatchFile.empty()) try { - std::vector<Zstring> filenames; - filenames.push_back(referenceFile); - mergeConfigs(filenames, output); //throw xmlAccess::FfsXmlError + XmlBatchConfig batchCfg; + readConfig(referenceBatchFile, batchCfg); //throw FfsXmlError + + batchCfg.mainCfg = guiCfg.mainCfg; + return batchCfg; } catch (xmlAccess::FfsXmlError&) {} - output.mainCfg = guiCfg.mainCfg; - return output; + return convertGuiToBatch(guiCfg); } @@ -146,9 +153,9 @@ xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<Zstring>& filenam bool guiCfgExists = false; bool batchCfgExists = false; - for (auto iter = filenames.begin(); iter != filenames.end(); ++iter) + for (auto it = filenames.begin(); it != filenames.end(); ++it) { - switch (xmlAccess::getXmlType(*iter)) //throw() + switch (xmlAccess::getXmlType(*it)) //throw() { case XML_TYPE_GUI: guiCfgExists = true; @@ -164,59 +171,60 @@ xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<Zstring>& filenam } } - if (guiCfgExists && batchCfgExists) - return MERGE_GUI_BATCH; - else if (guiCfgExists && !batchCfgExists) - return MERGE_GUI; - else if (!guiCfgExists && batchCfgExists) - return MERGE_BATCH; + if (guiCfgExists) + return batchCfgExists ? MERGE_GUI_BATCH : MERGE_GUI; else - return MERGE_OTHER; + return batchCfgExists ? MERGE_BATCH : MERGE_OTHER; } namespace { template <class XmlCfg> -XmlCfg loadCfgImpl(const Zstring& filename, std::unique_ptr<xmlAccess::FfsXmlError>& warning) //throw xmlAccess::FfsXmlError +XmlCfg readConfigNoWarnings(const Zstring& filename, std::unique_ptr<FfsXmlError>& warning) //throw FfsXmlError, but only if "FATAL" { XmlCfg cfg; try { - xmlAccess::readConfig(filename, cfg); //throw xmlAccess::FfsXmlError + readConfig(filename, cfg); //throw xmlAccess::FfsXmlError } - catch (const xmlAccess::FfsXmlError& e) + catch (const FfsXmlError& e) { - if (e.getSeverity() == xmlAccess::FfsXmlError::FATAL) + if (e.getSeverity() == FfsXmlError::FATAL) throw; - else - warning.reset(new xmlAccess::FfsXmlError(e)); + else if (!warning.get()) warning = make_unique<FfsXmlError>(e); } return cfg; } +} -template <class XmlCfg> -void mergeConfigFilesImpl(const std::vector<Zstring>& filenames, XmlCfg& config) //throw xmlAccess::FfsXmlError +void xmlAccess::readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config) //throw FfsXmlError { assert(!filenames.empty()); - if (filenames.empty()) - return; std::vector<zen::MainConfiguration> mainCfgs; std::unique_ptr<FfsXmlError> savedWarning; - std::for_each(filenames.begin(), filenames.end(), - [&](const Zstring& filename) + for (auto it = filenames.begin(); it != filenames.end(); ++it) { + const Zstring& filename = *it; + const bool firstLine = it == filenames.begin(); //init all non-"mainCfg" settings with first config file + switch (getXmlType(filename)) { case XML_TYPE_GUI: - mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(filename, savedWarning).mainCfg); //throw xmlAccess::FfsXmlError + if (firstLine) + config = readConfigNoWarnings<XmlGuiConfig>(filename, savedWarning); + else + mainCfgs.push_back(readConfigNoWarnings<XmlGuiConfig>(filename, savedWarning).mainCfg); //throw FfsXmlError break; case XML_TYPE_BATCH: - mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(filename, savedWarning).mainCfg); //throw xmlAccess::FfsXmlError + if (firstLine) + config = convertBatchToGui(readConfigNoWarnings<XmlBatchConfig>(filename, savedWarning)); + else + mainCfgs.push_back(readConfigNoWarnings<XmlBatchConfig>(filename, savedWarning).mainCfg); //throw FfsXmlError break; case XML_TYPE_GLOBAL: @@ -226,32 +234,14 @@ void mergeConfigFilesImpl(const std::vector<Zstring>& filenames, XmlCfg& config) else throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); } - }); - - try //...to init all non-"mainCfg" settings with first config file - { - xmlAccess::readConfig(filenames[0], config); //throw xmlAccess::FfsXmlError } - catch (xmlAccess::FfsXmlError&) {} + mainCfgs.push_back(config.mainCfg); //save cfg from first line config.mainCfg = merge(mainCfgs); if (savedWarning.get()) //"re-throw" exception throw* savedWarning; } -} - - -void xmlAccess::mergeConfigs(const std::vector<Zstring>& filenames, XmlGuiConfig& config) //throw FfsXmlError -{ - mergeConfigFilesImpl(filenames, config); //throw FfsXmlError -} - - -void xmlAccess::mergeConfigs(const std::vector<Zstring>& filenames, XmlBatchConfig& config) //throw FfsXmlError -{ - mergeConfigFilesImpl(filenames, config); //throw FfsXmlError -} namespace zen @@ -662,6 +652,35 @@ bool readText(const std::string& input, UnitSize& value) template <> inline +void writeText(const VersioningStyle& value, std::string& output) +{ + switch (value) + { + case VER_STYLE_REPLACE: + output = "Replace"; + break; + case VER_STYLE_ADD_TIMESTAMP: + output = "AddTimeStamp"; + break; + } +} + +template <> inline +bool readText(const std::string& input, VersioningStyle& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "Replace") + value = VER_STYLE_REPLACE; + else if (tmp == "AddTimeStamp") + value = VER_STYLE_ADD_TIMESTAMP; + else + return false; + return true; +} + + +template <> inline void writeText(const DirectionConfig::Variant& value, std::string& output) { switch (value) @@ -776,15 +795,14 @@ void readConfig(const XmlIn& in, SyncConfig& syncCfg) warn_static("remove after migration?") if (in["CustomDeletionFolder"]) - { in["CustomDeletionFolder"](syncCfg.versioningDirectory);//obsolete name - syncCfg.versionCountLimit = -1; //new parameter - } else - { in["VersioningFolder"](syncCfg.versioningDirectory); - in["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit); - } + warn_static("remove after migration?") + if (in["VersioningStyle"]) //new parameter + in["VersioningStyle"](syncCfg.versioningStyle); + else + syncCfg.versioningStyle = VER_STYLE_ADD_TIMESTAMP; //obsolete fallback } @@ -893,12 +911,14 @@ void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) warn_static("remove after migration?") if (inGuiCfg["HideFiltered" ]) //obsolete name + inGuiCfg["HideFiltered" ](config.hideExcludedItems); + else if (inGuiCfg["ShowFiltered" ]) //obsolete name { - inGuiCfg["HideFiltered" ](config.showFilteredElements); - config.showFilteredElements = !config.showFilteredElements; + inGuiCfg["ShowFiltered"](config.hideExcludedItems); + config.hideExcludedItems = !config.hideExcludedItems; } else - inGuiCfg["ShowFiltered"](config.showFilteredElements); + inGuiCfg["HideExcluded"](config.hideExcludedItems); inGuiCfg["HandleError" ](config.handleError); inGuiCfg["SyncPreviewActive"](config.showSyncAction); @@ -1035,12 +1055,12 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) warn_static("remove after migration?") //convert new internal macro naming convention: %name -> %item_path%; %dir -> %item_folder% - for (auto iter = config.gui.externelApplications.begin(); iter != config.gui.externelApplications.end(); ++iter) + for (auto it = config.gui.externelApplications.begin(); it != config.gui.externelApplications.end(); ++it) { - replace(iter->second, L"%nameCo", L"%item2_path%"); //unambiguous "Co" names first - replace(iter->second, L"%dirCo" , L"%item2_folder%"); - replace(iter->second, L"%name" , L"%item_path%"); - replace(iter->second, L"%dir" , L"%item_folder%"); + replace(it->second, L"%nameCo", L"%item2_path%"); //unambiguous "Co" names first + replace(it->second, L"%dirCo" , L"%item2_folder%"); + replace(it->second, L"%name" , L"%item_path%"); + replace(it->second, L"%dir" , L"%item_folder%"); } @@ -1056,7 +1076,7 @@ template <class ConfigType> void readConfig(const Zstring& filename, XmlType type, ConfigType& config) { XmlDoc doc; - loadXmlDocument(filename, doc); //throw FfsXmlError + 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))); @@ -1119,7 +1139,8 @@ void writeConfig(const SyncConfig& syncCfg, XmlOut& out) out["DeletionPolicy" ](syncCfg.handleDeletion); out["VersioningFolder"](syncCfg.versioningDirectory); - out["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit); + //out["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit); + out["VersioningStyle"](syncCfg.versioningStyle); } @@ -1213,7 +1234,7 @@ void writeConfig(const XmlGuiConfig& config, XmlOut& out) //write GUI specific config data XmlOut outGuiCfg = out["GuiConfig"]; - outGuiCfg["ShowFiltered" ](config.showFilteredElements); + outGuiCfg["HideExcluded" ](config.hideExcludedItems); outGuiCfg["HandleError" ](config.handleError); outGuiCfg["SyncPreviewActive"](config.showSyncAction); } diff --git a/lib/process_xml.h b/lib/process_xml.h index 8d1d4538..29237081 100644 --- a/lib/process_xml.h +++ b/lib/process_xml.h @@ -48,13 +48,13 @@ typedef std::vector<std::pair<Description, Commandline> > ExternalApps; struct XmlGuiConfig { XmlGuiConfig() : - showFilteredElements(true), + hideExcludedItems(false), handleError(ON_GUIERROR_POPUP), showSyncAction(true) {} //initialize values zen::MainConfiguration mainCfg; - bool showFilteredElements; + bool hideExcludedItems; OnGuiError handleError; //reaction on error situation during synchronization bool showSyncAction; }; @@ -63,10 +63,10 @@ struct XmlGuiConfig inline bool operator==(const XmlGuiConfig& lhs, const XmlGuiConfig& rhs) { - return lhs.mainCfg == rhs.mainCfg && - lhs.showFilteredElements == rhs.showFilteredElements && - lhs.handleError == rhs.handleError && - lhs.showSyncAction == rhs.showSyncAction; + return lhs.mainCfg == rhs.mainCfg && + lhs.hideExcludedItems == rhs.hideExcludedItems && + lhs.handleError == rhs.handleError && + lhs.showSyncAction == rhs.showSyncAction; } @@ -232,6 +232,7 @@ struct XmlGlobalSettings //struct Batch }; +//read/write specific config types void readConfig(const Zstring& filename, XmlGuiConfig& config); // void readConfig(const Zstring& filename, XmlBatchConfig& config); //throw FfsXmlError void readConfig( XmlGlobalSettings& config); // @@ -240,11 +241,6 @@ void writeConfig(const XmlGuiConfig& config, const Zstring& filename); // void writeConfig(const XmlBatchConfig& config, const Zstring& filename); //throw FfsXmlError void writeConfig(const XmlGlobalSettings& config); // -//config conversion utilities -XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); -XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg, const Zstring& referenceFile); - - //convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: enum MergeType { @@ -253,10 +249,14 @@ enum MergeType MERGE_GUI_BATCH, //gui and batch files MERGE_OTHER }; -MergeType getMergeType(const std::vector<Zstring>& filenames); //throw () +MergeType getMergeType(const std::vector<Zstring>& filenames); //noexcept -void mergeConfigs(const std::vector<Zstring>& filenames, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError -void mergeConfigs(const std::vector<Zstring>& filenames, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError +void readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config); //throw FfsXmlError + +//config conversion utilities +XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); //noexcept +XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg ); // +XmlBatchConfig convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile); //noexcept std::wstring extractJobName(const Zstring& configFilename); } diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp index 248e3507..9c690c9a 100644 --- a/lib/resolve_path.cpp +++ b/lib/resolve_path.cpp @@ -212,6 +212,9 @@ std::unique_ptr<Zstring> resolveMacro(const Zstring& macro, //macro without %-ch if (equalNoCase(macro, Zstr("date"))) return make_unique<Zstring>(formatTime<Zstring>(FORMAT_ISO_DATE)); + if (equalNoCase(macro, Zstr("timestamp"))) + return make_unique<Zstring>(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"))); //e.g. "2012-05-15 131513" + std::unique_ptr<Zstring> cand; auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool { @@ -224,7 +227,7 @@ std::unique_ptr<Zstring> resolveMacro(const Zstring& macro, //macro without %-ch if (processPhrase(Zstr("weekday"), Zstr("%A"))) return cand; if (processPhrase(Zstr("day" ), Zstr("%d"))) return cand; - if (processPhrase(Zstr("month" ), Zstr("%B"))) return cand; + if (processPhrase(Zstr("month" ), Zstr("%m"))) return cand; if (processPhrase(Zstr("week" ), Zstr("%U"))) return cand; if (processPhrase(Zstr("year" ), Zstr("%Y"))) return cand; if (processPhrase(Zstr("hour" ), Zstr("%H"))) return cand; @@ -233,9 +236,9 @@ std::unique_ptr<Zstring> resolveMacro(const Zstring& macro, //macro without %-ch //check domain-specific extensions { - auto iter = std::find_if(ext.begin(), ext.end(), [&](const std::pair<Zstring, Zstring>& p) { return equalNoCase(macro, p.first); }); - if (iter != ext.end()) - return make_unique<Zstring>(iter->second); + auto it = std::find_if(ext.begin(), ext.end(), [&](const std::pair<Zstring, Zstring>& p) { return equalNoCase(macro, p.first); }); + if (it != ext.end()) + return make_unique<Zstring>(it->second); } //try to resolve as environment variable @@ -246,9 +249,9 @@ std::unique_ptr<Zstring> resolveMacro(const Zstring& macro, //macro without %-ch //try to resolve as CSIDL value { const auto& csidlMap = CsidlConstants::get(); - auto iter = csidlMap.find(macro); - if (iter != csidlMap.end()) - return make_unique<Zstring>(iter->second); + auto it = csidlMap.find(macro); + if (it != csidlMap.end()) + return make_unique<Zstring>(it->second); } #endif @@ -325,9 +328,9 @@ Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on //search for matching path in parallel until first hit RunUntilFirstHit<Zstring> findFirstMatch; - for (const wchar_t* iter = &buffer[0]; *iter != 0; iter += strLength(iter) + 1) //list terminated by empty c-string + for (const wchar_t* it = &buffer[0]; *it != 0; it += strLength(it) + 1) //list terminated by empty c-string { - const Zstring path = iter; + const Zstring path = it; findFirstMatch.addJob([path, volumeName]() -> std::unique_ptr<Zstring> { @@ -364,9 +367,9 @@ Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on TraverseMedia traverser(deviceList); traverseFolder("/media", traverser); //traverse one level - TraverseMedia::DeviceList::const_iterator iter = deviceList.find(volumeName); - if (iter != deviceList.end()) - return iter->second; + TraverseMedia::DeviceList::const_iterator it = deviceList.find(volumeName); + if (it != deviceList.end()) + return it->second; #endif return Zstring(); } diff --git a/lib/resources.cpp b/lib/resources.cpp index 6d586e54..7d46739e 100644 --- a/lib/resources.cpp +++ b/lib/resources.cpp @@ -86,11 +86,11 @@ GlobalResources::GlobalResources() const wxBitmap& GlobalResources::getImageInt(const wxString& imageName) const { - auto iter = bitmaps.find(!contains(imageName, L'.') ? //assume .png ending if nothing else specified + auto it = bitmaps.find(!contains(imageName, L'.') ? //assume .png ending if nothing else specified imageName + L".png" : imageName); - if (iter != bitmaps.end()) - return iter->second; + if (it != bitmaps.end()) + return it->second; else { assert(false); diff --git a/lib/shadow.cpp b/lib/shadow.cpp index 8ef86f30..6dae97b1 100644 --- a/lib/shadow.cpp +++ b/lib/shadow.cpp @@ -108,13 +108,13 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) } //get or create instance of shadow volume - VolNameShadowMap::const_iterator iter = shadowVol.find(volumeNamePf); - if (iter == shadowVol.end()) + VolNameShadowMap::const_iterator it = shadowVol.find(volumeNamePf); + if (it == shadowVol.end()) { auto newEntry = std::make_shared<ShadowVolume>(volumeNamePf); - iter = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first; + it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first; } //return filename alias on shadow copy volume - return iter->second->geNamePf() + Zstring(inputFile.c_str() + pos + volumeNamePf.length()); + return it->second->geNamePf() + Zstring(inputFile.c_str() + pos + volumeNamePf.length()); } diff --git a/lib/versioning.cpp b/lib/versioning.cpp index b12d7307..d4b6e2b2 100644 --- a/lib/versioning.cpp +++ b/lib/versioning.cpp @@ -20,14 +20,14 @@ Zstring getExtension(const Zstring& relativeName) //including "." if extension i bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt") { - auto iter = shortnameVersion.begin(); + auto it = shortnameVersion.begin(); auto last = shortnameVersion.end(); auto nextDigit = [&]() -> bool { - if (iter == last || !isDigit(*iter)) + if (it == last || !isDigit(*it)) return false; - ++iter; + ++it; return true; }; auto nextDigits = [&](size_t count) -> bool @@ -39,16 +39,16 @@ bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameV }; auto nextChar = [&](Zchar c) -> bool { - if (iter == last || *iter != c) + if (it == last || *it != c) return false; - ++iter; + ++it; return true; }; auto nextStringI = [&](const Zstring& str) -> bool //windows: ignore case! { - if (last - iter < static_cast<ptrdiff_t>(str.size()) || !EqualFilename()(str, Zstring(&*iter, str.size()))) + if (last - it < static_cast<ptrdiff_t>(str.size()) || !EqualFilename()(str, Zstring(&*it, str.size()))) return false; - iter += str.size(); + it += str.size(); return true; }; @@ -62,25 +62,41 @@ bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameV nextChar(Zstr(' ')) && // nextDigits(6) && //HHMMSS nextStringI(getExtension(shortname)) && - iter == last; + it == last; } namespace { +/* +- handle not existing source +- create target super directories if missing +*/ template <class Function> void moveItemToVersioning(const Zstring& sourceObj, //throw FileError const Zstring& relativeName, const Zstring& versioningDirectory, const Zstring& timestamp, - Function moveObj) //move source -> target; allowed to throw FileError + VersioningStyle versioningStyle, + Function moveObj) //move source -> target; may throw FileError { assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); + assert(!endsWith (relativeName, FILE_NAME_SEPARATOR)); assert(endsWith(sourceObj, relativeName)); //usually, yes, but we might relax this in the future - //assemble time-stamped version name - const Zstring targetObj = appendSeparator(versioningDirectory) + relativeName + Zstr(' ') + timestamp + getExtension(relativeName); - assert(impl::isMatchingVersion(afterLast(relativeName, FILE_NAME_SEPARATOR), afterLast(targetObj, FILE_NAME_SEPARATOR))); //paranoid? no! + Zstring targetObj; + switch (versioningStyle) + { + case VER_STYLE_REPLACE: + targetObj = appendSeparator(versioningDirectory) + relativeName; + break; + + case VER_STYLE_ADD_TIMESTAMP: + //assemble time-stamped version name + targetObj = appendSeparator(versioningDirectory) + relativeName + Zstr(' ') + timestamp + getExtension(relativeName); + assert(impl::isMatchingVersion(afterLast(relativeName, FILE_NAME_SEPARATOR), afterLast(targetObj, FILE_NAME_SEPARATOR))); //paranoid? no! + break; + } try { @@ -104,11 +120,18 @@ void moveItemToVersioning(const Zstring& sourceObj, //throw FileError } -//move source to target across volumes; prerequisite: all super-directories of target exist -//if target already contains some files/dirs they are seen as remnants of a previous incomplete move -void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile& callback) //throw FileError +//move source to target across volumes +//no need to check if: - super-directories of target exist - source exists +//if target already exists, it is overwritten, even if it is a different type, e.g. a directory! +template <class Function> +void moveObject(const Zstring& sourceFile, //throw FileError + const Zstring& targetFile, + Function copyDelete) //fallback if move failed; may throw FileError { + assert(!dirExists(sourceFile) || symlinkExists(sourceFile)); //we process files and symlinks only + //first try to move directly without copying + bool targetExisting = false; try { renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting @@ -116,59 +139,59 @@ void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopy } //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file) catch (const ErrorDifferentVolume&) {} - catch (const ErrorTargetExisting&) {} + catch (const ErrorTargetExisting&) { targetExisting = true; } - //create target - if (!fileExists(targetFile)) //check even if ErrorTargetExisting: me may have clashed with another item type of the same name!!! + if (!targetExisting) + targetExisting = somethingExists(targetFile); + + //remove target object + if (targetExisting) + { + if (fileExists(targetFile)) //file or symlink + removeFile(targetFile); //throw FileError + else if (dirExists(targetFile)) //directory or symlink + removeDirectory(targetFile); //throw FileError + //we do not expect targetFile to be a directory in general => no callback required + else assert(false); + } + + copyDelete(); +} + + +void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile& callback) //throw FileError +{ + moveObject(sourceFile, //throw FileError + targetFile, + [&] { - //file is on a different volume: let's copy it + //create target if (symlinkExists(sourceFile)) copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions else copyFile(sourceFile, targetFile, false, true, &callback); //throw FileError - permissions "false", transactional copy "true" - } - //delete source - removeFile(sourceFile); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + //delete source + removeFile(sourceFile); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + }); } void moveDirSymlink(const Zstring& sourceLink, const Zstring& targetLink) //throw FileError { - //first try to move directly without copying - try + moveObject(sourceLink, //throw FileError + targetLink, + [&] { - renameFile(sourceLink, targetLink); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - return; //great, we get away cheaply! - } - //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file) - catch (const ErrorDifferentVolume&) {} - catch (const ErrorTargetExisting&) {} - - //create target - if (!symlinkExists(targetLink)) //check even if ErrorTargetExisting: me may have clashed with another item type of the same name!!! - { - //link is on a different volume: let's copy it + //create target copySymlink(sourceLink, targetLink, false); //throw FileError; don't copy filesystem permissions - } - //delete source - removeDirectory(sourceLink); //throw FileError; newly copied link is NOT deleted if exception is thrown here! + //delete source + removeDirectory(sourceLink); //throw FileError; newly copied link is NOT deleted if exception is thrown here! + }); } -struct CopyCallbackImpl : public CallbackCopyFile -{ - CopyCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} - -private: - virtual void deleteTargetFile(const Zstring& targetFile) { assert(!somethingExists(targetFile)); } - virtual void updateCopyStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } - - CallbackMoveFile& callback_; -}; - - class TraverseFilesOneLevel : public TraverseCallback { public: @@ -200,18 +223,6 @@ private: std::vector<Zstring>& files_; std::vector<Zstring>& dirs_; }; - - -struct RemoveCallbackImpl : public CallbackRemoveDir -{ - RemoveCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} - -private: - virtual void notifyFileDeletion(const Zstring& filename) { callback_.updateStatus(0); } - virtual void notifyDirDeletion (const Zstring& dirname ) { callback_.updateStatus(0); } - - CallbackMoveFile& callback_; -}; } @@ -221,25 +232,29 @@ void FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relat relativeName, versioningDirectory_, timeStamp_, + versioningStyle_, [&](const Zstring& source, const Zstring& target) { - callback.onBeforeFileMove(source, target); + struct CopyCallbackImpl : public CallbackCopyFile + { + CopyCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} + private: + virtual void deleteTargetFile(const Zstring& targetFile) { assert(!somethingExists(targetFile)); } + virtual void updateCopyStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } + CallbackMoveFile& callback_; + } copyCallback(callback); - CopyCallbackImpl copyCallback(callback); + callback.onBeforeFileMove(source, target); moveFile(source, target, copyCallback); //throw FileError callback.objectProcessed(); }); - fileRelNames.push_back(relativeName); + //fileRelNames.push_back(relativeName); } void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError { - //note: we cannot support "throw exception if target already exists": If we did, we would have to do a full cleanup - //removing all newly created directories in case of an exception so that subsequent tries would not fail with "target already existing". - //However an exception may also happen during final deletion of source folder, in which case cleanup effectively leads to data loss! - //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! { @@ -247,6 +262,7 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ relativeName, versioningDirectory_, timeStamp_, + versioningStyle_, [&](const Zstring& source, const Zstring& target) { callback.onBeforeDirMove(source, target); @@ -254,12 +270,12 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ callback.objectProcessed(); }); - fileRelNames.push_back(relativeName); + //fileRelNames.push_back(relativeName); } else { assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); - assert(endsWith(sourceDir, relativeName)); + assert(endsWith(sourceDir, relativeName)); //usually, yes, but we might relax this in the future const Zstring targetDir = appendSeparator(versioningDirectory_) + relativeName; callback.onBeforeDirMove(sourceDir, targetDir); @@ -272,7 +288,7 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ try { TraverseFilesOneLevel tol(fileList, dirList); //throw FileError - traverseFolder(sourceDir, tol); // + traverseFolder(sourceDir, tol); // } catch (FileError&) { @@ -293,7 +309,7 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ callback); }); - //move directories + //move items in subdirectories std::for_each(dirList.begin(), dirList.end(), [&](const Zstring& shortname) { @@ -303,7 +319,15 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ }); //delete source - RemoveCallbackImpl removeCallback(callback); + struct RemoveCallbackImpl : public CallbackRemoveDir + { + RemoveCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} + private: + virtual void notifyFileDeletion(const Zstring& filename) { callback_.updateStatus(0); } + virtual void notifyDirDeletion (const Zstring& dirname ) { callback_.updateStatus(0); } + CallbackMoveFile& callback_; + } removeCallback(callback); + removeDirectory(sourceDir, &removeCallback); //throw FileError callback.objectProcessed(); @@ -311,6 +335,7 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ } +/* namespace { class TraverseVersionsOneLevel : public TraverseCallback @@ -329,7 +354,6 @@ private: }; } - void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileError { if (versionCountLimit_ < 0) //no limit! @@ -340,9 +364,9 @@ void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileEr auto getVersionsBuffered = [&](const Zstring& dirname) -> const std::vector<Zstring>& { - auto iter = dirBuffer.find(dirname); - if (iter != dirBuffer.end()) - return iter->second; + auto it = dirBuffer.find(dirname); + if (it != dirBuffer.end()) + return it->second; std::vector<Zstring> fileShortNames; TraverseVersionsOneLevel tol(fileShortNames, updateUI); //throw FileError @@ -396,3 +420,4 @@ void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileEr }); }); } +*/
\ No newline at end of file diff --git a/lib/versioning.h b/lib/versioning.h index b025511b..3e0dd33c 100644 --- a/lib/versioning.h +++ b/lib/versioning.h @@ -13,6 +13,7 @@ #include <zen/zstring.h> #include <zen/int64.h> #include <zen/file_error.h> +#include "../structures.h" namespace zen { @@ -25,19 +26,20 @@ struct CallbackMoveFile; - creates missing intermediate directories - does not create empty directories - handles symlinks - - ignores already existing target files/dirs (support retry) - => (unlikely) risk of data loss: race-condition if two FFS instances start at the very same second and process the same filename!! + - replaces already existing target files/dirs (supports retry) + => (unlikely) risk of data loss for naming convention "versioning": + race-condition if two FFS instances start at the very same second OR multiple folder pairs process the same filename!! */ class FileVersioner { public: FileVersioner(const Zstring& versioningDirectory, //throw FileError - const TimeComp& timeStamp, - int versionCountLimit) : //max versions per file; < 0 := no limit + VersioningStyle versioningStyle, + const TimeComp& timeStamp) : //max versions per file; < 0 := no limit + versioningStyle_(versioningStyle), versioningDirectory_(versioningDirectory), - timeStamp_(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)), //e.g. "2012-05-15 131513" - versionCountLimit_(versionCountLimit) + timeStamp_(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" { if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000! throw FileError(_("Failure to create time stamp for versioning:") + L" \'" + timeStamp_ + L"\'"); @@ -46,14 +48,14 @@ public: void revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError void revisionDir (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError - void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning! + //void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning! private: + const VersioningStyle versioningStyle_; const Zstring versioningDirectory_; const Zstring timeStamp_; - const int versionCountLimit_; - std::vector<Zstring> fileRelNames; //store list of revisioned file and symlink relative names for limitVersions() + //std::vector<Zstring> fileRelNames; //store list of revisioned file and symlink relative names for limitVersions() }; diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp index c7650bc0..814b9bdb 100644 --- a/lib/xml_base.cpp +++ b/lib/xml_base.cpp @@ -56,8 +56,8 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff throw FfsXmlError( replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), L"%x", fmtFileName(filename)), - L"%y", numberTo<std::wstring>(e.row)), - L"%z", numberTo<std::wstring>(e.col))); + L"%y", numberTo<std::wstring>(e.row + 1)), + L"%z", numberTo<std::wstring>(e.col + 1))); } } |