diff options
Diffstat (limited to 'library')
-rw-r--r-- | library/binary.cpp | 8 | ||||
-rw-r--r-- | library/binary.h | 4 | ||||
-rw-r--r-- | library/custom_grid.cpp (renamed from library/CustomGrid.cpp) | 34 | ||||
-rw-r--r-- | library/custom_grid.h (renamed from library/CustomGrid.h) | 22 | ||||
-rw-r--r-- | library/db_file.cpp (renamed from library/dbFile.cpp) | 84 | ||||
-rw-r--r-- | library/db_file.h (renamed from library/dbFile.h) | 4 | ||||
-rw-r--r-- | library/detect_renaming.cpp (renamed from library/detectRenaming.cpp) | 2 | ||||
-rw-r--r-- | library/detect_renaming.h (renamed from library/detectRenaming.h) | 6 | ||||
-rw-r--r-- | library/dir_lock.cpp | 448 | ||||
-rw-r--r-- | library/dir_lock.h | 35 | ||||
-rw-r--r-- | library/error_log.cpp (renamed from library/errorLogging.cpp) | 4 | ||||
-rw-r--r-- | library/error_log.h (renamed from library/errorLogging.h) | 2 | ||||
-rw-r--r-- | library/filter.cpp | 38 | ||||
-rw-r--r-- | library/filter.h | 2 | ||||
-rw-r--r-- | library/icon_buffer.cpp (renamed from library/iconBuffer.cpp) | 163 | ||||
-rw-r--r-- | library/icon_buffer.h (renamed from library/iconBuffer.h) | 11 | ||||
-rw-r--r-- | library/pch.h | 9 | ||||
-rw-r--r-- | library/process_xml.cpp (renamed from library/processXml.cpp) | 270 | ||||
-rw-r--r-- | library/process_xml.h (renamed from library/processXml.h) | 48 | ||||
-rw-r--r-- | library/resources.cpp | 10 | ||||
-rw-r--r-- | library/soft_filter.cpp (renamed from library/softFilter.cpp) | 2 | ||||
-rw-r--r-- | library/soft_filter.h (renamed from library/softFilter.h) | 4 | ||||
-rw-r--r-- | library/statistics.cpp | 42 | ||||
-rw-r--r-- | library/statistics.h | 2 | ||||
-rw-r--r-- | library/status_handler.cpp (renamed from library/statusHandler.cpp) | 2 | ||||
-rw-r--r-- | library/status_handler.h (renamed from library/statusHandler.h) | 0 |
26 files changed, 926 insertions, 330 deletions
diff --git a/library/binary.cpp b/library/binary.cpp index 8b8fd2ec..d4d35e92 100644 --- a/library/binary.cpp +++ b/library/binary.cpp @@ -5,7 +5,7 @@ // ************************************************************************** // #include "binary.h" -#include "../shared/fileIO.h" +#include "../shared/file_io.h" #include <vector> #include <wx/stopwatch.h> @@ -13,7 +13,7 @@ inline void setMinSize(std::vector<char>& buffer, size_t minSize) { - if (buffer.size() < minSize) //this is similar to reserve(), but be need a "properly initialized" array here + if (buffer.size() < minSize) //this is similar to reserve(), but we need a "properly initialized" array here buffer.resize(minSize); } @@ -66,7 +66,7 @@ private: } -bool FreeFileSync::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) +bool ffs3::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) { FileInput file1(filename1); //throw FileError() FileInput file2(filename2); //throw FileError() @@ -124,7 +124,7 @@ bool FreeFileSync::filesHaveSameContent(const Zstring& filename1, const Zstring& } while (!file1.eof()); - if (!file1.eof()) //highly unlikely, but theoretically possible! (but then again, not in this context where both files have same size...) + if (!file2.eof()) //highly unlikely, but theoretically possible! (but then again, not in this context where both files have same size...) return false; return true; diff --git a/library/binary.h b/library/binary.h index e33e46bf..bf477116 100644 --- a/library/binary.h +++ b/library/binary.h @@ -9,9 +9,9 @@ #include "../shared/zstring.h" #include <wx/longlong.h> -#include "../shared/fileError.h" +#include "../shared/file_error.h" -namespace FreeFileSync +namespace ffs3 { //callback functionality for status updates while comparing diff --git a/library/CustomGrid.cpp b/library/custom_grid.cpp index a9846f74..2537f529 100644 --- a/library/CustomGrid.cpp +++ b/library/custom_grid.cpp @@ -4,31 +4,31 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "customGrid.h" -#include "../shared/systemConstants.h" +#include "custom_grid.h" +#include "../shared/system_constants.h" #include "resources.h" #include <wx/dc.h> #include "../shared/util.h" -#include "../shared/stringConv.h" +#include "../shared/string_conv.h" #include "resources.h" #include <typeinfo> -#include "../ui/gridView.h" +#include "../ui/grid_view.h" #include "../synchronization.h" -#include "../shared/customTooltip.h" +#include "../shared/custom_tooltip.h" #include <wx/dcclient.h> -#include "iconBuffer.h" +#include "icon_buffer.h" #include <wx/icon.h> #ifdef FFS_WIN #include <wx/timer.h> -#include "statusHandler.h" +#include "status_handler.h" #include <cmath> #elif defined FFS_LINUX #include <gtk/gtk.h> #endif -using namespace FreeFileSync; +using namespace ffs3; const size_t MIN_ROW_COUNT = 15; @@ -271,7 +271,7 @@ protected: switch (colType_) { case xmlAccess::FULL_PATH: - value = zToWx(fileObj.getFullName<side>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR)); + value = zToWx(fileObj.getFullName<side>().BeforeLast(common::FILE_NAME_SEPARATOR)); break; case xmlAccess::FILENAME: //filename value = zToWx(fileObj.getShortName<side>()); @@ -283,10 +283,10 @@ protected: value = zToWx(fileObj.getBaseDirPf<side>()); break; case xmlAccess::SIZE: //file size - value = FreeFileSync::numberToStringSep(fileObj.getFileSize<side>()); + value = ffs3::numberToStringSep(fileObj.getFileSize<side>()); break; case xmlAccess::DATE: //date - value = FreeFileSync::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); + value = ffs3::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); break; case xmlAccess::EXTENSION: //file extension value = zToWx(fileObj.getExtension<side>()); @@ -299,7 +299,7 @@ protected: switch (colType_) { case xmlAccess::FULL_PATH: - value = zToWx(linkObj.getFullName<side>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR)); + value = zToWx(linkObj.getFullName<side>().BeforeLast(common::FILE_NAME_SEPARATOR)); break; case xmlAccess::FILENAME: //filename value = zToWx(linkObj.getShortName<side>()); @@ -314,7 +314,7 @@ protected: value = _("<Symlink>"); break; case xmlAccess::DATE: //date - value = FreeFileSync::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); + value = ffs3::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); break; case xmlAccess::EXTENSION: //file extension value = wxEmptyString; @@ -624,7 +624,7 @@ void CustomGrid::initSettings(CustomGridLeft* gridLeft, } -void CustomGrid::release() //release connection to FreeFileSync::GridView +void CustomGrid::release() //release connection to ffs3::GridView { setGridDataTable(NULL); } @@ -1472,7 +1472,7 @@ void CustomGridRim::getIconsToBeLoaded(std::vector<Zstring>& newLoad) //loads al if (!fileName.empty()) { //test if they are already loaded in buffer: - if (FreeFileSync::IconBuffer::getInstance().requestFileIcon(fileName)) + if (ffs3::IconBuffer::getInstance().requestFileIcon(fileName)) { //exists in buffer: refresh Row for (int k = 0; k < totalCols; ++k) @@ -1520,7 +1520,7 @@ void IconUpdater::loadIconsAsynchronously(wxEvent& event) //loads all (not yet) //merge vectors newLoad.insert(newLoad.end(), iconsLeft.begin(), iconsLeft.end()); - FreeFileSync::IconBuffer::getInstance().setWorkload(newLoad); //attention: newLoad is invalidated after this call!!! + ffs3::IconBuffer::getInstance().setWorkload(newLoad); //attention: newLoad is invalidated after this call!!! //event.Skip(); } @@ -2075,7 +2075,7 @@ void CustomGridMiddle::DrawColLabel(wxDC& dc, int col) } -const wxBitmap& FreeFileSync::getSyncOpImage(SyncOperation syncOp) +const wxBitmap& ffs3::getSyncOpImage(SyncOperation syncOp) { switch (syncOp) //evaluate comparison result and sync direction { diff --git a/library/CustomGrid.h b/library/custom_grid.h index 0cace1a9..6c35ffbe 100644 --- a/library/CustomGrid.h +++ b/library/custom_grid.h @@ -9,7 +9,7 @@ #include <vector> #include <wx/grid.h> -#include "processXml.h" +#include "process_xml.h" #include <map> #include <memory> #include <set> @@ -27,7 +27,7 @@ class CustomGridMiddle; class CustomGridRight; -namespace FreeFileSync +namespace ffs3 { class GridView; @@ -60,12 +60,12 @@ public: virtual ~CustomGrid() {} - void initSettings(CustomGridLeft* gridLeft, //create connection with FreeFileSync::GridView + void initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const FreeFileSync::GridView* gridDataView); + const ffs3::GridView* gridDataView); - void release(); //release connection to FreeFileSync::GridView + void release(); //release connection to ffs3::GridView std::set<size_t> getAllSelectedRows() const; @@ -89,7 +89,7 @@ protected: virtual void DrawColLabel(wxDC& dc, int col); private: - virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView) = 0; + virtual void setGridDataTable(const ffs3::GridView* gridDataView) = 0; //this method is called when grid view changes: useful for parallel updating of multiple grids void OnPaintGrid(wxEvent& event); @@ -198,7 +198,7 @@ public: virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); private: - virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView); + virtual void setGridDataTable(const ffs3::GridView* gridDataView); virtual const CustomGridTableRim* getGridDataTable() const; //this method is called when grid view changes: useful for parallel updating of multiple grids @@ -221,7 +221,7 @@ public: virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); private: - virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView); + virtual void setGridDataTable(const ffs3::GridView* gridDataView); virtual const CustomGridTableRim* getGridDataTable() const; //this method is called when grid view changes: useful for parallel updating of multiple grids @@ -257,7 +257,7 @@ private: virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true); #endif - virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView); + virtual void setGridDataTable(const ffs3::GridView* gridDataView); //this method is called when grid view changes: useful for parallel updating of multiple grids virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight); @@ -331,7 +331,7 @@ extern const wxEventType FFS_SYNC_DIRECTION_EVENT; //define new event type class FFSSyncDirectionEvent : public wxCommandEvent { public: - FFSSyncDirectionEvent(const int from, const int to, const FreeFileSync::SyncDirection dir) : + FFSSyncDirectionEvent(const int from, const int to, const ffs3::SyncDirection dir) : wxCommandEvent(FFS_SYNC_DIRECTION_EVENT), rowFrom(from), rowTo(to), @@ -344,7 +344,7 @@ public: const int rowFrom; const int rowTo; - const FreeFileSync::SyncDirection direction; + const ffs3::SyncDirection direction; }; typedef void (wxEvtHandler::*FFSSyncDirectionEventFunction)(FFSSyncDirectionEvent&); diff --git a/library/dbFile.cpp b/library/db_file.cpp index 1919dd6d..1daa51f5 100644 --- a/library/dbFile.cpp +++ b/library/db_file.cpp @@ -4,25 +4,25 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "dbFile.h" +#include "db_file.h" #include <wx/wfstream.h> #include <wx/zstream.h> -#include "../shared/globalFunctions.h" -#include "../shared/fileError.h" +#include "../shared/global_func.h" +#include "../shared/file_error.h" #include <wx/intl.h> -#include "../shared/stringConv.h" -#include "../shared/fileHandling.h" +#include "../shared/string_conv.h" +#include "../shared/file_handling.h" #include <wx/mstream.h> #include "../shared/serialize.h" -#include "../shared/fileIO.h" +#include "../shared/file_io.h" #include "../shared/loki/ScopeGuard.h" #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" -#include "../shared/longPathPrefix.h" +#include "../shared/long_path_prefix.h" #endif -using namespace FreeFileSync; +using namespace ffs3; namespace @@ -72,7 +72,7 @@ private: -class ReadDirInfo : public Utility::ReadInputStream +class ReadDirInfo : public util::ReadInputStream { public: ReadDirInfo(wxInputStream& stream, const wxString& errorObjName, DirInformation& dirInfo) : ReadInputStream(stream, errorObjName) @@ -112,7 +112,7 @@ private: const unsigned long sizeHigh = readNumberC<unsigned long>(); const unsigned long sizeLow = readNumberC<unsigned long>(); - //const Utility::FileID fileIdentifier(stream_); + //const util::FileID fileIdentifier(stream_); //check(); dirCont.addSubFile(shortName, @@ -144,9 +144,9 @@ private: }; -typedef boost::shared_ptr<std::vector<char> > MemoryStreamPtr; //byte stream representing DirInformation -typedef std::map<Utility::UniqueId, MemoryStreamPtr> DirectoryTOC; //list of streams ordered by a UUID pointing to their partner database -typedef std::pair<Utility::UniqueId, DirectoryTOC> DbStreamData; //header data: UUID representing this database, item data: list of dir-streams +typedef boost::shared_ptr<std::vector<char> > MemoryStreamPtr; //byte stream representing DirInformation +typedef std::map<util::UniqueId, MemoryStreamPtr> DirectoryTOC; //list of streams ordered by a UUID pointing to their partner database +typedef std::pair<util::UniqueId, DirectoryTOC> DbStreamData; //header data: UUID representing this database, item data: list of dir-streams /* Example left side right side --------- ---------- @@ -157,7 +157,7 @@ Partner-ID 567 _/ \_ Partner-ID 123 ... ... */ -class ReadFileStream : public Utility::ReadInputStream +class ReadFileStream : public util::ReadInputStream { public: ReadFileStream(wxInputStream& stream, const wxString& filename, DbStreamData& output) : ReadInputStream(stream, filename) @@ -166,7 +166,7 @@ public: throw FileError(wxString(_("Incompatible synchronization database format:")) + wxT(" \n") + wxT("\"") + filename + wxT("\"")); //read DB id - output.first = Utility::UniqueId(getStream()); + output.first = util::UniqueId(getStream()); check(); DirectoryTOC& dbList = output.second; @@ -175,7 +175,7 @@ public: size_t dbCount = readNumberC<size_t>(); //number of databases: one for each sync-pair while (dbCount-- != 0) { - const Utility::UniqueId partnerID(getStream()); //DB id of partner databases + const util::UniqueId partnerID(getStream()); //DB id of partner databases check(); CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) @@ -188,7 +188,7 @@ public: DbStreamData loadFile(const Zstring& filename) //throw (FileError) { - if (!FreeFileSync::fileExists(filename)) + if (!ffs3::fileExists(filename)) throw FileError(wxString(_("Initial synchronization:")) + wxT(" \n\n") + _("One of the FreeFileSync database files is not yet existing:") + wxT(" \n") + wxT("\"") + zToWx(filename) + wxT("\"")); @@ -204,7 +204,7 @@ DbStreamData loadFile(const Zstring& filename) //throw (FileError) } -std::pair<DirInfoPtr, DirInfoPtr> FreeFileSync::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError) +std::pair<DirInfoPtr, DirInfoPtr> ffs3::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError) { const Zstring fileNameLeft = baseMapping.getDBFilename<LEFT_SIDE>(); const Zstring fileNameRight = baseMapping.getDBFilename<RIGHT_SIDE>(); @@ -259,7 +259,7 @@ struct IsNonEmpty template <SelectedSide side> -class SaveDirInfo : public Utility::WriteOutputStream +class SaveDirInfo : public util::WriteOutputStream { public: SaveDirInfo(const BaseDirMapping& baseMapping, const wxString& errorObjName, wxOutputStream& stream) : WriteOutputStream(errorObjName, stream) @@ -273,11 +273,11 @@ public: } private: - friend class Utility::Proxy<SaveDirInfo<side> >; //friend declaration of std::for_each is NOT sufficient as implementation is compiler dependent! + friend class util::ProxyForEach<SaveDirInfo<side> >; //friend declaration of std::for_each is NOT sufficient as implementation is compiler dependent! void execute(const HierarchyObject& hierObj) { - Utility::Proxy<SaveDirInfo<side> > prx(*this); //grant std::for_each access to private parts of this class + util::ProxyForEach<SaveDirInfo<side> > prx(*this); //grant std::for_each access to private parts of this class writeNumberC<size_t>(std::count_if(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), IsNonEmpty<side>())); //number of (existing) files std::for_each(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), prx); @@ -327,7 +327,7 @@ private: }; -class WriteFileStream : public Utility::WriteOutputStream +class WriteFileStream : public util::WriteOutputStream { public: WriteFileStream(const DbStreamData& input, const wxString& filename, wxOutputStream& stream) : WriteOutputStream(filename, stream) @@ -373,26 +373,42 @@ void saveFile(const DbStreamData& dbStream, const Zstring& filename) //throw (Fi } //(try to) hide database file #ifdef FFS_WIN - ::SetFileAttributes(FreeFileSync::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); + ::SetFileAttributes(ffs3::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); #endif } -void FreeFileSync::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) +bool entryExisting(const DirectoryTOC& table, const util::UniqueId& newKey, const MemoryStreamPtr& newValue) +{ + DirectoryTOC::const_iterator iter = table.find(newKey); + if (iter == table.end()) + return false; + + if (!iter->second.get()) + return !newValue.get(); + + if (!newValue.get()) + return false; + + return newValue->size() == iter->second->size() && std::equal(newValue->begin(), newValue->end(), iter->second->begin()); +} + + +void ffs3::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) { //transactional behaviour! write to tmp files first const Zstring fileNameLeftTmp = baseMapping.getDBFilename<LEFT_SIDE>() + DefaultStr(".tmp"); const Zstring fileNameRightTmp = baseMapping.getDBFilename<RIGHT_SIDE>() + DefaultStr(".tmp");; //delete old tmp file, if necessary -> throws if deletion fails! - removeFile(fileNameLeftTmp); - removeFile(fileNameRightTmp); + removeFile(fileNameLeftTmp); // + removeFile(fileNameRightTmp); //throw (FileError) //load old database files... //read file data: db ID + mapping of partner-ID/DirInfo-stream: may throw! DbStreamData dbEntriesLeft; - if (FreeFileSync::fileExists(baseMapping.getDBFilename<LEFT_SIDE>())) + if (ffs3::fileExists(baseMapping.getDBFilename<LEFT_SIDE>())) try { dbEntriesLeft = ::loadFile(baseMapping.getDBFilename<LEFT_SIDE>()); @@ -402,7 +418,7 @@ void FreeFileSync::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileEr //read file data: db ID + mapping of partner-ID/DirInfo-stream: may throw! DbStreamData dbEntriesRight; - if (FreeFileSync::fileExists(baseMapping.getDBFilename<RIGHT_SIDE>())) + if (ffs3::fileExists(baseMapping.getDBFilename<RIGHT_SIDE>())) try { dbEntriesRight = ::loadFile(baseMapping.getDBFilename<RIGHT_SIDE>()); @@ -427,6 +443,14 @@ void FreeFileSync::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileEr } //create/update DirInfo-streams + { + const bool updateRequiredLeft = !entryExisting(dbEntriesLeft. second, dbEntriesRight.first, dbEntryLeft); + const bool updateRequiredRight = !entryExisting(dbEntriesRight.second, dbEntriesLeft. first, dbEntryRight); + //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed + if (!updateRequiredLeft && !updateRequiredRight) + return; + } + dbEntriesLeft.second[dbEntriesRight.first] = dbEntryLeft; dbEntriesRight.second[dbEntriesLeft.first] = dbEntryRight; @@ -454,9 +478,9 @@ void FreeFileSync::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileEr //if there were no write access, creation of temp files would have failed removeFile(baseMapping.getDBFilename<LEFT_SIDE>()); removeFile(baseMapping.getDBFilename<RIGHT_SIDE>()); - renameFile(fileNameLeftTmp, baseMapping.getDBFilename<LEFT_SIDE>()); //throw (FileError); + renameFile(fileNameLeftTmp, baseMapping.getDBFilename<LEFT_SIDE>()); //throw (FileError); renameFile(fileNameRightTmp, baseMapping.getDBFilename<RIGHT_SIDE>()); //throw (FileError); - guardTempFileLeft.Dismiss(); //no need to delete temp file anymore + guardTempFileLeft. Dismiss(); //no need to delete temp file anymore guardTempFileRight.Dismiss(); // } diff --git a/library/dbFile.h b/library/db_file.h index 9fa49c51..8e1f9c94 100644 --- a/library/dbFile.h +++ b/library/db_file.h @@ -7,9 +7,9 @@ #ifndef DBFILE_H_INCLUDED #define DBFILE_H_INCLUDED -#include "../fileHierarchy.h" +#include "../file_hierarchy.h" -namespace FreeFileSync +namespace ffs3 { void saveToDisk(const BaseDirMapping& baseMapping); //throw (FileError) diff --git a/library/detectRenaming.cpp b/library/detect_renaming.cpp index bd25874b..617a3eba 100644 --- a/library/detectRenaming.cpp +++ b/library/detect_renaming.cpp @@ -4,7 +4,7 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "detectRenaming.h" +#include "detect_renaming.h" #include <map> #include <vector> #include <boost/bind.hpp> diff --git a/library/detectRenaming.h b/library/detect_renaming.h index ecfa0344..45bdf59d 100644 --- a/library/detectRenaming.h +++ b/library/detect_renaming.h @@ -7,18 +7,18 @@ #ifndef DETECTRENAMING_H_INCLUDED #define DETECTRENAMING_H_INCLUDED -#include "../fileHierarchy.h" +#include "../file_hierarchy.h" //identify a file "create and delete"-operation as a file renaming! -namespace FreeFileSync +namespace ffs3 { typedef FileMapping* CreateOnLeft; typedef FileMapping* DeleteOnLeft; typedef FileMapping* CreateOnRight; typedef FileMapping* DeleteOnRight; -void getRenameCandidates(FreeFileSync::BaseDirMapping& baseMapping, //in +void getRenameCandidates(ffs3::BaseDirMapping& baseMapping, //in std::vector<std::pair<CreateOnLeft, DeleteOnLeft> >& renameOnLeft, //out std::vector<std::pair<CreateOnRight, DeleteOnRight> >& renameOnRight); //out throw()! } diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp new file mode 100644 index 00000000..c5ea37b1 --- /dev/null +++ b/library/dir_lock.cpp @@ -0,0 +1,448 @@ +#include "dir_lock.h" +#include <wx/intl.h> +#include "../shared/string_conv.h" +#include "../shared/system_func.h" +#include <wx/utils.h> +#include <wx/timer.h> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/thread.hpp> +#include "../shared/loki/ScopeGuard.h" +#include <wx/msgdlg.h> +#include "../shared/system_constants.h" +#include "../shared/guid.h" +#include "../shared/file_io.h" +#include <utility> + +#ifdef FFS_WIN +#include <wx/msw/wrapwin.h> //includes "windows.h" +#include "../shared/long_path_prefix.h" + +#elif defined FFS_LINUX +#include <sys/stat.h> +#include <cerrno> +#endif + +using namespace ffs3; +using namespace std::rel_ops; + + +namespace +{ +const size_t EMIT_LIFE_SIGN_INTERVAL = 5000; //show life sign; unit [ms] +const size_t POLL_LIFE_SIGN_INTERVAL = 6000; //poll for life sign; unit [ms] +const size_t DETECT_EXITUS_INTERVAL = 30000; //assume abandoned lock; unit [ms] +} + +class LifeSigns +{ +public: + LifeSigns(const Zstring& lockfilename) : //throw()!!! siehe SharedDirLock() + lockfilename_(lockfilename.c_str()) //ref-counting structure is used by thread: make deep copy! + { + threadObj = boost::thread(boost::cref(*this)); //localize all thread logic to this class! + } + + ~LifeSigns() + { + threadObj.interrupt(); //thread lifetime is subset of this instances's life + threadObj.join(); + } + + void operator()() const //thread entry + { + try + { + while (true) + { + boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(EMIT_LIFE_SIGN_INTERVAL)); //interruption point! + + //actual work + emitLifeSign(); //throw () + } + } + catch (const std::exception& e) //exceptions must be catched per thread + { + wxMessageBox(wxString::FromAscii(e.what()), wxString(_("An exception occurred!")) + wxT("(Dirlock)"), wxOK | wxICON_ERROR); + } + } + + void emitLifeSign() const //try to append one byte...; throw() + { + const char buffer[1] = {' '}; + +#ifdef FFS_WIN + const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_).c_str(), + FILE_APPEND_DATA, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (fileHandle == INVALID_HANDLE_VALUE) + return; + + Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, fileHandle); + (void)dummy; //silence warning "unused variable" + + const DWORD fpLow = ::SetFilePointer(fileHandle, 0, NULL, FILE_END); + if (fpLow == INVALID_SET_FILE_POINTER) + return; + + DWORD bytesWritten = 0; + ::WriteFile( + fileHandle, //__in HANDLE hFile, + buffer, //__out LPVOID lpBuffer, + 1, //__in DWORD nNumberOfBytesToRead, + &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, + NULL); //__inout_opt LPOVERLAPPED lpOverlapped + +#elif defined FFS_LINUX + const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open + if (fileHandle == -1) + return; + + Loki::ScopeGuard dummy = Loki::MakeGuard(::close, fileHandle); + (void)dummy; //silence warning "unused variable" + + const ssize_t bytesWritten = ::write(fileHandle, buffer, 1); + (void)bytesWritten; +#endif + } + +private: + LifeSigns(const LifeSigns&); //just be sure this ref-counting Zstring doesn't bite + LifeSigns& operator=(const LifeSigns&); // + + boost::thread threadObj; + const Zstring lockfilename_; //used by worker thread only! Not ref-counted! +}; + + +namespace +{ +bool somethingExists(const Zstring& objname) //throw() check whether any object with this name exists +{ +#ifdef FFS_WIN + return ::GetFileAttributes(applyLongPathPrefix(objname).c_str()) != INVALID_FILE_ATTRIBUTES; + +#elif defined FFS_LINUX + struct stat fileInfo; + return ::lstat(objname.c_str(), &fileInfo) == 0; +#endif +} + + +void deleteLockFile(const Zstring& filename) //throw (FileError) +{ +#ifdef FFS_WIN + if (!::DeleteFile(applyLongPathPrefix(filename).c_str())) +#elif defined FFS_LINUX + if (::unlink(filename.c_str()) != 0) +#endif + { + wxString errorMessage = wxString(_("Error deleting file:")) + wxT("\n\"") + zToWx(filename) + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + getLastErrorFormatted()); + } +} + + +wxULongLong getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNotExisting) +{ +#ifdef FFS_WIN + WIN32_FIND_DATA fileMetaData; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileMetaData); + if (searchHandle == INVALID_HANDLE_VALUE) + { + const DWORD lastError = ::GetLastError(); + const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\"") + + wxT("\n\n") + getLastErrorFormatted(lastError); + if (lastError == ERROR_FILE_NOT_FOUND) + throw ErrorNotExisting(errorMessage); + else + throw FileError(errorMessage); + } + + ::FindClose(searchHandle); + + return wxULongLong(fileMetaData.nFileSizeHigh, fileMetaData.nFileSizeLow); + +#elif defined FFS_LINUX + struct stat fileInfo; + if (::stat(filename.c_str(), &fileInfo) != 0) //follow symbolic links + { + const int lastError = errno; + const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\"") + + wxT("\n\n") + getLastErrorFormatted(lastError); + if (lastError == ENOENT) + throw ErrorNotExisting(errorMessage); + else + throw FileError(errorMessage); + } + + return fileInfo.st_size; +#endif +} + + +Zstring deleteAbandonedLockName(const Zstring& lockfilename) +{ + const size_t pos = lockfilename.Find(common::FILE_NAME_SEPARATOR, true); //search from end + + return pos == Zstring::npos ? DefaultStr("Del.") + lockfilename : + + Zstring(lockfilename.c_str(), pos + 1) + //include path separator + DefaultStr("Del.") + + lockfilename.AfterLast(common::FILE_NAME_SEPARATOR); //returns the whole string if ch not found +} + + +void writeLockId(const Zstring& lockfilename) //throw (FileError) +{ + //write GUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks ,etc.) + FileOutputStream lockFile(lockfilename); //throw FileError() + util::UniqueId().toStream(lockFile); // +} + + +util::UniqueId retrieveLockId(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting) +{ + //read GUID from beginning of file + FileInputStream lockFile(lockfilename); //throw (FileError, ErrorNotExisting) + return util::UniqueId(lockFile); // +} + + +void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError) +{ + Zstring infoMsg; + infoMsg = wxToZ(_("Waiting while directory is locked (%x)...")); + infoMsg.Replace(DefaultStr("%x"), DefaultStr("\"") + lockfilename + DefaultStr("\"")); + if (callback) callback->updateStatusText(infoMsg); + //--------------------------------------------------------------- + try + { + const util::UniqueId lockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting) + + wxULongLong fileSizeOld; + wxLongLong lockSilentStart = wxGetLocalTimeMillis(); + + while (true) + { + const wxULongLong fileSizeNew = ::getLockFileSize(lockfilename); //throw (FileError, ErrorNotExisting) + const wxLongLong currentTime = wxGetLocalTimeMillis(); + + if (fileSizeNew != fileSizeOld) + { + //received life sign from lock + fileSizeOld = fileSizeNew; + lockSilentStart = currentTime; + } + else if (currentTime - lockSilentStart > DETECT_EXITUS_INTERVAL) + { + DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw (FileError) + + //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! + + if (retrieveLockId(lockfilename) != lockId) //throw (FileError, ErrorNotExisting) + return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... + + if (getLockFileSize(lockfilename) != fileSizeOld) //throw (FileError, ErrorNotExisting) + continue; //belated lifesign + + //--------------------------------------------------------------- + Zstring infoMsg2 = wxToZ(_("Removing abandoned directory lock (%x)...")); + infoMsg2.Replace(DefaultStr("%x"), DefaultStr("\"") + lockfilename + DefaultStr("\"")); + if (callback) callback->updateStatusText(infoMsg2); + //--------------------------------------------------------------- + + ::deleteLockFile(lockfilename); //throw (FileError) + return; + } + + //wait some time... + const size_t GUI_CALLBACK_INTERVAL = 100; + for (size_t i = 0; i < POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i) + { + if (callback) callback->requestUiRefresh(); + wxMilliSleep(GUI_CALLBACK_INTERVAL); + + //show some countdown on abandoned locks + if (callback) + { + if (currentTime - lockSilentStart > EMIT_LIFE_SIGN_INTERVAL) //one signal missed: it's likely this is an abandoned lock: + { + long remainingSeconds = ((DETECT_EXITUS_INTERVAL - (wxGetLocalTimeMillis() - lockSilentStart)) / 1000).GetLo(); + remainingSeconds = std::max(0L, remainingSeconds); + + Zstring remSecMsg = wxToZ(_("%x sec")); + remSecMsg.Replace(DefaultStr("%x"), numberToZstring(remainingSeconds)); + callback->updateStatusText(infoMsg + DefaultStr(" ") + remSecMsg); + } + else + callback->updateStatusText(infoMsg); //emit a message in any case (might clear other one) + } + } + } + + } + catch (const ErrorNotExisting&) + { + return; //what we are waiting for... + } +} + + +void releaseLock(const Zstring& lockfilename) //throw () +{ + try + { + ::deleteLockFile(lockfilename); + } + catch(...) {} +} + + +bool tryLock(const Zstring& lockfilename) //throw (FileError) +{ +#ifdef FFS_WIN + const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(), + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (fileHandle == INVALID_HANDLE_VALUE) + { + if (::GetLastError() == ERROR_FILE_EXISTS) + return false; + else + throw FileError(wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\"") + + wxT("\n\n") + getLastErrorFormatted()); + } + ::CloseHandle(fileHandle); + +#elif defined FFS_LINUX + //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open + ::umask(0); //important! + const int fileHandle = ::open(lockfilename.c_str(), O_CREAT | O_WRONLY | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO); + if (fileHandle == -1) + { + if (errno == EEXIST) + return false; + else + throw FileError(wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\"") + + wxT("\n\n") + getLastErrorFormatted()); + } + ::close(fileHandle); +#endif + + Loki::ScopeGuard guardLockFile = Loki::MakeGuard(::releaseLock, lockfilename); + + //write UUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks ,etc.) + writeLockId(lockfilename); //throw (FileError) + + guardLockFile.Dismiss(); //lockfile created successfully + return true; +} +} + + +class DirLock::SharedDirLock +{ +public: + SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL) : //throw (FileError) + lockfilename_(lockfilename) + { + while (!::tryLock(lockfilename)) //throw (FileError) + ::waitOnDirLock(lockfilename, callback); // + + emitLifeSigns.reset(new LifeSigns(lockfilename)); //throw()! ownership of lockfile not yet managed! + } + + ~SharedDirLock() + { + emitLifeSigns.reset(); + + ::releaseLock(lockfilename_); //throw () + } + +private: + SharedDirLock(const DirLock&); + SharedDirLock& operator=(const DirLock&); + + const Zstring lockfilename_; + + std::auto_ptr<LifeSigns> emitLifeSigns; +}; + + +class DirLock::LockAdmin //administrate all locks of this process to avoid deadlock by recursion +{ +public: + static LockAdmin& instance() + { + static LockAdmin inst; + return inst; + } + + //create or retrieve a SharedDirLock + boost::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError) + { + //optimization: check if there is an active(!) lock for "lockfilename" + FileToUuidMap::const_iterator iterUuid = fileToUuid.find(lockfilename); + if (iterUuid != fileToUuid.end()) + { + const boost::shared_ptr<SharedDirLock>& activeLock = findActive(iterUuid->second); //returns null-lock if not found + if (activeLock) + return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership + } + + try //actual check based on lock UUID, deadlock prevention: "lockfilename" may be an alternative name for an already active lock + { + const util::UniqueId lockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting) + + const boost::shared_ptr<SharedDirLock>& activeLock = findActive(lockId); //returns null-lock if not found + if (activeLock) + { + fileToUuid[lockfilename] = lockId; //perf-optimization: update relation + return activeLock; + } + } + catch (const ErrorNotExisting&) {} //let other FileError(s) propagate! + + //not yet in buffer, so create a new directory lock + boost::shared_ptr<SharedDirLock> newLock(new SharedDirLock(lockfilename, callback)); //throw (FileError) + const util::UniqueId newLockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting) + + //update registry + fileToUuid[lockfilename] = newLockId; //throw() + uuidToLock[newLockId] = newLock; // + + return newLock; + } + +private: + LockAdmin() {} + + boost::shared_ptr<SharedDirLock> findActive(const util::UniqueId& lockId) //returns null-lock if not found + { + UuidToLockMap::const_iterator iterLock = uuidToLock.find(lockId); + return iterLock != uuidToLock.end() ? + iterLock->second.lock() : //try to get shared_ptr; throw() + boost::shared_ptr<SharedDirLock>(); + } + + typedef boost::weak_ptr<SharedDirLock> SharedLock; + + typedef std::map<Zstring, util::UniqueId, LessFilename> FileToUuidMap; //n:1 handle uppper/lower case correctly + typedef std::map<util::UniqueId, SharedLock> UuidToLockMap; //1:1 + + FileToUuidMap fileToUuid; //lockname |-> UUID; locks can be referenced by a lockfilename or alternatively a UUID + UuidToLockMap uuidToLock; //UUID |-> "shared lock ownership" +}; + + +DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) : //throw (FileError) + sharedLock(LockAdmin::instance().retrieve(lockfilename, callback)) {} diff --git a/library/dir_lock.h b/library/dir_lock.h new file mode 100644 index 00000000..e3b6597c --- /dev/null +++ b/library/dir_lock.h @@ -0,0 +1,35 @@ +#ifndef DIR_LOCK_H_INCLUDED +#define DIR_LOCK_H_INCLUDED + +#include "../shared/zstring.h" +#include "../shared/file_error.h" +#include <boost/shared_ptr.hpp> + + +struct DirLockCallback //while waiting for the lock +{ + virtual ~DirLockCallback() {} + virtual void requestUiRefresh() = 0; //allowed to throw exceptions + virtual void updateStatusText(const Zstring& text) = 0; +}; + +/* +RAII structure to place a directory lock against other FFS processes: + - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc. + - ownership shared between all object instances refering to a specific lock location(= UUID) + - can be copied safely and efficiently! (ref-counting) + - detects and resolves abandoned locks + - race-free (Windows, almost on Linux) +*/ +class DirLock +{ +public: + DirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL); //throw (FileError) + +private: + class LockAdmin; + class SharedDirLock; + boost::shared_ptr<SharedDirLock> sharedLock; +}; + +#endif // DIR_LOCK_H_INCLUDED diff --git a/library/errorLogging.cpp b/library/error_log.cpp index 7e73f005..9936a2ea 100644 --- a/library/errorLogging.cpp +++ b/library/error_log.cpp @@ -4,12 +4,12 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "errorLogging.h" +#include "error_log.h" #include <wx/datetime.h> #include <wx/intl.h> -using FreeFileSync::ErrorLogging; +using ffs3::ErrorLogging; void ErrorLogging::logInfo(const wxString& infoMessage) diff --git a/library/errorLogging.h b/library/error_log.h index 6b27e1c1..dc3ef580 100644 --- a/library/errorLogging.h +++ b/library/error_log.h @@ -12,7 +12,7 @@ class Zstring; -namespace FreeFileSync +namespace ffs3 { class ErrorLogging { diff --git a/library/filter.cpp b/library/filter.cpp index a64c3a31..c4bc69d7 100644 --- a/library/filter.cpp +++ b/library/filter.cpp @@ -10,14 +10,14 @@ #include <set> #include <stdexcept> #include <vector> -#include "../shared/systemConstants.h" +#include "../shared/system_constants.h" #include "../structures.h" #include <boost/bind.hpp> #include "../shared/loki/LokiTypeInfo.h" #include "../shared/serialize.h" -using FreeFileSync::BaseFilter; -using FreeFileSync::NameFilter; +using ffs3::BaseFilter; +using ffs3::NameFilter; //-------------------------------------------------------------------------------------------------- @@ -46,7 +46,7 @@ bool BaseFilter::operator<(const BaseFilter& other) const void BaseFilter::saveFilter(wxOutputStream& stream) const //serialize derived object { //save type information - Utility::writeString(stream, uniqueClassIdentifier()); + util::writeString(stream, uniqueClassIdentifier()); //save actual object save(stream); @@ -56,7 +56,7 @@ void BaseFilter::saveFilter(wxOutputStream& stream) const //serialize derived ob BaseFilter::FilterRef BaseFilter::loadFilter(wxInputStream& stream) { //read type information - const Zstring uniqueClassId = Utility::readString(stream); + const Zstring uniqueClassId = util::readString(stream); //read actual object if (uniqueClassId == DefaultStr("NullFilter")) @@ -83,17 +83,17 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st //Linux DOES distinguish between upper/lower-case: nothing to do here #endif - static const Zstring sepAsterisk = Zstring() + globalFunctions::FILE_NAME_SEPARATOR + DefaultChar('*'); - static const Zstring sepQuestionMark = Zstring() + globalFunctions::FILE_NAME_SEPARATOR + DefaultChar('?'); - static const Zstring asteriskSep = Zstring(DefaultStr("*")) + globalFunctions::FILE_NAME_SEPARATOR; - static const Zstring questionMarkSep = Zstring(DefaultStr("?")) + globalFunctions::FILE_NAME_SEPARATOR; + static const Zstring sepAsterisk = Zstring() + common::FILE_NAME_SEPARATOR + DefaultChar('*'); + static const Zstring sepQuestionMark = Zstring() + common::FILE_NAME_SEPARATOR + DefaultChar('?'); + static const Zstring asteriskSep = Zstring(DefaultStr("*")) + common::FILE_NAME_SEPARATOR; + static const Zstring questionMarkSep = Zstring(DefaultStr("?")) + common::FILE_NAME_SEPARATOR; //-------------------------------------------------------------------------------------------------- //add some syntactic sugar: handle beginning of filtername - if (filterFormatted.StartsWith(globalFunctions::FILE_NAME_SEPARATOR)) + if (filterFormatted.StartsWith(common::FILE_NAME_SEPARATOR)) { //remove leading separators (keep BEFORE test for Zstring::empty()!) - filterFormatted = filterFormatted.AfterFirst(globalFunctions::FILE_NAME_SEPARATOR); + filterFormatted = filterFormatted.AfterFirst(common::FILE_NAME_SEPARATOR); } else if (filterFormatted.StartsWith(asteriskSep) || // *\abc filterFormatted.StartsWith(questionMarkSep)) // ?\abc @@ -103,9 +103,9 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st //-------------------------------------------------------------------------------------------------- //even more syntactic sugar: handle end of filtername - if (filterFormatted.EndsWith(globalFunctions::FILE_NAME_SEPARATOR)) + if (filterFormatted.EndsWith(common::FILE_NAME_SEPARATOR)) { - const Zstring candidate = filterFormatted.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR); + const Zstring candidate = filterFormatted.BeforeLast(common::FILE_NAME_SEPARATOR); if (!candidate.empty()) directoryFilter.insert(candidate); //only relevant for directory filtering } @@ -115,7 +115,7 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st fileFilter.insert( filterFormatted); directoryFilter.insert(filterFormatted); - const Zstring candidate = filterFormatted.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR); + const Zstring candidate = filterFormatted.BeforeLast(common::FILE_NAME_SEPARATOR); if (!candidate.empty()) directoryFilter.insert(candidate); //only relevant for directory filtering } @@ -251,7 +251,7 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch { if (subObjMightMatch) { - const Zstring& subNameBegin = relDirname + globalFunctions::FILE_NAME_SEPARATOR; //const-ref optimization + const Zstring& subNameBegin = relDirname + common::FILE_NAME_SEPARATOR; //const-ref optimization *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory @@ -300,15 +300,15 @@ Zstring NameFilter::uniqueClassIdentifier() const void NameFilter::save(wxOutputStream& stream) const { - Utility::writeString(stream, includeFilterTmp); - Utility::writeString(stream, excludeFilterTmp); + util::writeString(stream, includeFilterTmp); + util::writeString(stream, excludeFilterTmp); } BaseFilter::FilterRef NameFilter::load(wxInputStream& stream) //"constructor" { - const Zstring include = Utility::readString(stream); - const Zstring exclude = Utility::readString(stream); + const Zstring include = util::readString(stream); + const Zstring exclude = util::readString(stream); return FilterRef(new NameFilter(include, exclude)); } diff --git a/library/filter.h b/library/filter.h index f27e06e6..9497eb8d 100644 --- a/library/filter.h +++ b/library/filter.h @@ -12,7 +12,7 @@ #include <boost/shared_ptr.hpp> #include <wx/stream.h> -namespace FreeFileSync +namespace ffs3 { //------------------------------------------------------------------ /* class hierarchy: diff --git a/library/iconBuffer.cpp b/library/icon_buffer.cpp index fa0b1673..81d14146 100644 --- a/library/iconBuffer.cpp +++ b/library/icon_buffer.cpp @@ -4,15 +4,12 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "iconBuffer.h" -#include <wx/thread.h> -#include <wx/bitmap.h> +#include "icon_buffer.h" #include <wx/msgdlg.h> -#include <wx/icon.h> #include <map> #include <queue> -#include <stdexcept> #include <set> +#include <boost/thread.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -24,20 +21,11 @@ #endif -using FreeFileSync::IconBuffer; +using ffs3::IconBuffer; namespace { -struct CmpFilename -{ - bool operator()(const Zstring& a, const Zstring& b) const - { - return a.cmpFileName(b) < 0; - } -}; - - #ifdef FFS_WIN Zstring getFileExtension(const Zstring& filename) { @@ -52,7 +40,7 @@ Zstring getFileExtension(const Zstring& filename) //test for extension for icons that physically have to be retrieved from disc bool isPriceyExtension(const Zstring& extension) { - static std::set<Zstring, CmpFilename> exceptions; + static std::set<Zstring, LessFilename> exceptions; static bool isInitalized = false; if (!isInitalized) { @@ -182,7 +170,7 @@ IconBuffer::IconHolder IconBuffer::getAssociatedIcon(const Zstring& filename) //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! - ::SHGetFileInfo(filename.c_str(), //FreeFileSync::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! + ::SHGetFileInfo(filename.c_str(), //ffs3::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! 0, &fileInfo, sizeof(fileInfo), @@ -224,17 +212,19 @@ IconBuffer::IconHolder IconBuffer::getAssociatedIcon(const Zstring& filename) } catch (const Glib::Error&) {} - - //fallback: icon lookup may fail because some icons are currently not present on system - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) + try //fallback: icon lookup may fail because some icons are currently not present on system { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + if (iconTheme) + { + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (!iconPixbuf) + iconPixbuf = iconTheme->load_icon("text-x-generic", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (iconPixbuf) + return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + } } + catch (const Glib::Error&) {} //fallback fallback return IconHolder(); @@ -266,15 +256,15 @@ typedef std::vector<DefaultChar> BasicString; //simple thread safe string class: //--------------------------------------------------------------------------------------------------- -class IconBuffer::WorkerThread : public wxThread +class IconBuffer::WorkerThread { public: - WorkerThread(IconBuffer* iconBuff); + WorkerThread(IconBuffer& iconBuff); + ~WorkerThread(); void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved - void quitThread(); - virtual ExitCode Entry(); + void operator()(); //thread entry private: void doWork(); @@ -282,88 +272,69 @@ private: //---------------------- Shared Data ------------------------- typedef BasicString FileName; - wxCriticalSection lockWorkload; //use for locking shared data - std::vector<FileName> workload; //processes last elements of vector first! - bool threadHasMutex; + struct SharedData + { + std::vector<FileName> workload; //processes last elements of vector first! + boost::mutex mutex; + boost::condition_variable condition; //signal event: data for processing available + } shared; //------------------------------------------------------------ - //signal event: icon buffer(main thread) -> worker thread - wxMutex threadIsListening; - wxCondition continueWork; //wake up thread + IconBuffer& iconBuffer; - IconBuffer* iconBuffer; + boost::thread threadObj; }; -IconBuffer::WorkerThread::WorkerThread(IconBuffer* iconBuff) : - wxThread(wxTHREAD_DETACHED), //we're using the thread encapsulated in a static object => use "detached" to avoid main thread waiting for this thread on exit(which in turn would prevent deletion of static object...ect.) => deadlock! - threadHasMutex(false), - threadIsListening(), - continueWork(threadIsListening), +IconBuffer::WorkerThread::WorkerThread(IconBuffer& iconBuff) : iconBuffer(iconBuff) { - if (Create() != wxTHREAD_NO_ERROR) - throw std::runtime_error("Error creating icon buffer worker thread!"); + threadObj = boost::thread(boost::ref(*this)); //localize all thread logic to this class! +} - if (Run() != wxTHREAD_NO_ERROR) - throw std::runtime_error("Error starting icon buffer worker thread!"); - //wait until thread has aquired mutex - bool hasMutex = false; - while (!hasMutex) - { - wxMilliSleep(5); - wxCriticalSectionLocker dummy(lockWorkload); - hasMutex = threadHasMutex; - } //polling -> it's not nice, but works and is no issue +IconBuffer::WorkerThread::~WorkerThread() +{ + setWorkload(std::vector<Zstring>()); //make sure interruption point is always reached! + threadObj.interrupt(); + threadObj.join(); } void IconBuffer::WorkerThread::setWorkload(const std::vector<Zstring>& load) //(re-)set new workload of icons to be retrieved { - wxCriticalSectionLocker dummy(lockWorkload); - - workload.clear(); - for (std::vector<Zstring>::const_iterator i = load.begin(); i != load.end(); ++i) - workload.push_back(FileName(i->c_str(), i->c_str() + i->length() + 1)); //make DEEP COPY from Zstring (include null-termination)! - - if (!workload.empty()) - continueWork.Signal(); //wake thread IF he is waiting -} + { + boost::lock_guard<boost::mutex> dummy(shared.mutex); + shared.workload.clear(); + for (std::vector<Zstring>::const_iterator i = load.begin(); i != load.end(); ++i) + shared.workload.push_back(FileName(i->c_str(), i->c_str() + i->length() + 1)); //make DEEP COPY from Zstring (include null-termination)! + } -void IconBuffer::WorkerThread::quitThread() -{ - setWorkload(std::vector<Zstring>()); - Delete(); //gracefully terminate a detached thread... + shared.condition.notify_one(); + //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref } -wxThread::ExitCode IconBuffer::WorkerThread::Entry() +void IconBuffer::WorkerThread::operator()() //thread entry { try { - //this lock needs to be called from WITHIN the thread => calling it from constructor(Main thread) would be useless (signal direction: main -> thread) - wxMutexLocker dummy(threadIsListening); //this mutex STAYS locked all the time except of continueWork.Wait()! - { - wxCriticalSectionLocker dummy2(lockWorkload); - threadHasMutex = true; - } - while (true) { - continueWork.WaitTimeout(100); //waiting for continueWork.Signal(); unlocks Mutex "threadIsListening" - - if (TestDestroy()) - return 0; + { + boost::unique_lock<boost::mutex> dummy(shared.mutex); + while(shared.workload.empty()) + shared.condition.wait(dummy); //interruption point! + //shared.condition.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(100)); + } - doWork(); + doWork(); //no need to lock the complete method! } } catch (const std::exception& e) //exceptions must be catched per thread { - wxMessageBox(wxString::FromAscii(e.what()), _("An exception occured!"), wxOK | wxICON_ERROR); - return 0; + wxMessageBox(wxString::FromAscii(e.what()), wxString(_("An exception occurred!")) + wxT("(Icon buffer)"), wxOK | wxICON_ERROR); } } @@ -375,14 +346,14 @@ void IconBuffer::WorkerThread::doWork() { Zstring fileName; { - wxCriticalSectionLocker dummy(lockWorkload); - if (workload.empty()) + boost::lock_guard<boost::mutex> dummy(shared.mutex); + if (shared.workload.empty()) break; //enter waiting state - fileName = &workload.back()[0]; //deep copy (includes NULL-termination) - workload.pop_back(); + fileName = &shared.workload.back()[0]; //deep copy (includes NULL-termination) + shared.workload.pop_back(); } - if (iconBuffer->requestFileIcon(fileName)) //thread safety: Zstring okay, won't be reference-counted in requestIcon() + if (iconBuffer.requestFileIcon(fileName)) //thread safety: Zstring okay, won't be reference-counted in requestIcon() continue; //icon already in buffer: skip #ifdef FFS_WIN @@ -390,16 +361,16 @@ void IconBuffer::WorkerThread::doWork() if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension { const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName); - iconBuffer->insertIntoBuffer(fileName, newIcon); + iconBuffer.insertIntoBuffer(fileName, newIcon); } else //no read-access to disk! determine icon by extension { const IconHolder newIcon = IconBuffer::getAssociatedIconByExt(extension); - iconBuffer->insertIntoBuffer(extension, newIcon); + iconBuffer.insertIntoBuffer(extension, newIcon); } #elif defined FFS_LINUX const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName); - iconBuffer->insertIntoBuffer(fileName, newIcon); + iconBuffer.insertIntoBuffer(fileName, newIcon); #endif } } @@ -419,22 +390,18 @@ IconBuffer& IconBuffer::getInstance() IconBuffer::IconBuffer() : - lockIconDB( new wxCriticalSection), buffer( new IconDB), bufSequence(new IconDbSequence), - worker( new WorkerThread(this)) //might throw exceptions! + worker( new WorkerThread(*this)) //might throw exceptions! {} -IconBuffer::~IconBuffer() -{ - worker->quitThread(); -} +IconBuffer::~IconBuffer() {} //auto_ptr<>: keep destructor non-inline bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { - wxCriticalSectionLocker dummy(*lockIconDB); + boost::lock_guard<boost::mutex> dummy(lockIconDB); #ifdef FFS_WIN //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension @@ -462,7 +429,7 @@ void IconBuffer::setWorkload(const std::vector<Zstring>& load) void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, const IconHolder& icon) //called by worker thread { - wxCriticalSectionLocker dummy(*lockIconDB); + boost::lock_guard<boost::mutex> dummy(lockIconDB); //thread safety, ref-counting: (implicitly) make deep copy! const Zstring fileNameZ = entryName; diff --git a/library/iconBuffer.h b/library/icon_buffer.h index 4a19af3d..9dc1ee55 100644 --- a/library/iconBuffer.h +++ b/library/icon_buffer.h @@ -10,12 +10,11 @@ #include <vector> #include "../shared/zstring.h" #include <memory> +#include <wx/icon.h> +#include <boost/thread/mutex.hpp> -class wxCriticalSection; -class wxIcon; - -namespace FreeFileSync +namespace ffs3 { class IconBuffer @@ -50,13 +49,13 @@ private: static IconHolder getAssociatedIconByExt(const Zstring& extension); //---------------------- Shared Data ------------------------- - std::auto_ptr<wxCriticalSection> lockIconDB; + boost::mutex lockIconDB; std::auto_ptr<IconDB> buffer; //use synchronisation when accessing this! std::auto_ptr<IconDbSequence> bufSequence; //save sequence of buffer entry to delete oldest elements (implicitly shared by sharing Zstring with IconDB!!!) //------------------------------------------------------------ class WorkerThread; - WorkerThread* worker; //detached thread => destroys itself + std::auto_ptr<WorkerThread> worker; }; } diff --git a/library/pch.h b/library/pch.h index 573ae73f..798319c7 100644 --- a/library/pch.h +++ b/library/pch.h @@ -8,7 +8,7 @@ #define FFS_PRECOMPILED_HEADER //pay attention when using this file: might cause issues! -#ifndef __WXDEBUG__ +#ifdef NDEBUG do NOT use in release build! #endif @@ -102,13 +102,10 @@ do NOT use in release build! //Boost #include <boost/shared_ptr.hpp> -//#include <boost/bind.hpp> <- conflict with #include <boost/lambda/bind.hpp> #include <boost/scoped_array.hpp> -#include <boost/lambda/lambda.hpp> -#ifdef FFS_WIN +#ifdef __WXMSW__ #include <wx/msw/wrapwin.h> //includes "windows.h" -#endif //FFS_WIN -//##################################################### +#endif //__WXMSW__ #endif //FFS_PRECOMPILED_HEADER diff --git a/library/processXml.cpp b/library/process_xml.cpp index c9b641fb..e81fb3f9 100644 --- a/library/processXml.cpp +++ b/library/process_xml.cpp @@ -4,15 +4,15 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "processXml.h" -#include "../shared/xmlBase.h" +#include "process_xml.h" +#include "../shared/xml_base.h" #include <wx/intl.h> #include <wx/filefn.h> -#include "../shared/globalFunctions.h" -#include "../shared/standardPaths.h" -#include "../shared/stringConv.h" +#include "../shared/global_func.h" +#include "../shared/standard_paths.h" +#include "../shared/string_conv.h" -using namespace FreeFileSync; +using namespace ffs3; using namespace xmlAccess; //functionally needed!!! @@ -49,7 +49,7 @@ void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent); bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc); -void xmlAccess::readGuiConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) { //load XML if (!wxFileExists(filename)) @@ -61,13 +61,13 @@ void xmlAccess::readGuiConfig(const wxString& filename, xmlAccess::XmlGuiConfig& FfsXmlParser parser(doc.RootElement()); parser.readXmlGuiConfig(config); //read GUI layout configuration - if (parser.errorsOccured()) + if (parser.errorsOccurred()) throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + parser.getErrorMessageFormatted(), XmlError::WARNING); } -void xmlAccess::readBatchConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) { //load XML if (!wxFileExists(filename)) @@ -79,13 +79,13 @@ void xmlAccess::readBatchConfig(const wxString& filename, xmlAccess::XmlBatchCon FfsXmlParser parser(doc.RootElement()); parser.readXmlBatchConfig(config); //read GUI layout configuration - if (parser.errorsOccured()) + if (parser.errorsOccurred()) throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + parser.getErrorMessageFormatted(), XmlError::WARNING); } -void xmlAccess::readGlobalSettings(xmlAccess::XmlGlobalSettings& config) +void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) { //load XML if (!wxFileExists(getGlobalConfigFile())) @@ -97,13 +97,13 @@ void xmlAccess::readGlobalSettings(xmlAccess::XmlGlobalSettings& config) FfsXmlParser parser(doc.RootElement()); parser.readXmlGlobalSettings(config); //read GUI layout configuration - if (parser.errorsOccured()) + if (parser.errorsOccurred()) throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"\n\n") + parser.getErrorMessageFormatted(), XmlError::WARNING); } -void xmlAccess::writeGuiConfig(const XmlGuiConfig& outputCfg, const wxString& filename) +void xmlAccess::writeConfig(const XmlGuiConfig& outputCfg, const wxString& filename) { TiXmlDocument doc; getDefaultXmlDocument(XML_GUI_CONFIG, doc); @@ -116,7 +116,7 @@ void xmlAccess::writeGuiConfig(const XmlGuiConfig& outputCfg, const wxString& fi } -void xmlAccess::writeBatchConfig(const XmlBatchConfig& outputCfg, const wxString& filename) +void xmlAccess::writeConfig(const XmlBatchConfig& outputCfg, const wxString& filename) { TiXmlDocument doc; getDefaultXmlDocument(XML_BATCH_CONFIG, doc); @@ -129,7 +129,7 @@ void xmlAccess::writeBatchConfig(const XmlBatchConfig& outputCfg, const wxString } -void xmlAccess::writeGlobalSettings(const XmlGlobalSettings& outputCfg) +void xmlAccess::writeConfig(const XmlGlobalSettings& outputCfg) { TiXmlDocument doc; getDefaultXmlDocument(XML_GLOBAL_SETTINGS, doc); @@ -148,9 +148,9 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent, Compare if (xmlAccess::readXmlElement(name, parent, dummy)) { if (dummy == "ByTimeAndSize") - output = FreeFileSync::CMP_BY_TIME_SIZE; + output = ffs3::CMP_BY_TIME_SIZE; else if (dummy == "ByContent") - output = FreeFileSync::CMP_BY_CONTENT; + output = ffs3::CMP_BY_CONTENT; else return false; @@ -199,17 +199,17 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent, xmlAcce } -bool readXmlElement(const std::string& name, const TiXmlElement* parent , FreeFileSync::DeletionPolicy& output) +bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3::DeletionPolicy& output) { std::string dummy; if (xmlAccess::readXmlElement(name, parent, dummy)) { if (dummy == "DeletePermanently") - output = FreeFileSync::DELETE_PERMANENTLY; + output = ffs3::DELETE_PERMANENTLY; else if (dummy == "MoveToRecycleBin") - output = FreeFileSync::MOVE_TO_RECYCLE_BIN; + output = ffs3::MOVE_TO_RECYCLE_BIN; else if (dummy == "MoveToCustomDirectory") - output = FreeFileSync::MOVE_TO_CUSTOM_DIRECTORY; + output = ffs3::MOVE_TO_CUSTOM_DIRECTORY; else return false; @@ -219,17 +219,17 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent , FreeFi } -bool readXmlElement(const std::string& name, const TiXmlElement* parent , FreeFileSync::SymLinkHandling& output) +bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3::SymLinkHandling& output) { std::string dummy; if (xmlAccess::readXmlElement(name, parent, dummy)) { if (dummy == "Ignore") - output = FreeFileSync::SYMLINK_IGNORE; + output = ffs3::SYMLINK_IGNORE; else if (dummy == "UseDirectly") - output = FreeFileSync::SYMLINK_USE_DIRECTLY; + output = ffs3::SYMLINK_USE_DIRECTLY; else if (dummy == "FollowLink") - output = FreeFileSync::SYMLINK_FOLLOW_LINK; + output = ffs3::SYMLINK_FOLLOW_LINK; else return false; @@ -318,9 +318,6 @@ void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) //read compare variant readXmlElementLogging("Variant", cmpSettings, mainCfg.compareVar); - //max. allowed file time deviation - readXmlElementLogging("FileTimeTolerance", cmpSettings, mainCfg.hidden.fileTimeTolerance); - //include symbolic links at all? readXmlElementLogging("HandleSymlinks", cmpSettings, mainCfg.handleSymlinks); @@ -338,12 +335,6 @@ void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) readXmlElementLogging("Conflict", syncDirections, mainCfg.syncConfiguration.conflict); //########################################################### - const TiXmlElement* syncConfig = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").ToElement(); - - //verify file copying - readXmlElementLogging("VerifyCopiedFiles", syncConfig, mainCfg.hidden.verifyFileCopy); - -//########################################################### const TiXmlElement* miscSettings = hRoot.FirstChild("MainConfig").FirstChild("Miscellaneous").ToElement(); //misc @@ -430,6 +421,15 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg //copy locked files using VSS readXmlElementLogging("CopyLockedFiles", global, outputCfg.copyLockedFiles); + //file permissions + readXmlElementLogging("CopyFilePermissions", global, outputCfg.copyFilePermissions); + + //verify file copying + readXmlElementLogging("VerifyCopiedFiles", global, outputCfg.verifyFileCopy); + + //max. allowed file time deviation + readXmlElementLogging("FileTimeTolerance", global, outputCfg.fileTimeTolerance); + const TiXmlElement* optionalDialogs = TiXmlHandleConst(getRoot()).FirstChild("Shared").FirstChild("ShowOptionalDialogs").ToElement(); @@ -559,10 +559,10 @@ void addXmlElement(const std::string& name, const CompareVariant variant, TiXmlE { switch (variant) { - case FreeFileSync::CMP_BY_TIME_SIZE: + case ffs3::CMP_BY_TIME_SIZE: xmlAccess::addXmlElement(name, std::string("ByTimeAndSize"), parent); break; - case FreeFileSync::CMP_BY_CONTENT: + case ffs3::CMP_BY_CONTENT: xmlAccess::addXmlElement(name, std::string("ByContent"), parent); break; } @@ -603,34 +603,34 @@ void addXmlElement(const std::string& name, const xmlAccess::OnError value, TiXm } -void addXmlElement(const std::string& name, const FreeFileSync::DeletionPolicy value, TiXmlElement* parent) +void addXmlElement(const std::string& name, const ffs3::DeletionPolicy value, TiXmlElement* parent) { switch (value) { - case FreeFileSync::DELETE_PERMANENTLY: + case ffs3::DELETE_PERMANENTLY: xmlAccess::addXmlElement(name, std::string("DeletePermanently"), parent); break; - case FreeFileSync::MOVE_TO_RECYCLE_BIN: + case ffs3::MOVE_TO_RECYCLE_BIN: xmlAccess::addXmlElement(name, std::string("MoveToRecycleBin"), parent); break; - case FreeFileSync::MOVE_TO_CUSTOM_DIRECTORY: + case ffs3::MOVE_TO_CUSTOM_DIRECTORY: xmlAccess::addXmlElement(name, std::string("MoveToCustomDirectory"), parent); break; } } -void addXmlElement(const std::string& name, const FreeFileSync::SymLinkHandling value, TiXmlElement* parent) +void addXmlElement(const std::string& name, const ffs3::SymLinkHandling value, TiXmlElement* parent) { switch (value) { - case FreeFileSync::SYMLINK_IGNORE: + case ffs3::SYMLINK_IGNORE: xmlAccess::addXmlElement(name, std::string("Ignore"), parent); break; - case FreeFileSync::SYMLINK_USE_DIRECTLY: + case ffs3::SYMLINK_USE_DIRECTLY: xmlAccess::addXmlElement(name, std::string("UseDirectly"), parent); break; - case FreeFileSync::SYMLINK_FOLLOW_LINK: + case ffs3::SYMLINK_FOLLOW_LINK: xmlAccess::addXmlElement(name, std::string("FollowLink"), parent); break; } @@ -704,11 +704,6 @@ void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent) bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) { - //reset hidden settings: we don't want implicit behaviour! Hidden settings are loaded but changes are not saved! - MainConfiguration mainCfgLocal = mainCfg; - mainCfgLocal.hidden = HiddenSettings(); - - TiXmlElement* root = doc.RootElement(); if (!root) return false; @@ -720,34 +715,27 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) settings->LinkEndChild(cmpSettings); //write compare algorithm - addXmlElement("Variant", mainCfgLocal.compareVar, cmpSettings); - - //max. allowed file time deviation - addXmlElement("FileTimeTolerance", mainCfgLocal.hidden.fileTimeTolerance, cmpSettings); + addXmlElement("Variant", mainCfg.compareVar, cmpSettings); //include symbolic links at all? - addXmlElement("HandleSymlinks", mainCfgLocal.handleSymlinks, cmpSettings); + addXmlElement("HandleSymlinks", mainCfg.handleSymlinks, cmpSettings); //########################################################### TiXmlElement* syncSettings = new TiXmlElement("Synchronization"); settings->LinkEndChild(syncSettings); //write sync configuration - addXmlElement("Automatic", mainCfgLocal.syncConfiguration.automatic, syncSettings); + addXmlElement("Automatic", mainCfg.syncConfiguration.automatic, syncSettings); TiXmlElement* syncDirections = new TiXmlElement("Directions"); syncSettings->LinkEndChild(syncDirections); - addXmlElement("LeftOnly", mainCfgLocal.syncConfiguration.exLeftSideOnly, syncDirections); - addXmlElement("RightOnly", mainCfgLocal.syncConfiguration.exRightSideOnly, syncDirections); - addXmlElement("LeftNewer", mainCfgLocal.syncConfiguration.leftNewer, syncDirections); - addXmlElement("RightNewer", mainCfgLocal.syncConfiguration.rightNewer, syncDirections); - addXmlElement("Different", mainCfgLocal.syncConfiguration.different, syncDirections); - addXmlElement("Conflict", mainCfgLocal.syncConfiguration.conflict, syncDirections); - -//########################################################### - //verify file copying - addXmlElement("VerifyCopiedFiles", mainCfgLocal.hidden.verifyFileCopy, syncSettings); + addXmlElement("LeftOnly", mainCfg.syncConfiguration.exLeftSideOnly, syncDirections); + addXmlElement("RightOnly", mainCfg.syncConfiguration.exRightSideOnly, syncDirections); + addXmlElement("LeftNewer", mainCfg.syncConfiguration.leftNewer, syncDirections); + addXmlElement("RightNewer", mainCfg.syncConfiguration.rightNewer, syncDirections); + addXmlElement("Different", mainCfg.syncConfiguration.different, syncDirections); + addXmlElement("Conflict", mainCfg.syncConfiguration.conflict, syncDirections); //########################################################### TiXmlElement* miscSettings = new TiXmlElement("Miscellaneous"); @@ -757,22 +745,22 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) TiXmlElement* filter = new TiXmlElement("Filter"); miscSettings->LinkEndChild(filter); - addXmlElement("Include", mainCfgLocal.globalFilter.includeFilter, filter); - addXmlElement("Exclude", mainCfgLocal.globalFilter.excludeFilter, filter); + addXmlElement("Include", mainCfg.globalFilter.includeFilter, filter); + addXmlElement("Exclude", mainCfg.globalFilter.excludeFilter, filter); //other - addXmlElement("DeletionPolicy", mainCfgLocal.handleDeletion, miscSettings); - addXmlElement("CustomDeletionFolder", mainCfgLocal.customDeletionDirectory, miscSettings); + addXmlElement("DeletionPolicy", mainCfg.handleDeletion, miscSettings); + addXmlElement("CustomDeletionFolder", mainCfg.customDeletionDirectory, miscSettings); //########################################################### TiXmlElement* pairs = new TiXmlElement("FolderPairs"); settings->LinkEndChild(pairs); //write first folder pair - writeXmlLocalConfig(mainCfgLocal.firstPair, *pairs); + writeXmlLocalConfig(mainCfg.firstPair, *pairs); //write additional folder pairs - for (std::vector<FolderPairEnh>::const_iterator i = mainCfgLocal.additionalPairs.begin(); i != mainCfgLocal.additionalPairs.end(); ++i) + for (std::vector<FolderPairEnh>::const_iterator i = mainCfg.additionalPairs.begin(); i != mainCfg.additionalPairs.end(); ++i) writeXmlLocalConfig(*i, *pairs); return true; @@ -842,6 +830,15 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD //copy locked files using VSS addXmlElement("CopyLockedFiles", inputCfg.copyLockedFiles, global); + //file permissions + addXmlElement("CopyFilePermissions", inputCfg.copyFilePermissions, global); + + //verify file copying + addXmlElement("VerifyCopiedFiles", inputCfg.verifyFileCopy, global); + + //max. allowed file time deviation + addXmlElement("FileTimeTolerance", inputCfg.fileTimeTolerance, global); + //optional dialogs TiXmlElement* optionalDialogs = new TiXmlElement("ShowOptionalDialogs"); @@ -984,6 +981,12 @@ int xmlAccess::retrieveSystemLanguage() } +wxString xmlAccess::getGlobalConfigFile() +{ + return ffs3::getConfigDir() + wxT("GlobalSettings.xml"); +} + + void xmlAccess::OptionalDialogs::resetDialogs() { warningDependentFolders = true; @@ -998,40 +1001,137 @@ void xmlAccess::OptionalDialogs::resetDialogs() xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) { - xmlAccess::XmlGuiConfig output; + XmlGuiConfig output; output.mainCfg = batchCfg.mainCfg; return output; } -void xmlAccess::readGuiOrBatchConfig(const wxString& filename, XmlGuiConfig& config) //throw (xmlAccess::XmlError); +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) { - if (xmlAccess::getXmlType(filename) != xmlAccess::XML_BATCH_CONFIG) + XmlBatchConfig output; + output.mainCfg = guiCfg.mainCfg; + + if (guiCfg.ignoreErrors) + output.handleError = xmlAccess::ON_ERROR_IGNORE; + else + output.handleError = xmlAccess::ON_ERROR_POPUP; + + return output; +} + + +xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filenames) //throw () +{ + bool guiCfgExists = false; + bool batchCfgExists = false; + + for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) { - xmlAccess::readGuiConfig(filename, config); - return; + switch (xmlAccess::getXmlType(*i)) //throw() + { + case XML_GUI_CONFIG: + guiCfgExists = true; + break; + + case XML_BATCH_CONFIG: + batchCfgExists = true; + break; + + case XML_GLOBAL_SETTINGS: + case XML_REAL_CONFIG: + case XML_OTHER: + return MERGE_OTHER; + } } - //convert batch config to gui config - xmlAccess::XmlBatchConfig batchCfg; + if (guiCfgExists && batchCfgExists) + return MERGE_GUI_BATCH; + else if (guiCfgExists && !batchCfgExists) + return MERGE_GUI; + else if (!guiCfgExists && batchCfgExists) + return MERGE_BATCH; + else + return MERGE_OTHER; +} + + +namespace +{ +template <class XmlCfg> +XmlCfg loadCfgImpl(const wxString& filename, std::auto_ptr<xmlAccess::XmlError>& exeption) //throw (xmlAccess::XmlError) +{ + XmlCfg cfg; try { - xmlAccess::readBatchConfig(filename, batchCfg); //throw (xmlAccess::XmlError); + xmlAccess::readConfig(filename, cfg); //throw (xmlAccess::XmlError); } catch (const xmlAccess::XmlError& e) { - if (e.getSeverity() != xmlAccess::XmlError::WARNING) + if (e.getSeverity() == xmlAccess::XmlError::FATAL) throw; + else + exeption.reset(new xmlAccess::XmlError(e)); + } + return cfg; +} + + +template <class XmlCfg> +void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw (xmlAccess::XmlError) +{ + using namespace xmlAccess; - config = convertBatchToGui(batchCfg); //do work despite parsing errors, then re-throw - throw; // + assert(!filenames.empty()); + if (filenames.empty()) + return; + + std::vector<ffs3::MainConfiguration> mainCfgs; + std::auto_ptr<XmlError> savedException; + + for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) + { + switch (getXmlType(*i)) + { + case XML_GUI_CONFIG: + mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) + break; + + case XML_BATCH_CONFIG: + mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) + break; + + case XML_GLOBAL_SETTINGS: + case XML_REAL_CONFIG: + case XML_OTHER: + break; + } } - config = convertBatchToGui(batchCfg); + if (mainCfgs.empty()) + throw XmlError(_("Invalid FreeFileSync config file!")); + + try //...to init all non-"mainCfg" settings with first config file + { + xmlAccess::readConfig(filenames[0], config); //throw (xmlAccess::XmlError); + } + catch (...) {} + + config.mainCfg = merge(mainCfgs); + + if (savedException.get()) //"re-throw" exception + throw *savedException; +} } -wxString xmlAccess::getGlobalConfigFile() +void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config) //throw (xmlAccess::XmlError) +{ + mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::XmlError) +} + + +void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config) //throw (xmlAccess::XmlError); { - return FreeFileSync::getConfigDir() + wxT("GlobalSettings.xml"); + mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::XmlError) } diff --git a/library/processXml.h b/library/process_xml.h index b46f0163..22465739 100644 --- a/library/processXml.h +++ b/library/process_xml.h @@ -8,7 +8,7 @@ #define PROCESSXML_H_INCLUDED #include "../structures.h" -#include "../shared/xmlError.h" +#include "../shared/xml_error.h" namespace xmlAccess @@ -54,7 +54,7 @@ struct XmlGuiConfig ignoreErrors(false), syncPreviewEnabled(true) {} //initialize values - FreeFileSync::MainConfiguration mainCfg; + ffs3::MainConfiguration mainCfg; bool hideFilteredElements; bool ignoreErrors; //reaction on error situation during synchronization @@ -79,13 +79,14 @@ struct XmlBatchConfig { XmlBatchConfig() : silent(false), handleError(ON_ERROR_POPUP) {} - FreeFileSync::MainConfiguration mainCfg; + ffs3::MainConfiguration mainCfg; bool silent; wxString logFileDirectory; OnError handleError; //reaction on error situation during synchronization }; + int retrieveSystemLanguage(); @@ -117,12 +118,18 @@ struct XmlGlobalSettings XmlGlobalSettings() : programLanguage(retrieveSystemLanguage()), ignoreOneHourDiff(false), - copyLockedFiles(true) - {} + copyLockedFiles(true), + copyFilePermissions(false), + fileTimeTolerance(2), //default 2s: FAT vs NTFS + verifyFileCopy(false) {} int programLanguage; bool ignoreOneHourDiff; //ignore +/- 1 hour due to DST change bool copyLockedFiles; //VSS usage + bool copyFilePermissions; + + size_t fileTimeTolerance; //max. allowed file time deviation + bool verifyFileCopy; //verify copied files OptionalDialogs optDialogs; @@ -229,16 +236,31 @@ bool sortByPositionAndVisibility(const ColumnAttrib& a, const ColumnAttrib& b) return a.position < b.position; } -void readGuiConfig( const wxString& filename, XmlGuiConfig& config); //throw (xmlAccess::XmlError); -void readBatchConfig(const wxString& filename, XmlBatchConfig& config); //throw (xmlAccess::XmlError); -void readGlobalSettings( XmlGlobalSettings& config); //throw (xmlAccess::XmlError); +void readConfig(const wxString& filename, XmlGuiConfig& config); //throw (xmlAccess::XmlError) +void readConfig(const wxString& filename, XmlBatchConfig& config); //throw (xmlAccess::XmlError) +void readConfig( XmlGlobalSettings& config); //throw (xmlAccess::XmlError) + +void writeConfig(const XmlGuiConfig& outputCfg, const wxString& filename); //throw (xmlAccess::XmlError) +void writeConfig(const XmlBatchConfig& outputCfg, const wxString& filename); //throw (xmlAccess::XmlError) +void writeConfig(const XmlGlobalSettings& outputCfg); //throw (xmlAccess::XmlError) + +//config conversion utilities +XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); +XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg); -void readGuiOrBatchConfig(const wxString& filename, XmlGuiConfig& config); //throw (xmlAccess::XmlError); -XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); -void writeGuiConfig( const XmlGuiConfig& outputCfg, const wxString& filename); //throw (xmlAccess::XmlError); -void writeBatchConfig( const XmlBatchConfig& outputCfg, const wxString& filename); //throw (xmlAccess::XmlError); -void writeGlobalSettings(const XmlGlobalSettings& outputCfg); //throw (xmlAccess::XmlError); +//convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: +enum MergeType +{ + MERGE_GUI, //pure gui config files + MERGE_BATCH, // " batch " + MERGE_GUI_BATCH, //gui and batch files + MERGE_OTHER +}; +MergeType getMergeType(const std::vector<wxString>& filenames); //throw () + +void convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config); //throw (xmlAccess::XmlError) +void convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config); //throw (xmlAccess::XmlError) } diff --git a/library/resources.cpp b/library/resources.cpp index 2104efc3..637dfea3 100644 --- a/library/resources.cpp +++ b/library/resources.cpp @@ -10,12 +10,12 @@ #include <wx/image.h> #include <wx/icon.h> #include <wx/mstream.h> -#include "../shared/stringConv.h" -#include "../shared/systemConstants.h" +#include "../shared/string_conv.h" +#include "../shared/system_constants.h" #include <memory> -#include "../shared/standardPaths.h" +#include "../shared/standard_paths.h" -using namespace FreeFileSync; +using namespace ffs3; const GlobalResources& GlobalResources::getInstance() @@ -67,7 +67,7 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation* animation) void GlobalResources::load() const { - wxFFileInputStream input(FreeFileSync::getResourceDir() + wxT("Resources.dat")); + wxFFileInputStream input(ffs3::getResourceDir() + wxT("Resources.dat")); if (input.IsOk()) //if not... we don't want to react too harsh here { //activate support for .png files diff --git a/library/softFilter.cpp b/library/soft_filter.cpp index 496c185e..18dcfd54 100644 --- a/library/softFilter.cpp +++ b/library/soft_filter.cpp @@ -4,7 +4,7 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "softFilter.h" +#include "soft_filter.h" diff --git a/library/softFilter.h b/library/soft_filter.h index 275aa9bd..dc889245 100644 --- a/library/softFilter.h +++ b/library/soft_filter.h @@ -7,7 +7,7 @@ #ifndef SOFTFILTER_H_INCLUDED #define SOFTFILTER_H_INCLUDED -#include "../fileHierarchy.h" +#include "../file_hierarchy.h" #include <wx/timer.h> /* Semantics of SoftFilter: @@ -18,7 +18,7 @@ Semantics of SoftFilter: -> SoftFilter is equivalent to a user temporarily (de-)selecting rows */ -namespace FreeFileSync +namespace ffs3 { class SoftFilter diff --git a/library/statistics.cpp b/library/statistics.cpp index 3a6970d9..d246a7cc 100644 --- a/library/statistics.cpp +++ b/library/statistics.cpp @@ -7,13 +7,13 @@ #include "statistics.h" #include <wx/ffile.h> -#include "../shared/globalFunctions.h" -#include "statusHandler.h" -//#include "../algorithm.h" +#include "../shared/global_func.h" +#include "status_handler.h" #include "../shared/util.h" #include <wx/intl.h> #include <limits> #include <wx/stopwatch.h> +#include "../shared/assert_static.h" RetrieveStatistics::RetrieveStatistics() : @@ -31,7 +31,7 @@ RetrieveStatistics::~RetrieveStatistics() for (std::vector<statEntry>::const_iterator i = data.begin(); i != data.end(); ++i) { - using globalFunctions::numberToString; + using common::numberToString; outputFile.Write(numberToString(i->time)); outputFile.Write(wxT(";")); outputFile.Write(numberToString(i->objects)); @@ -53,42 +53,42 @@ void RetrieveStatistics::writeEntry(const double value, const int objects) //######################################################################################## - +namespace +{ +template <class T> inline -bool isNull(double number) +bool isNull(T number) { - return globalFunctions::abs(number) < std::numeric_limits<double>::epsilon(); + return common::abs(number) <= std::numeric_limits<T>::epsilon(); //epsilon == 0 for integer types, therefore less-equal(!) +} } inline wxString Statistics::formatRemainingTime(double timeInMs) const { -#ifndef _MSC_VER -#warning adapt units "%x sec" -#endif - bool unitSec = true; double remainingTime = timeInMs / 1000; - wxString unit = _(" sec"); + + wxString output = _("%x sec"); if (remainingTime > 55) { unitSec = false; remainingTime /= 60; - unit = _(" min"); + output = _("%x min"); if (remainingTime > 59) { remainingTime /= 60; - unit = _(" hour(s)"); + output = _("%x hour(s)"); if (remainingTime > 23) { remainingTime /= 24; - unit = _(" day(s)"); + output = _("%x day(s)"); } } } - int formattedTime = globalFunctions::round(remainingTime); + int formattedTime = common::round(remainingTime); //reduce precision to 5 seconds if (unitSec && formattedTime % 5 != 0) @@ -100,7 +100,7 @@ wxString Statistics::formatRemainingTime(double timeInMs) const { if (unitSec) { - formattedTime = globalFunctions::round(remainingTime); + formattedTime = common::round(remainingTime); formattedTime -= formattedTime % 5; //"floor" } else @@ -108,8 +108,10 @@ wxString Statistics::formatRemainingTime(double timeInMs) const } remainingTimeLast = formattedTime; - return globalFunctions::numberToString(formattedTime) + unit; - //+ wxT("(") + globalFunctions::numberToWxString(globalFunctions::round(timeInMs / 1000)) + wxT(")"); + output.Replace(wxT("%x"), common::numberToString(formattedTime)); + + return output; + //+ wxT("(") + common::numberToWxString(common::round(timeInMs / 1000)) + wxT(")"); } @@ -191,7 +193,7 @@ wxString Statistics::getBytesPerSecond() const const double dataDelta = measurements.back().data - frontElement->data; if (!isNull(timeDelta)) - return FreeFileSync::formatFilesizeToShortString(dataDelta * 1000 / timeDelta) + _("/sec"); + return ffs3::formatFilesizeToShortString(dataDelta * 1000 / timeDelta) + _("/sec"); } return wxT("-"); //fallback diff --git a/library/statistics.h b/library/statistics.h index ba9d8afb..26e8c196 100644 --- a/library/statistics.h +++ b/library/statistics.h @@ -11,6 +11,8 @@ #include <list> #include <memory> #include <wx/defs.h> + + #include <wx/string.h> class wxStopWatch; diff --git a/library/statusHandler.cpp b/library/status_handler.cpp index 7ac46134..0f953a32 100644 --- a/library/statusHandler.cpp +++ b/library/status_handler.cpp @@ -4,7 +4,7 @@ // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "statusHandler.h" +#include "status_handler.h" #include <wx/app.h> #include <wx/timer.h> diff --git a/library/statusHandler.h b/library/status_handler.h index a1226c54..a1226c54 100644 --- a/library/statusHandler.h +++ b/library/status_handler.h |