1c540156cSNicolas Bonnefon #ifndef WATCHTOWERLIST_H 2c540156cSNicolas Bonnefon #define WATCHTOWERLIST_H 3c540156cSNicolas Bonnefon 4c540156cSNicolas Bonnefon // Utility classes for the WatchTower implementations 5c540156cSNicolas Bonnefon 6c540156cSNicolas Bonnefon #include <functional> 7c540156cSNicolas Bonnefon #include <string> 8c540156cSNicolas Bonnefon #include <vector> 9c540156cSNicolas Bonnefon #include <map> 10c540156cSNicolas Bonnefon #include <list> 11c540156cSNicolas Bonnefon #include <memory> 12c540156cSNicolas Bonnefon #include <algorithm> 13*fcaa7557SNicolas Bonnefon #include <chrono> 14c540156cSNicolas Bonnefon 15f09fa651SNicolas Bonnefon #include "log.h" 16b278d183SNicolas Bonnefon 17c540156cSNicolas Bonnefon // Utility classes 18c540156cSNicolas Bonnefon struct ProtocolInfo { 19c540156cSNicolas Bonnefon // Win32 notification variables 20c540156cSNicolas Bonnefon static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096; 21c540156cSNicolas Bonnefon 22c540156cSNicolas Bonnefon void* handle_; 23c540156cSNicolas Bonnefon static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE; 24c540156cSNicolas Bonnefon char buffer_[buffer_length_]; 25c540156cSNicolas Bonnefon }; 26c540156cSNicolas Bonnefon 27c540156cSNicolas Bonnefon // List of files and observers 28f09fa651SNicolas Bonnefon template <typename Driver> 29c540156cSNicolas Bonnefon struct ObservedDir { 30c540156cSNicolas Bonnefon ObservedDir( const std::string this_path ) : path { this_path } {} 31c540156cSNicolas Bonnefon 32c540156cSNicolas Bonnefon // Returns the address of the protocol specific informations 33c540156cSNicolas Bonnefon ProtocolInfo* protocolInfo() { return &protocol_info_; } 34c540156cSNicolas Bonnefon 35c540156cSNicolas Bonnefon std::string path; 36f09fa651SNicolas Bonnefon typename Driver::DirId dir_id_; 37c540156cSNicolas Bonnefon // Contains data specific to the protocol (inotify/Win32...) 38c540156cSNicolas Bonnefon ProtocolInfo protocol_info_; 39c540156cSNicolas Bonnefon }; 40c540156cSNicolas Bonnefon 41f09fa651SNicolas Bonnefon template <typename Driver> 42c540156cSNicolas Bonnefon struct ObservedFile { 43c540156cSNicolas Bonnefon ObservedFile( 44c540156cSNicolas Bonnefon const std::string& file_name, 45c540156cSNicolas Bonnefon std::shared_ptr<void> callback, 46f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 47f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ) 48b278d183SNicolas Bonnefon : file_name_( file_name ) { 49c540156cSNicolas Bonnefon addCallback( callback ); 50c540156cSNicolas Bonnefon 51b278d183SNicolas Bonnefon file_id_ = file_id; 52b278d183SNicolas Bonnefon symlink_id_ = symlink_id; 53c540156cSNicolas Bonnefon dir_ = nullptr; 54*fcaa7557SNicolas Bonnefon 55*fcaa7557SNicolas Bonnefon markAsChanged(); 56c540156cSNicolas Bonnefon } 57c540156cSNicolas Bonnefon 58c540156cSNicolas Bonnefon void addCallback( std::shared_ptr<void> callback ) { 59c540156cSNicolas Bonnefon callbacks.push_back( callback ); 60c540156cSNicolas Bonnefon } 61c540156cSNicolas Bonnefon 62*fcaa7557SNicolas Bonnefon // Records the file has changed 63*fcaa7557SNicolas Bonnefon void markAsChanged() { 64*fcaa7557SNicolas Bonnefon change_token_.readFromFile( file_name_ ); 65*fcaa7557SNicolas Bonnefon last_check_time_ = std::chrono::steady_clock::now(); 66*fcaa7557SNicolas Bonnefon } 67*fcaa7557SNicolas Bonnefon 68*fcaa7557SNicolas Bonnefon // Returns whether a file has changed 69*fcaa7557SNicolas Bonnefon // (for polling) 70*fcaa7557SNicolas Bonnefon bool hasChanged() { 71*fcaa7557SNicolas Bonnefon typename Driver::FileChangeToken new_token( file_name_ ); 72*fcaa7557SNicolas Bonnefon last_check_time_ = std::chrono::steady_clock::now(); 73*fcaa7557SNicolas Bonnefon return change_token_ != new_token; 74*fcaa7557SNicolas Bonnefon } 75*fcaa7557SNicolas Bonnefon 76*fcaa7557SNicolas Bonnefon std::chrono::steady_clock::time_point timeForLastCheck() { 77*fcaa7557SNicolas Bonnefon return last_check_time_; 78*fcaa7557SNicolas Bonnefon } 79*fcaa7557SNicolas Bonnefon 80c540156cSNicolas Bonnefon std::string file_name_; 81c540156cSNicolas Bonnefon // List of callbacks for this file 82c540156cSNicolas Bonnefon std::vector<std::shared_ptr<void>> callbacks; 83c540156cSNicolas Bonnefon 84c540156cSNicolas Bonnefon // watch descriptor for the file itself 85f09fa651SNicolas Bonnefon typename Driver::FileId file_id_; 86c540156cSNicolas Bonnefon // watch descriptor for the symlink (if file is a symlink) 87f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id_; 88c540156cSNicolas Bonnefon 89c540156cSNicolas Bonnefon // link to the dir containing the file 90f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir_; 91*fcaa7557SNicolas Bonnefon 92*fcaa7557SNicolas Bonnefon // token to identify modification 93*fcaa7557SNicolas Bonnefon // (the token change when the file is modified, this is used when polling) 94*fcaa7557SNicolas Bonnefon typename Driver::FileChangeToken change_token_; 95*fcaa7557SNicolas Bonnefon 96*fcaa7557SNicolas Bonnefon // Last time a check has been done 97*fcaa7557SNicolas Bonnefon std::chrono::steady_clock::time_point last_check_time_ = 98*fcaa7557SNicolas Bonnefon std::chrono::steady_clock::time_point::min(); 99c540156cSNicolas Bonnefon }; 100c540156cSNicolas Bonnefon 101c540156cSNicolas Bonnefon // A list of the observed files and directories 102c540156cSNicolas Bonnefon // This class is not thread safe 103f09fa651SNicolas Bonnefon template<typename Driver> 104c540156cSNicolas Bonnefon class ObservedFileList { 105c540156cSNicolas Bonnefon public: 1063104b268SNicolas Bonnefon ObservedFileList() : 1073104b268SNicolas Bonnefon heartBeat_ { std::shared_ptr<void>((void*) 0xDEADC0DE, [] (void*) {}) } 1083104b268SNicolas Bonnefon { } 109c540156cSNicolas Bonnefon ~ObservedFileList() = default; 110c540156cSNicolas Bonnefon 1113104b268SNicolas Bonnefon // The functions return a pointer to the existing file (if exists) 1123104b268SNicolas Bonnefon // but keep ownership of the object. 113f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByName( const std::string& file_name ); 114f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByFileOrSymlinkWd( 115f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 116f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ); 117f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByDirWdAndName( 118f09fa651SNicolas Bonnefon typename Driver::DirId id, const char* name ); 119c540156cSNicolas Bonnefon 1203104b268SNicolas Bonnefon // Add a new file, the list returns a pointer to the added file, 1213104b268SNicolas Bonnefon // but has ownership of the file. 122f09fa651SNicolas Bonnefon ObservedFile<Driver>* addNewObservedFile( ObservedFile<Driver> new_observed ); 123c540156cSNicolas Bonnefon // Remove a callback, remove and returns the file object if 124c540156cSNicolas Bonnefon // it was the last callback on this object, nullptr if not. 125c540156cSNicolas Bonnefon // The caller has ownership of the object. 126f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> removeCallback( 127c540156cSNicolas Bonnefon std::shared_ptr<void> callback ); 128c540156cSNicolas Bonnefon 129c540156cSNicolas Bonnefon // Return the watched directory if it is watched, or nullptr 130f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> watchedDirectory( const std::string& dir_name ); 1313104b268SNicolas Bonnefon // Create a new watched directory for dir_name, the client is passed 1323104b268SNicolas Bonnefon // shared ownership and have to keep the shared_ptr (the list only 1333104b268SNicolas Bonnefon // maintain a weak link). 1343104b268SNicolas Bonnefon // The remove notification is called just before the reference to 1353104b268SNicolas Bonnefon // the directory is destroyed. 1363104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> addWatchedDirectory( 1373104b268SNicolas Bonnefon const std::string& dir_name, 1383104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ); 139c540156cSNicolas Bonnefon 1403104b268SNicolas Bonnefon // Similar to previous functions but extract the name of the 1413104b268SNicolas Bonnefon // directory from the file name. 142f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> watchedDirectoryForFile( const std::string& file_name ); 1433104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> addWatchedDirectoryForFile( const std::string& file_name, 1443104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ); 1453104b268SNicolas Bonnefon 1463104b268SNicolas Bonnefon // Removal of directories is done when there is no shared reference 1473104b268SNicolas Bonnefon // left (RAII) 1483104b268SNicolas Bonnefon 1493104b268SNicolas Bonnefon // Number of watched directories (for tests) 1503104b268SNicolas Bonnefon unsigned int numberWatchedDirectories() const; 151c540156cSNicolas Bonnefon 152*fcaa7557SNicolas Bonnefon // Iterator 153*fcaa7557SNicolas Bonnefon template<typename Container> 154*fcaa7557SNicolas Bonnefon class iterator : std::iterator<std::input_iterator_tag, ObservedFile<Driver>> { 155*fcaa7557SNicolas Bonnefon public: 156*fcaa7557SNicolas Bonnefon iterator( Container* list, 157*fcaa7557SNicolas Bonnefon const typename Container::iterator& iter ) 158*fcaa7557SNicolas Bonnefon { list_ = list; pos_ = iter; } 159*fcaa7557SNicolas Bonnefon 160*fcaa7557SNicolas Bonnefon iterator operator++() 161*fcaa7557SNicolas Bonnefon { ++pos_; return *this; } 162*fcaa7557SNicolas Bonnefon 163*fcaa7557SNicolas Bonnefon bool operator==( const iterator& other ) 164*fcaa7557SNicolas Bonnefon { return ( pos_ == other.pos_ ); } 165*fcaa7557SNicolas Bonnefon 166*fcaa7557SNicolas Bonnefon bool operator!=( const iterator& other ) 167*fcaa7557SNicolas Bonnefon { return ! operator==( other ); } 168*fcaa7557SNicolas Bonnefon 169*fcaa7557SNicolas Bonnefon typename Container::iterator operator*() 170*fcaa7557SNicolas Bonnefon { return pos_; } 171*fcaa7557SNicolas Bonnefon 172*fcaa7557SNicolas Bonnefon private: 173*fcaa7557SNicolas Bonnefon Container* list_; 174*fcaa7557SNicolas Bonnefon typename Container::iterator pos_; 175*fcaa7557SNicolas Bonnefon }; 176*fcaa7557SNicolas Bonnefon 177*fcaa7557SNicolas Bonnefon iterator<std::list<ObservedFile<Driver>>> begin() 178*fcaa7557SNicolas Bonnefon { return iterator<std::list<ObservedFile<Driver>>>( &observed_files_, observed_files_.begin() ); } 179*fcaa7557SNicolas Bonnefon iterator<std::list<ObservedFile<Driver>>> end() 180*fcaa7557SNicolas Bonnefon { return iterator<std::list<ObservedFile<Driver>>>( &observed_files_, observed_files_.end() ); } 181*fcaa7557SNicolas Bonnefon 182c540156cSNicolas Bonnefon private: 183c540156cSNicolas Bonnefon // List of observed files 184f09fa651SNicolas Bonnefon std::list<ObservedFile<Driver>> observed_files_; 185c540156cSNicolas Bonnefon 186c540156cSNicolas Bonnefon // List of observed dirs, key-ed by name 187f09fa651SNicolas Bonnefon std::map<std::string, std::weak_ptr<ObservedDir<Driver>>> observed_dirs_; 188c540156cSNicolas Bonnefon 189c540156cSNicolas Bonnefon // Map the inotify file (including symlinks) wds to the observed file 190f09fa651SNicolas Bonnefon std::map<int, ObservedFile<Driver>*> by_file_wd_; 191c540156cSNicolas Bonnefon // Map the inotify directory wds to the observed files 192f09fa651SNicolas Bonnefon std::map<int, ObservedFile<Driver>*> by_dir_wd_; 1933104b268SNicolas Bonnefon 1943104b268SNicolas Bonnefon // Heartbeat 1953104b268SNicolas Bonnefon std::shared_ptr<void> heartBeat_; 1963104b268SNicolas Bonnefon 1973104b268SNicolas Bonnefon // Clean all reference to any expired directory 1983104b268SNicolas Bonnefon void cleanRefsToExpiredDirs(); 199c540156cSNicolas Bonnefon }; 200c540156cSNicolas Bonnefon 201f09fa651SNicolas Bonnefon namespace { 202f09fa651SNicolas Bonnefon std::string directory_path( const std::string& path ); 203f09fa651SNicolas Bonnefon }; 204f09fa651SNicolas Bonnefon 205f09fa651SNicolas Bonnefon // ObservedFileList class 206f09fa651SNicolas Bonnefon template <typename Driver> 207f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByName( 208f09fa651SNicolas Bonnefon const std::string& file_name ) 209f09fa651SNicolas Bonnefon { 210f09fa651SNicolas Bonnefon // Look for an existing observer on this file 211f09fa651SNicolas Bonnefon auto existing_observer = observed_files_.begin(); 212f09fa651SNicolas Bonnefon for ( ; existing_observer != observed_files_.end(); ++existing_observer ) 213f09fa651SNicolas Bonnefon { 214f09fa651SNicolas Bonnefon if ( existing_observer->file_name_ == file_name ) 215f09fa651SNicolas Bonnefon { 216f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Found " << file_name; 217f09fa651SNicolas Bonnefon break; 218f09fa651SNicolas Bonnefon } 219f09fa651SNicolas Bonnefon } 220f09fa651SNicolas Bonnefon 221f09fa651SNicolas Bonnefon if ( existing_observer != observed_files_.end() ) 222f09fa651SNicolas Bonnefon return &( *existing_observer ); 223f09fa651SNicolas Bonnefon else 224f09fa651SNicolas Bonnefon return nullptr; 225f09fa651SNicolas Bonnefon } 226f09fa651SNicolas Bonnefon 227f09fa651SNicolas Bonnefon template <typename Driver> 228f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByFileOrSymlinkWd( 229f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 230f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ) 231f09fa651SNicolas Bonnefon { 232f09fa651SNicolas Bonnefon auto result = find_if( observed_files_.begin(), observed_files_.end(), 233f09fa651SNicolas Bonnefon [file_id, symlink_id] (ObservedFile<Driver> file) -> bool { 234f09fa651SNicolas Bonnefon return ( file_id == file.file_id_ ) || 235f09fa651SNicolas Bonnefon ( symlink_id == file.symlink_id_ ); 236f09fa651SNicolas Bonnefon } ); 237f09fa651SNicolas Bonnefon 238f09fa651SNicolas Bonnefon if ( result != observed_files_.end() ) 239f09fa651SNicolas Bonnefon return &( *result ); 240f09fa651SNicolas Bonnefon else 241f09fa651SNicolas Bonnefon return nullptr; 242f09fa651SNicolas Bonnefon } 243f09fa651SNicolas Bonnefon 244f09fa651SNicolas Bonnefon template <typename Driver> 245f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByDirWdAndName( 246f09fa651SNicolas Bonnefon typename Driver::DirId id, const char* name ) 247f09fa651SNicolas Bonnefon { 248f09fa651SNicolas Bonnefon auto dir = find_if( observed_dirs_.begin(), observed_dirs_.end(), 249f09fa651SNicolas Bonnefon [id] (std::pair<std::string,std::weak_ptr<ObservedDir<Driver>>> d) -> bool { 250f09fa651SNicolas Bonnefon if ( auto dir = d.second.lock() ) { 251f09fa651SNicolas Bonnefon return ( id == dir->dir_id_ ); 252f09fa651SNicolas Bonnefon } 253f09fa651SNicolas Bonnefon else { 254f09fa651SNicolas Bonnefon return false; } } ); 255f09fa651SNicolas Bonnefon 256f09fa651SNicolas Bonnefon if ( dir != observed_dirs_.end() ) { 257f09fa651SNicolas Bonnefon std::string path = dir->first + "/" + name; 258f09fa651SNicolas Bonnefon 259f09fa651SNicolas Bonnefon // LOG(logDEBUG) << "Testing path: " << path; 260f09fa651SNicolas Bonnefon 261f09fa651SNicolas Bonnefon // Looking for the path in the files we are watching 262f09fa651SNicolas Bonnefon return searchByName( path ); 263f09fa651SNicolas Bonnefon } 264f09fa651SNicolas Bonnefon else { 265f09fa651SNicolas Bonnefon return nullptr; 266f09fa651SNicolas Bonnefon } 267f09fa651SNicolas Bonnefon } 268f09fa651SNicolas Bonnefon 269f09fa651SNicolas Bonnefon template <typename Driver> 270f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::addNewObservedFile( 271f09fa651SNicolas Bonnefon ObservedFile<Driver> new_observed ) 272f09fa651SNicolas Bonnefon { 273f09fa651SNicolas Bonnefon auto new_file = observed_files_.insert( std::begin( observed_files_ ), new_observed ); 274f09fa651SNicolas Bonnefon 275f09fa651SNicolas Bonnefon return &( *new_file ); 276f09fa651SNicolas Bonnefon } 277f09fa651SNicolas Bonnefon 278f09fa651SNicolas Bonnefon template <typename Driver> 279f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> ObservedFileList<Driver>::removeCallback( 280f09fa651SNicolas Bonnefon std::shared_ptr<void> callback ) 281f09fa651SNicolas Bonnefon { 282f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> returned_file = nullptr; 283f09fa651SNicolas Bonnefon 284*fcaa7557SNicolas Bonnefon for ( auto observer = std::begin( observed_files_ ); 285*fcaa7557SNicolas Bonnefon observer != std::end( observed_files_ ); ) 286f09fa651SNicolas Bonnefon { 287f09fa651SNicolas Bonnefon std::vector<std::shared_ptr<void>>& callbacks = observer->callbacks; 288f09fa651SNicolas Bonnefon callbacks.erase( std::remove( 289f09fa651SNicolas Bonnefon std::begin( callbacks ), std::end( callbacks ), callback ), 290f09fa651SNicolas Bonnefon std::end( callbacks ) ); 291f09fa651SNicolas Bonnefon 292f09fa651SNicolas Bonnefon /* See if all notifications have been deleted for this file */ 293f09fa651SNicolas Bonnefon if ( callbacks.empty() ) { 2943104b268SNicolas Bonnefon LOG(logDEBUG) << "Empty notification list for " << observer->file_name_ 2953104b268SNicolas Bonnefon << ", removing the watched file"; 296f09fa651SNicolas Bonnefon returned_file = std::make_shared<ObservedFile<Driver>>( *observer ); 297f09fa651SNicolas Bonnefon observer = observed_files_.erase( observer ); 298f09fa651SNicolas Bonnefon } 299f09fa651SNicolas Bonnefon else { 300f09fa651SNicolas Bonnefon ++observer; 301f09fa651SNicolas Bonnefon } 302f09fa651SNicolas Bonnefon } 303f09fa651SNicolas Bonnefon 304f09fa651SNicolas Bonnefon return returned_file; 305f09fa651SNicolas Bonnefon } 306f09fa651SNicolas Bonnefon 307f09fa651SNicolas Bonnefon template <typename Driver> 308f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectory( 309f09fa651SNicolas Bonnefon const std::string& dir_name ) 310f09fa651SNicolas Bonnefon { 311f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir = nullptr; 312f09fa651SNicolas Bonnefon 313f09fa651SNicolas Bonnefon if ( observed_dirs_.find( dir_name ) != std::end( observed_dirs_ ) ) 314f09fa651SNicolas Bonnefon dir = observed_dirs_[ dir_name ].lock(); 315f09fa651SNicolas Bonnefon 316f09fa651SNicolas Bonnefon return dir; 317f09fa651SNicolas Bonnefon } 318f09fa651SNicolas Bonnefon 319f09fa651SNicolas Bonnefon template <typename Driver> 320f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectory( 3213104b268SNicolas Bonnefon const std::string& dir_name, 3223104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ) 323f09fa651SNicolas Bonnefon { 3243104b268SNicolas Bonnefon std::weak_ptr<void> weakHeartBeat(heartBeat_); 3253104b268SNicolas Bonnefon 3263104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir = { 3273104b268SNicolas Bonnefon new ObservedDir<Driver>( dir_name ), 3283104b268SNicolas Bonnefon [this, remove_notification, weakHeartBeat] (ObservedDir<Driver>* d) { 3293104b268SNicolas Bonnefon if ( auto heart_beat = weakHeartBeat.lock() ) { 3303104b268SNicolas Bonnefon remove_notification( d ); 331dfb2a39cSNicolas Bonnefon this->cleanRefsToExpiredDirs(); 3323104b268SNicolas Bonnefon } 3333104b268SNicolas Bonnefon delete d; } }; 334f09fa651SNicolas Bonnefon 335f09fa651SNicolas Bonnefon observed_dirs_[ dir_name ] = std::weak_ptr<ObservedDir<Driver>>( dir ); 336f09fa651SNicolas Bonnefon 337f09fa651SNicolas Bonnefon return dir; 338f09fa651SNicolas Bonnefon } 339f09fa651SNicolas Bonnefon 340f09fa651SNicolas Bonnefon template <typename Driver> 341f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectoryForFile( 342f09fa651SNicolas Bonnefon const std::string& file_name ) 343f09fa651SNicolas Bonnefon { 344f09fa651SNicolas Bonnefon return watchedDirectory( directory_path( file_name ) ); 345f09fa651SNicolas Bonnefon } 346f09fa651SNicolas Bonnefon 347f09fa651SNicolas Bonnefon template <typename Driver> 348f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectoryForFile( 3493104b268SNicolas Bonnefon const std::string& file_name, 3503104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ) 351f09fa651SNicolas Bonnefon { 3523104b268SNicolas Bonnefon return addWatchedDirectory( directory_path( file_name ), 3533104b268SNicolas Bonnefon remove_notification ); 3543104b268SNicolas Bonnefon } 3553104b268SNicolas Bonnefon 3563104b268SNicolas Bonnefon template <typename Driver> 3573104b268SNicolas Bonnefon unsigned int ObservedFileList<Driver>::numberWatchedDirectories() const 3583104b268SNicolas Bonnefon { 3593104b268SNicolas Bonnefon return observed_dirs_.size(); 3603104b268SNicolas Bonnefon } 3613104b268SNicolas Bonnefon 3623104b268SNicolas Bonnefon // Private functions 3633104b268SNicolas Bonnefon template <typename Driver> 3643104b268SNicolas Bonnefon void ObservedFileList<Driver>::cleanRefsToExpiredDirs() 3653104b268SNicolas Bonnefon { 3663104b268SNicolas Bonnefon for ( auto it = std::begin( observed_dirs_ ); 3673104b268SNicolas Bonnefon it != std::end( observed_dirs_ ); ) 3683104b268SNicolas Bonnefon { 3693104b268SNicolas Bonnefon if ( it->second.expired() ) { 3703104b268SNicolas Bonnefon it = observed_dirs_.erase( it ); 3713104b268SNicolas Bonnefon } 3723104b268SNicolas Bonnefon else { 3733104b268SNicolas Bonnefon ++it; 3743104b268SNicolas Bonnefon } 3753104b268SNicolas Bonnefon } 376f09fa651SNicolas Bonnefon } 377f09fa651SNicolas Bonnefon 378f09fa651SNicolas Bonnefon namespace { 379f09fa651SNicolas Bonnefon std::string directory_path( const std::string& path ) 380f09fa651SNicolas Bonnefon { 381f09fa651SNicolas Bonnefon size_t slash_pos = path.rfind( '/' ); 382f09fa651SNicolas Bonnefon 383f09fa651SNicolas Bonnefon #ifdef _WIN32 384f09fa651SNicolas Bonnefon if ( slash_pos == std::string::npos ) { 385f09fa651SNicolas Bonnefon slash_pos = path.rfind( '\\' ); 386f09fa651SNicolas Bonnefon } 387f09fa651SNicolas Bonnefon 388f09fa651SNicolas Bonnefon // We need to include the final slash on Windows 389f09fa651SNicolas Bonnefon ++slash_pos; 390f09fa651SNicolas Bonnefon #endif 391f09fa651SNicolas Bonnefon 392f09fa651SNicolas Bonnefon return std::string( path, 0, slash_pos ); 393f09fa651SNicolas Bonnefon } 394f09fa651SNicolas Bonnefon }; 395c540156cSNicolas Bonnefon #endif 396