// ************************************************************************** // * 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) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** #ifndef FILEHIERARCHY_H_INCLUDED #define FILEHIERARCHY_H_INCLUDED #include "shared/zstring.h" #include #include #include #include #include "shared/fixed_list.h" #include "structures.h" #include "shared/guid.h" #include "shared/file_id.h" #include "shared/int64.h" #include "structures.h" #include "library/hard_filter.h" namespace zen { struct FileDescriptor { FileDescriptor() {} FileDescriptor(zen::Int64 lastWriteTimeRawIn, zen::UInt64 fileSizeIn) : lastWriteTimeRaw(lastWriteTimeRawIn), fileSize(fileSizeIn) {} zen::Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) zen::UInt64 fileSize; }; struct LinkDescriptor { enum LinkType { TYPE_DIR, //Windows: dir symlink; Linux: dir symlink TYPE_FILE //Windows: file symlink; Linux: file symlink or broken link (or other symlink, pathological) }; LinkDescriptor() : type(TYPE_FILE) {} LinkDescriptor(zen::Int64 lastWriteTimeRawIn, const Zstring& targetPathIn, LinkType lt) : lastWriteTimeRaw(lastWriteTimeRawIn), targetPath(targetPathIn), type(lt) {} zen::Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) Zstring targetPath; //symlink "content", may be empty if determination failed LinkType type; //type is required for Windows only! On Linux there is no such thing => consider this when comparing Symbolic Links! }; enum SelectedSide { LEFT_SIDE, RIGHT_SIDE }; template struct OtherSide; template <> struct OtherSide { static const SelectedSide result = RIGHT_SIDE; }; template <> struct OtherSide { static const SelectedSide result = LEFT_SIDE; }; class BaseDirMapping; class DirMapping; class FileMapping; class SymLinkMapping; class FileSystemObject; //------------------------------------------------------------------ /* ERD: DirContainer 1 -----> 0..n DirContainer DirContainer 1 -----> 0..n FileDescriptor DirContainer 1 -----> 0..n LinkDescriptor */ struct DirContainer { //------------------------------------------------------------------ typedef std::map DirList; // typedef std::map FileList; //key: shortName typedef std::map LinkList; // //------------------------------------------------------------------ DirList dirs; //contained directories FileList files; //contained files LinkList links; //contained symlinks (note: only symlinks that are not treated as their target are placed here!) //convenience DirContainer& addSubDir(const Zstring& shortName) { //use C++11 emplace when available return dirs.insert(std::make_pair(shortName, DirContainer())).first->second; } void addSubFile(const Zstring& shortName, const FileDescriptor& fileData) { files.insert(std::make_pair(shortName, fileData)); } void addSubLink(const Zstring& shortName, const LinkDescriptor& linkData) { links.insert(std::make_pair(shortName, linkData)); } }; //------------------------------------------------------------------ /* inheritance diagram: FileSystemObject HierarchyObject /|\ /|\ _______________|______________ ______|______ | | | | | SymLinkMapping FileMapping DirMapping BaseDirMapping */ //------------------------------------------------------------------ class HierarchyObject { friend class DirMapping; friend class FileSystemObject; typedef zen::FixedList SubFileVec; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() typedef zen::FixedList SubLinkVec; //Note: deque<> has circular reference in VCPP! typedef zen::FixedList SubDirVec; public: DirMapping& addSubDir(const Zstring& shortNameLeft, const Zstring& shortNameRight); FileMapping& addSubFile(const Zstring& shortNameLeft, const FileDescriptor& left, //file exists on both sides CompareFilesResult defaultCmpResult, const Zstring& shortNameRight, const FileDescriptor& right); void addSubFile(const FileDescriptor& left, //file exists on left side only const Zstring& shortNameLeft); void addSubFile(const Zstring& shortNameRight, //file exists on right side only const FileDescriptor& right); SymLinkMapping& addSubLink(const Zstring& shortNameLeft, const LinkDescriptor& left, //link exists on both sides CompareSymlinkResult defaultCmpResult, const Zstring& shortNameRight, const LinkDescriptor& right); void addSubLink(const LinkDescriptor& left, //link exists on left side only const Zstring& shortNameLeft); void addSubLink(const Zstring& shortNameRight, //link exists on right side only const LinkDescriptor& right); const SubFileVec& refSubFiles() const { return subFiles; } /**/ SubFileVec& refSubFiles() { return subFiles; } const SubLinkVec& refSubLinks() const { return subLinks; } /**/ SubLinkVec& refSubLinks() { return subLinks; } const SubDirVec& refSubDirs() const { return subDirs; } /**/ SubDirVec& refSubDirs() { return subDirs; } BaseDirMapping& getRoot() { return root_; } protected: HierarchyObject(const Zstring& relativeNamePf, BaseDirMapping& baseMap) : objRelNamePf(relativeNamePf), root_(baseMap) {} ~HierarchyObject() {} //don't need polymorphic deletion virtual void flip(); void removeEmptyRec(); private: virtual void notifySyncCfgChanged() {} HierarchyObject(const HierarchyObject&); //this class is referenced by it's child elements => make it non-copyable/movable! HierarchyObject& operator=(const HierarchyObject&); // const Zstring& getObjRelativeNamePf() const { return objRelNamePf; } SubFileVec subFiles; //contained file maps SubLinkVec subLinks; //contained symbolic link maps SubDirVec subDirs; //contained directory maps Zstring objRelNamePf; BaseDirMapping& root_; }; //------------------------------------------------------------------ class BaseDirMapping : public HierarchyObject //synchronization base directory { public: BaseDirMapping(const Zstring& dirPostfixedLeft, bool dirExistsLeft, const Zstring& dirPostfixedRight, bool dirExistsRight, const HardFilter::FilterRef& filterIn) : HierarchyObject(Zstring(), *this), filter(filterIn), baseDirPfL(dirPostfixedLeft), baseDirPfR(dirPostfixedRight), dirExistsLeft_(dirExistsLeft), dirExistsRight_(dirExistsRight) {} template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR static void removeEmpty(BaseDirMapping& baseDir) { baseDir.removeEmptyRec(); }; //physically remove all invalid entries (where both sides are empty) recursively const HardFilter::FilterRef& getFilter() const; template bool wasExisting() const; //status of directory existence at the time of comparison! virtual void flip(); private: BaseDirMapping(const BaseDirMapping&); //this class is referenced by HierarchyObject => make it non-copyable/movable! BaseDirMapping& operator=(const BaseDirMapping&); // //this member is currently not used by the business logic -> may be removed! HardFilter::FilterRef filter; Zstring baseDirPfL; //base sync dir postfixed Zstring baseDirPfR; // bool dirExistsLeft_; bool dirExistsRight_; }; template <> inline const Zstring& BaseDirMapping::getBaseDirPf() const { return baseDirPfL; } template <> inline const Zstring& BaseDirMapping::getBaseDirPf() const { return baseDirPfR; } //get rid of shared_ptr indirection template < class IterTy, //underlying iterator type class U > //target object type class DerefIter : public std::iterator { public: DerefIter() {} DerefIter(IterTy it) : iter(it) {} DerefIter(const DerefIter& other) : iter(other.iter) {} DerefIter& operator++() { ++iter; return *this; } DerefIter& operator--() { --iter; return *this; } DerefIter operator++(int) { DerefIter tmp(*this); operator++(); return tmp; } DerefIter operator--(int) { DerefIter tmp(*this); operator--(); return tmp; } inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter - rhs.iter; } inline friend bool operator==(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter == rhs.iter; } inline friend bool operator!=(const DerefIter& lhs, const DerefIter& rhs) { return !(lhs == rhs); } U& operator* () { return **iter; } U* operator->() { return &** iter; } private: IterTy iter; }; typedef std::vector> FolderComparison; //make sure pointers to sub-elements remain valid //don't change this back to std::vector too easily: comparison uses push_back to add entries which may result in a full copy! DerefIter inline begin(FolderComparison& vect) { return vect.begin(); } DerefIter inline end (FolderComparison& vect) { return vect.end (); } DerefIter inline begin(const FolderComparison& vect) { return vect.begin(); } DerefIter inline end (const FolderComparison& vect) { return vect.end (); } //------------------------------------------------------------------ class FSObjectVisitor { public: virtual ~FSObjectVisitor() {} virtual void visit(const FileMapping& fileObj) = 0; virtual void visit(const SymLinkMapping& linkObj) = 0; virtual void visit(const DirMapping& dirObj) = 0; }; //inherit from this class to allow safe access by id instead of unsafe raw pointer //allow for similar semantics like std::weak_ptr without having to use std::shared_ptr template class ObjectMgr { public: typedef const ObjectMgr* ObjectID; ObjectID getId() { activeObjects().insert(this); return this; } //unfortunately we need to keep this method non-const to get non-const "this" pointer //we could instead put this into the constructor, but temporaries created by STL would lead to some overhead static T* retrieve(ObjectID id) //returns NULL if object is not found { auto iter = activeObjects().find(const_cast(id)); return static_cast(iter == activeObjects().end() ? NULL : *iter); //static down-cast } protected: ObjectMgr() {} ~ObjectMgr() { activeObjects().erase(this); } private: ObjectMgr(const ObjectMgr& rhs); // ObjectMgr& operator=(const ObjectMgr& rhs); //it's not well-defined what coping an objects means regarding object-identity in this context static std::unordered_set& activeObjects() { static std::unordered_set inst; //external linkage (even if in header file!) return inst; } }; //------------------------------------------------------------------ class FileSystemObject : public ObjectMgr { public: virtual void accept(FSObjectVisitor& visitor) const = 0; Zstring getObjShortName () const; //same as getShortName() but also returns value if either side is empty Zstring getObjRelativeName() const; //same as getRelativeName() but also returns value if either side is empty template bool isEmpty() const; template const Zstring& getShortName() const; template Zstring getRelativeName() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR prefix template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR template Zstring getFullName() const; //getFullName() == getBaseDirPf() + getRelativeName() //comparison result virtual CompareFilesResult getCategory() const = 0; virtual std::wstring getCatConflict() const = 0; //only filled if getCategory() == FILE_CONFLICT //sync operation virtual SyncOperation getSyncOperation() const; std::wstring getSyncOpConflict() const; //return conflict when determining sync direction or during categorization SyncOperation testSyncOperation(SyncDirection syncDir) const; //get syncOp with provided settings //sync settings void setSyncDir(SyncDirection newDir); void setSyncDirConflict(const std::wstring& description); //set syncDir = SYNC_DIR_NONE + fill conflict description bool isActive() const; void setActive(bool active); template void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion bool isEmpty() const; //true, if both sides are empty const HierarchyObject& parent() const { return parent_; } /**/ HierarchyObject& parent() { return parent_; } const BaseDirMapping& root() const { return parent_.getRoot(); } /**/ BaseDirMapping& root() { return parent_.getRoot(); } protected: FileSystemObject(const Zstring& shortNameLeft, const Zstring& shortNameRight, HierarchyObject& parentObj) : selectedForSynchronization(true), syncDir(SYNC_DIR_NONE), shortNameLeft_(shortNameLeft), shortNameRight_(shortNameRight), //shortNameRight_(shortNameRight == shortNameLeft ? shortNameLeft : shortNameRight), -> strangely doesn't seem to shrink peak memory consumption at all! parent_(parentObj) { parent_.notifySyncCfgChanged(); } ~FileSystemObject() {} //don't need polymorphic deletion //mustn't call parent here, it is already partially destroyed and nothing more than a pure HierarchyObject! virtual void flip(); virtual void notifySyncCfgChanged() { parent().notifySyncCfgChanged(); /*propagate!*/ } void copyToL(); void copyToR(); private: virtual void removeObjectL() = 0; virtual void removeObjectR() = 0; static SyncOperation getSyncOperation(CompareFilesResult cmpResult, bool selectedForSynchronization, SyncDirection syncDir, const std::wstring& syncDirConflict); //evaluate comparison result and sync direction bool selectedForSynchronization; SyncDirection syncDir; std::wstring syncDirConflict; //non-empty if we have a conflict setting sync-direction Zstring shortNameLeft_; //slightly redundant under linux, but on windows the "same" filenames can differ in case Zstring shortNameRight_; //use as indicator: an empty name means: not existing! HierarchyObject& parent_; }; //------------------------------------------------------------------ class DirMapping : public FileSystemObject, public HierarchyObject { friend class CompareProcess; //only CompareProcess shall be allowed to change cmpResult friend class HierarchyObject; public: virtual void accept(FSObjectVisitor& visitor) const; virtual CompareFilesResult getCategory() const; CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult virtual std::wstring getCatConflict() const; DirMapping(const Zstring& shortNameLeft, //use empty shortname if "not existing" const Zstring& shortNameRight, // HierarchyObject& parentObj) : FileSystemObject(shortNameLeft, shortNameRight, parentObj), HierarchyObject(getObjRelativeName() + FILE_NAME_SEPARATOR, parentObj.getRoot()), syncOpBuffered(SO_DO_NOTHING), syncOpUpToDate(false) { assert(!shortNameLeft.empty() || !shortNameRight.empty()); if (shortNameRight.empty()) cmpResult = DIR_LEFT_SIDE_ONLY; else if (shortNameLeft.empty()) cmpResult = DIR_RIGHT_SIDE_ONLY; else { if (shortNameLeft == shortNameRight) cmpResult = DIR_EQUAL; else cmpResult = DIR_DIFFERENT_METADATA; } } virtual SyncOperation getSyncOperation() const; template void copyTo(); //copy dir private: virtual void flip(); virtual void removeObjectL(); virtual void removeObjectR(); virtual void notifySyncCfgChanged() { syncOpUpToDate = false; FileSystemObject::notifySyncCfgChanged(); HierarchyObject::notifySyncCfgChanged(); } //------------------------------------------------------------------ //categorization CompareDirResult cmpResult; mutable SyncOperation syncOpBuffered; //determining sync-op for directory may be expensive as it depends on child-objects -> buffer it mutable bool syncOpUpToDate; // }; //------------------------------------------------------------------ class FileMapping : public FileSystemObject { friend class CompareProcess; //only CompareProcess shall be allowed to change cmpResult friend class HierarchyObject; //construction public: virtual void accept(FSObjectVisitor& visitor) const; FileMapping(const Zstring& shortNameLeft, //use empty string if "not existing" const FileDescriptor& left, CompareFilesResult defaultCmpResult, const Zstring& shortNameRight, // const FileDescriptor& right, HierarchyObject& parentObj) : FileSystemObject(shortNameLeft, shortNameRight, parentObj), cmpResult(defaultCmpResult), dataLeft(left), dataRight(right) {} template Int64 getLastWriteTime() const; template UInt64 getFileSize() const; template const Zstring getExtension() const; virtual CompareFilesResult getCategory() const; virtual std::wstring getCatConflict() const; template void copyTo(const FileDescriptor* srcDescr); //copy + update file attributes private: template void setCategory(); void setCategoryConflict(const std::wstring& description); virtual void flip(); virtual void removeObjectL(); virtual void removeObjectR(); //------------------------------------------------------------------ //categorization CompareFilesResult cmpResult; std::wstring cmpConflictDescr; //only filled if cmpResult == FILE_CONFLICT FileDescriptor dataLeft; FileDescriptor dataRight; }; //------------------------------------------------------------------ class SymLinkMapping : public FileSystemObject //this class models a TRUE symbolic link, i.e. one that is NEVER dereferenced: deref-links should be directly placed in class File/DirMapping { friend class CompareProcess; //only CompareProcess shall be allowed to change cmpResult friend class HierarchyObject; //construction public: virtual void accept(FSObjectVisitor& visitor) const; template zen::Int64 getLastWriteTime() const; //write time of the link, NOT target! template LinkDescriptor::LinkType getLinkType() const; template const Zstring& getTargetPath() const; virtual CompareFilesResult getCategory() const; CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFilesResult virtual std::wstring getCatConflict() const; SymLinkMapping(const Zstring& shortNameLeft, //use empty string if "not existing" const LinkDescriptor& left, CompareSymlinkResult defaultCmpResult, const Zstring& shortNameRight, //use empty string if "not existing" const LinkDescriptor& right, HierarchyObject& parentObj) : FileSystemObject(shortNameLeft, shortNameRight, parentObj), cmpResult(defaultCmpResult), dataLeft(left), dataRight(right) {} template void copyTo(); //copy private: virtual void flip(); virtual void removeObjectL(); virtual void removeObjectR(); template void setCategory(); void setCategoryConflict(const std::wstring& description); //------------------------------------------------------------------ //categorization CompareSymlinkResult cmpResult; std::wstring cmpConflictDescr; //only filled if cmpResult == SYMLINK_CONFLICT LinkDescriptor dataLeft; LinkDescriptor dataRight; }; //------------------------------------------------------------------ //---------------Inline Implementation--------------------------------------------------- inline //inline virtual... admittedly its use may be limited void FileMapping::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } inline void DirMapping::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } inline void SymLinkMapping::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } inline CompareFilesResult FileMapping::getCategory() const { return cmpResult; } inline std::wstring FileMapping::getCatConflict() const { return cmpConflictDescr; } inline CompareFilesResult DirMapping::getCategory() const { return convertToFilesResult(cmpResult); } inline CompareDirResult DirMapping::getDirCategory() const { return cmpResult; } inline std::wstring DirMapping::getCatConflict() const { return std::wstring(); } inline void FileSystemObject::setSyncDir(SyncDirection newDir) { syncDir = newDir; //should be safe by design syncDirConflict.clear(); notifySyncCfgChanged(); } inline void FileSystemObject::setSyncDirConflict(const std::wstring& description) { syncDir = SYNC_DIR_NONE; syncDirConflict = description; notifySyncCfgChanged(); } inline std::wstring FileSystemObject::getSyncOpConflict() const { return syncDirConflict; } inline bool FileSystemObject::isActive() const { return selectedForSynchronization; } inline void FileSystemObject::setActive(bool active) { selectedForSynchronization = active; notifySyncCfgChanged(); } inline SyncOperation FileSystemObject::getSyncOperation() const { return getSyncOperation(getCategory(), selectedForSynchronization, syncDir, syncDirConflict); } inline SyncOperation FileSystemObject::testSyncOperation(SyncDirection proposedDir) const { return getSyncOperation(getCategory(), true, proposedDir, std::wstring()); //should be safe by design } template <> inline bool FileSystemObject::isEmpty() const { return shortNameLeft_.empty(); } template <> inline bool FileSystemObject::isEmpty() const { return shortNameRight_.empty(); } inline bool FileSystemObject::isEmpty() const { return isEmpty() && isEmpty(); } template <> inline const Zstring& FileSystemObject::getShortName() const { return shortNameLeft_; //empty if not existing } template <> inline const Zstring& FileSystemObject::getShortName() const { return shortNameRight_; //empty if not existing } template inline Zstring FileSystemObject::getRelativeName() const { return isEmpty() ? Zstring() : parent_.getObjRelativeNamePf() + getShortName(); } inline Zstring FileSystemObject::getObjRelativeName() const { return parent_.getObjRelativeNamePf() + getObjShortName(); } inline Zstring FileSystemObject::getObjShortName() const { return isEmpty() ? getShortName() : getShortName(); } template inline Zstring FileSystemObject::getFullName() const { return isEmpty() ? Zstring() : getBaseDirPf() + parent_.getObjRelativeNamePf() + getShortName(); } template <> inline const Zstring& FileSystemObject::getBaseDirPf() const { return root().getBaseDirPf(); } template <> inline const Zstring& FileSystemObject::getBaseDirPf() const { return root().getBaseDirPf(); } template <> inline void FileSystemObject::removeObject() { shortNameLeft_.clear(); removeObjectL(); setSyncDir(SYNC_DIR_NONE); //calls notifySyncCfgChanged() } template <> inline void FileSystemObject::removeObject() { shortNameRight_.clear(); removeObjectR(); setSyncDir(SYNC_DIR_NONE); //calls notifySyncCfgChanged() } inline void FileSystemObject::copyToL() { assert(!isEmpty()); shortNameLeft_ = shortNameRight_; setSyncDir(SYNC_DIR_NONE); } inline void FileSystemObject::copyToR() { assert(!isEmpty()); shortNameRight_ = shortNameLeft_; setSyncDir(SYNC_DIR_NONE); } inline void FileSystemObject::flip() { std::swap(shortNameLeft_, shortNameRight_); notifySyncCfgChanged(); } inline void HierarchyObject::flip() { std::for_each(refSubFiles().begin(), refSubFiles().end(), std::mem_fun_ref(&FileMapping ::flip)); std::for_each(refSubDirs ().begin(), refSubDirs ().end(), std::mem_fun_ref(&DirMapping ::flip)); std::for_each(refSubLinks().begin(), refSubLinks().end(), std::mem_fun_ref(&SymLinkMapping::flip)); } inline DirMapping& HierarchyObject::addSubDir(const Zstring& shortNameLeft, const Zstring& shortNameRight) { subDirs.emplace_back(shortNameLeft, shortNameRight, *this); return subDirs.back(); } inline FileMapping& HierarchyObject::addSubFile( const Zstring& shortNameLeft, const FileDescriptor& left, //file exists on both sides CompareFilesResult defaultCmpResult, const Zstring& shortNameRight, const FileDescriptor& right) { subFiles.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); return subFiles.back(); } inline void HierarchyObject::addSubFile(const FileDescriptor& left, //file exists on left side only const Zstring& shortNameLeft) { subFiles.emplace_back(shortNameLeft, left, FILE_LEFT_SIDE_ONLY, Zstring(), FileDescriptor(), *this); } inline void HierarchyObject::addSubFile(const Zstring& shortNameRight, //file exists on right side only const FileDescriptor& right) { subFiles.emplace_back(Zstring(), FileDescriptor(), FILE_RIGHT_SIDE_ONLY, shortNameRight, right, *this); } inline SymLinkMapping& HierarchyObject::addSubLink( const Zstring& shortNameLeft, const LinkDescriptor& left, //link exists on both sides CompareSymlinkResult defaultCmpResult, const Zstring& shortNameRight, const LinkDescriptor& right) { subLinks.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); return subLinks.back(); } inline void HierarchyObject::addSubLink(const LinkDescriptor& left, //link exists on left side only const Zstring& shortNameLeft) { subLinks.emplace_back(shortNameLeft, left, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkDescriptor(), *this); } inline void HierarchyObject::addSubLink(const Zstring& shortNameRight, //link exists on right side only const LinkDescriptor& right) { subLinks.emplace_back(Zstring(), LinkDescriptor(), SYMLINK_RIGHT_SIDE_ONLY, shortNameRight, right, *this); } inline void BaseDirMapping::flip() { HierarchyObject::flip(); std::swap(baseDirPfL, baseDirPfR); } inline void DirMapping::flip() { HierarchyObject ::flip(); //call base class versions FileSystemObject::flip(); // //swap compare result switch (cmpResult) { case DIR_LEFT_SIDE_ONLY: cmpResult = DIR_RIGHT_SIDE_ONLY; break; case DIR_RIGHT_SIDE_ONLY: cmpResult = DIR_LEFT_SIDE_ONLY; break; case DIR_EQUAL: case DIR_DIFFERENT_METADATA: break; } } inline void DirMapping::removeObjectL() { cmpResult = isEmpty() ? DIR_EQUAL : DIR_RIGHT_SIDE_ONLY; std::for_each(refSubFiles().begin(), refSubFiles().end(), std::mem_fun_ref(&FileSystemObject::removeObject)); std::for_each(refSubLinks().begin(), refSubLinks().end(), std::mem_fun_ref(&FileSystemObject::removeObject)); std::for_each(refSubDirs(). begin(), refSubDirs() .end(), std::mem_fun_ref(&FileSystemObject::removeObject)); } inline void DirMapping::removeObjectR() { cmpResult = isEmpty() ? DIR_EQUAL : DIR_LEFT_SIDE_ONLY; std::for_each(refSubFiles().begin(), refSubFiles().end(), std::mem_fun_ref(&FileSystemObject::removeObject)); std::for_each(refSubLinks().begin(), refSubLinks().end(), std::mem_fun_ref(&FileSystemObject::removeObject)); std::for_each(refSubDirs(). begin(), refSubDirs(). end(), std::mem_fun_ref(&FileSystemObject::removeObject)); } inline const HardFilter::FilterRef& BaseDirMapping::getFilter() const { return filter; } template <> inline bool BaseDirMapping::wasExisting() const { return dirExistsLeft_; } template <> inline bool BaseDirMapping::wasExisting() const { return dirExistsRight_; } inline void FileMapping::flip() { FileSystemObject::flip(); //call base class version //swap compare result switch (cmpResult) { case FILE_LEFT_SIDE_ONLY: cmpResult = FILE_RIGHT_SIDE_ONLY; break; case FILE_RIGHT_SIDE_ONLY: cmpResult = FILE_LEFT_SIDE_ONLY; break; case FILE_LEFT_NEWER: cmpResult = FILE_RIGHT_NEWER; break; case FILE_RIGHT_NEWER: cmpResult = FILE_LEFT_NEWER; break; case FILE_DIFFERENT: case FILE_EQUAL: case FILE_DIFFERENT_METADATA: case FILE_CONFLICT: break; } std::swap(dataLeft, dataRight); } template inline void FileMapping::setCategory() { cmpResult = res; } template <> inline void FileMapping::setCategory(); //if conflict is detected, use setCategoryConflict! => method is not defined! inline void FileMapping::setCategoryConflict(const std::wstring& description) { cmpResult = FILE_CONFLICT; cmpConflictDescr = description; } inline void FileMapping::removeObjectL() { cmpResult = isEmpty() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY; dataLeft = FileDescriptor(); } inline void FileMapping::removeObjectR() { cmpResult = isEmpty() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY; dataRight = FileDescriptor(); } template <> inline zen::Int64 FileMapping::getLastWriteTime() const { return dataLeft.lastWriteTimeRaw; } template <> inline zen::Int64 FileMapping::getLastWriteTime() const { return dataRight.lastWriteTimeRaw; } template <> inline zen::UInt64 FileMapping::getFileSize() const { return dataLeft.fileSize; } template <> inline zen::UInt64 FileMapping::getFileSize() const { return dataRight.fileSize; } template inline const Zstring FileMapping::getExtension() const { //attention: Zstring::AfterLast() returns whole string if char not found! -> don't use const Zstring& shortName = getShortName(); const size_t pos = shortName.rfind(Zchar('.')); return pos == Zstring::npos ? Zstring() : Zstring(shortName.c_str() + pos + 1); } template <> inline void FileMapping::copyTo(const FileDescriptor* srcDescr) //copy + update file attributes { if (srcDescr) dataRight = *srcDescr; dataLeft = dataRight; cmpResult = FILE_EQUAL; copyToL(); //copy FileSystemObject specific part } template <> inline void FileMapping::copyTo(const FileDescriptor* srcDescr) //copy + update file attributes { if (srcDescr) dataLeft = *srcDescr; dataRight = dataLeft; cmpResult = FILE_EQUAL; copyToR(); //copy FileSystemObject specific part } template <> inline void SymLinkMapping::copyTo() //copy + update link attributes { dataLeft = dataRight; cmpResult = SYMLINK_EQUAL; copyToL(); //copy FileSystemObject specific part } template <> inline void SymLinkMapping::copyTo() //copy + update link attributes { dataRight = dataLeft; cmpResult = SYMLINK_EQUAL; copyToR(); //copy FileSystemObject specific part } template <> inline void DirMapping::copyTo() { cmpResult = DIR_EQUAL; copyToL(); //copy FileSystemObject specific part } template <> inline void DirMapping::copyTo() { cmpResult = DIR_EQUAL; copyToR(); //copy FileSystemObject specific part } template <> inline zen::Int64 SymLinkMapping::getLastWriteTime() const { return dataLeft.lastWriteTimeRaw; } template <> inline zen::Int64 SymLinkMapping::getLastWriteTime() const { return dataRight.lastWriteTimeRaw; } template <> inline LinkDescriptor::LinkType SymLinkMapping::getLinkType() const { return dataLeft.type; } template <> inline LinkDescriptor::LinkType SymLinkMapping::getLinkType() const { return dataRight.type; } template <> inline const Zstring& SymLinkMapping::getTargetPath() const { return dataLeft.targetPath; } template <> inline const Zstring& SymLinkMapping::getTargetPath() const { return dataRight.targetPath; } inline CompareFilesResult SymLinkMapping::getCategory() const { return convertToFilesResult(cmpResult); } inline CompareSymlinkResult SymLinkMapping::getLinkCategory() const { return cmpResult; } inline std::wstring SymLinkMapping::getCatConflict() const { return cmpConflictDescr; } inline void SymLinkMapping::flip() { FileSystemObject::flip(); //call base class versions switch (cmpResult) { case SYMLINK_LEFT_SIDE_ONLY: cmpResult = SYMLINK_RIGHT_SIDE_ONLY; break; case SYMLINK_RIGHT_SIDE_ONLY: cmpResult = SYMLINK_LEFT_SIDE_ONLY; break; case SYMLINK_LEFT_NEWER: cmpResult = SYMLINK_RIGHT_NEWER; break; case SYMLINK_RIGHT_NEWER: cmpResult = SYMLINK_LEFT_NEWER; break; case SYMLINK_EQUAL: case SYMLINK_DIFFERENT_METADATA: case SYMLINK_DIFFERENT: case SYMLINK_CONFLICT: break; } std::swap(dataLeft, dataRight); } inline void SymLinkMapping::removeObjectL() { cmpResult = isEmpty() ? SYMLINK_EQUAL : SYMLINK_RIGHT_SIDE_ONLY; dataLeft = LinkDescriptor(); } inline void SymLinkMapping::removeObjectR() { cmpResult = isEmpty() ? SYMLINK_EQUAL : SYMLINK_LEFT_SIDE_ONLY; dataRight = LinkDescriptor(); } template inline void SymLinkMapping::setCategory() { cmpResult = res; } template <> void SymLinkMapping::setCategory(); //if conflict is detected, use setCategoryConflict! => method is not defined! inline void SymLinkMapping::setCategoryConflict(const std::wstring& description) { cmpResult = SYMLINK_CONFLICT; cmpConflictDescr = description; } } #endif // FILEHIERARCHY_H_INCLUDED