389 lines
10 KiB
C++
389 lines
10 KiB
C++
#include <efsw/Debug.hpp>
|
|
#include <efsw/DirWatcherGeneric.hpp>
|
|
#include <efsw/FileSystem.hpp>
|
|
#include <efsw/String.hpp>
|
|
|
|
namespace efsw {
|
|
|
|
DirWatcherGeneric::DirWatcherGeneric( DirWatcherGeneric* parent, WatcherGeneric* ws,
|
|
const std::string& directory, bool recursive,
|
|
bool reportNewFiles ) :
|
|
Parent( parent ), Watch( ws ), Recursive( recursive ), Deleted( false ) {
|
|
resetDirectory( directory );
|
|
|
|
if ( !reportNewFiles ) {
|
|
DirSnap.scan();
|
|
} else {
|
|
DirectorySnapshotDiff Diff = DirSnap.scan();
|
|
|
|
if ( Diff.changed() ) {
|
|
FileInfoList::iterator it;
|
|
|
|
DiffIterator( FilesCreated ) {
|
|
handleAction( ( *it ).Filepath, Actions::Add );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DirWatcherGeneric::~DirWatcherGeneric() {
|
|
/// If the directory was deleted mark the files as deleted
|
|
if ( Deleted ) {
|
|
DirectorySnapshotDiff Diff = DirSnap.scan();
|
|
|
|
if ( !DirSnap.exists() ) {
|
|
FileInfoList::iterator it;
|
|
|
|
DiffIterator( FilesDeleted ) {
|
|
handleAction( ( *it ).Filepath, Actions::Delete );
|
|
}
|
|
|
|
DiffIterator( DirsDeleted ) {
|
|
handleAction( ( *it ).Filepath, Actions::Delete );
|
|
}
|
|
}
|
|
}
|
|
|
|
DirWatchMap::iterator it = Directories.begin();
|
|
|
|
for ( ; it != Directories.end(); ++it ) {
|
|
if ( Deleted ) {
|
|
/// If the directory was deleted, mark the flag for file deletion
|
|
it->second->Deleted = true;
|
|
}
|
|
|
|
efSAFE_DELETE( it->second );
|
|
}
|
|
}
|
|
|
|
void DirWatcherGeneric::resetDirectory( std::string directory ) {
|
|
std::string dir( directory );
|
|
|
|
/// Is this a recursive watch?
|
|
if ( Watch->Directory != directory ) {
|
|
if ( !( directory.size() &&
|
|
( directory.at( 0 ) == FileSystem::getOSSlash() ||
|
|
directory.at( directory.size() - 1 ) == FileSystem::getOSSlash() ) ) ) {
|
|
/// Get the real directory
|
|
if ( NULL != Parent ) {
|
|
std::string parentPath( Parent->DirSnap.DirectoryInfo.Filepath );
|
|
FileSystem::dirAddSlashAtEnd( parentPath );
|
|
FileSystem::dirAddSlashAtEnd( directory );
|
|
|
|
dir = parentPath + directory;
|
|
} else {
|
|
efDEBUG( "resetDirectory(): Parent is NULL. Fatal error." );
|
|
}
|
|
}
|
|
}
|
|
|
|
DirSnap.setDirectoryInfo( dir );
|
|
}
|
|
|
|
void DirWatcherGeneric::handleAction( const std::string& filename, unsigned long action,
|
|
std::string oldFilename ) {
|
|
Watch->Listener->handleFileAction( Watch->ID, DirSnap.DirectoryInfo.Filepath,
|
|
FileSystem::fileNameFromPath( filename ), (Action)action,
|
|
oldFilename );
|
|
}
|
|
|
|
void DirWatcherGeneric::addChilds( bool reportNewFiles ) {
|
|
if ( Recursive ) {
|
|
/// Create the subdirectories watchers
|
|
std::string dir;
|
|
|
|
for ( FileInfoMap::iterator it = DirSnap.Files.begin(); it != DirSnap.Files.end(); it++ ) {
|
|
if ( it->second.isDirectory() && it->second.isReadable() &&
|
|
!FileSystem::isRemoteFS( it->second.Filepath ) ) {
|
|
/// Check if the directory is a symbolic link
|
|
std::string curPath;
|
|
std::string link( FileSystem::getLinkRealPath( it->second.Filepath, curPath ) );
|
|
|
|
dir = it->first;
|
|
|
|
if ( "" != link ) {
|
|
/// Avoid adding symlinks directories if it's now enabled
|
|
if ( !Watch->WatcherImpl->mFileWatcher->followSymlinks() ) {
|
|
continue;
|
|
}
|
|
|
|
/// If it's a symlink check if the realpath exists as a watcher, or
|
|
/// if the path is outside the current dir
|
|
if ( Watch->WatcherImpl->pathInWatches( link ) ||
|
|
Watch->pathInWatches( link ) ||
|
|
!Watch->WatcherImpl->linkAllowed( curPath, link ) ) {
|
|
continue;
|
|
} else {
|
|
dir = link;
|
|
}
|
|
} else {
|
|
if ( Watch->pathInWatches( dir ) || Watch->WatcherImpl->pathInWatches( dir ) ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( reportNewFiles ) {
|
|
handleAction( dir, Actions::Add );
|
|
}
|
|
|
|
Directories[dir] =
|
|
new DirWatcherGeneric( this, Watch, dir, Recursive, reportNewFiles );
|
|
|
|
Directories[dir]->addChilds( reportNewFiles );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DirWatcherGeneric::watch( bool reportOwnChange ) {
|
|
DirectorySnapshotDiff Diff = DirSnap.scan();
|
|
|
|
if ( reportOwnChange && Diff.DirChanged && NULL != Parent ) {
|
|
Watch->Listener->handleFileAction(
|
|
Watch->ID, FileSystem::pathRemoveFileName( DirSnap.DirectoryInfo.Filepath ),
|
|
FileSystem::fileNameFromPath( DirSnap.DirectoryInfo.Filepath ), Actions::Modified );
|
|
}
|
|
|
|
if ( Diff.changed() ) {
|
|
FileInfoList::iterator it;
|
|
MovedList::iterator mit;
|
|
|
|
/// Files
|
|
DiffIterator( FilesCreated ) {
|
|
handleAction( ( *it ).Filepath, Actions::Add );
|
|
}
|
|
|
|
DiffIterator( FilesModified ) {
|
|
handleAction( ( *it ).Filepath, Actions::Modified );
|
|
}
|
|
|
|
DiffIterator( FilesDeleted ) {
|
|
handleAction( ( *it ).Filepath, Actions::Delete );
|
|
}
|
|
|
|
DiffMovedIterator( FilesMoved ) {
|
|
handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first );
|
|
}
|
|
|
|
/// Directories
|
|
DiffIterator( DirsCreated ) {
|
|
createDirectory( ( *it ).Filepath );
|
|
}
|
|
|
|
DiffIterator( DirsModified ) {
|
|
handleAction( ( *it ).Filepath, Actions::Modified );
|
|
}
|
|
|
|
DiffIterator( DirsDeleted ) {
|
|
handleAction( ( *it ).Filepath, Actions::Delete );
|
|
removeDirectory( ( *it ).Filepath );
|
|
}
|
|
|
|
DiffMovedIterator( DirsMoved ) {
|
|
handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first );
|
|
moveDirectory( ( *mit ).first, ( *mit ).second.Filepath );
|
|
}
|
|
}
|
|
|
|
/// Process the subdirectories looking for changes
|
|
for ( DirWatchMap::iterator dit = Directories.begin(); dit != Directories.end(); ++dit ) {
|
|
/// Just watch
|
|
dit->second->watch();
|
|
}
|
|
}
|
|
|
|
void DirWatcherGeneric::watchDir( std::string& dir ) {
|
|
DirWatcherGeneric* watcher = Watch->WatcherImpl->mFileWatcher->allowOutOfScopeLinks()
|
|
? findDirWatcher( dir )
|
|
: findDirWatcherFast( dir );
|
|
|
|
if ( NULL != watcher ) {
|
|
watcher->watch( true );
|
|
}
|
|
}
|
|
|
|
DirWatcherGeneric* DirWatcherGeneric::findDirWatcherFast( std::string dir ) {
|
|
// remove the common base ( dir should always start with the same base as the watcher )
|
|
efASSERT( !dir.empty() );
|
|
efASSERT( dir.size() >= DirSnap.DirectoryInfo.Filepath.size() );
|
|
efASSERT( DirSnap.DirectoryInfo.Filepath ==
|
|
dir.substr( 0, DirSnap.DirectoryInfo.Filepath.size() ) );
|
|
|
|
if ( dir.size() >= DirSnap.DirectoryInfo.Filepath.size() ) {
|
|
dir = dir.substr( DirSnap.DirectoryInfo.Filepath.size() - 1 );
|
|
}
|
|
|
|
if ( dir.size() == 1 ) {
|
|
efASSERT( dir[0] == FileSystem::getOSSlash() );
|
|
return this;
|
|
}
|
|
|
|
size_t level = 0;
|
|
std::vector<std::string> dirv = String::split( dir, FileSystem::getOSSlash(), false );
|
|
|
|
DirWatcherGeneric* watcher = this;
|
|
|
|
while ( level < dirv.size() ) {
|
|
// search the dir level in the current watcher
|
|
DirWatchMap::iterator it = watcher->Directories.find( dirv[level] );
|
|
|
|
// found? continue with the next level
|
|
if ( it != watcher->Directories.end() ) {
|
|
watcher = it->second;
|
|
|
|
level++;
|
|
} else {
|
|
// couldn't found the folder level?
|
|
// directory not watched
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return watcher;
|
|
}
|
|
|
|
DirWatcherGeneric* DirWatcherGeneric::findDirWatcher( std::string dir ) {
|
|
if ( DirSnap.DirectoryInfo.Filepath == dir ) {
|
|
return this;
|
|
} else {
|
|
DirWatcherGeneric* watcher = NULL;
|
|
|
|
for ( DirWatchMap::iterator it = Directories.begin(); it != Directories.end(); ++it ) {
|
|
watcher = it->second->findDirWatcher( dir );
|
|
|
|
if ( NULL != watcher ) {
|
|
return watcher;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DirWatcherGeneric* DirWatcherGeneric::createDirectory( std::string newdir ) {
|
|
FileSystem::dirRemoveSlashAtEnd( newdir );
|
|
newdir = FileSystem::fileNameFromPath( newdir );
|
|
|
|
DirWatcherGeneric* dw = NULL;
|
|
|
|
/// Check if the directory is a symbolic link
|
|
std::string parentPath( DirSnap.DirectoryInfo.Filepath );
|
|
FileSystem::dirAddSlashAtEnd( parentPath );
|
|
std::string dir( parentPath + newdir );
|
|
|
|
FileSystem::dirAddSlashAtEnd( dir );
|
|
|
|
FileInfo fi( dir );
|
|
|
|
if ( !fi.isDirectory() || !fi.isReadable() || FileSystem::isRemoteFS( dir ) ) {
|
|
return NULL;
|
|
}
|
|
|
|
std::string curPath;
|
|
std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
|
|
bool skip = false;
|
|
|
|
if ( "" != link ) {
|
|
/// Avoid adding symlinks directories if it's now enabled
|
|
if ( !Watch->WatcherImpl->mFileWatcher->followSymlinks() ) {
|
|
skip = true;
|
|
}
|
|
|
|
/// If it's a symlink check if the realpath exists as a watcher, or
|
|
/// if the path is outside the current dir
|
|
if ( Watch->WatcherImpl->pathInWatches( link ) || Watch->pathInWatches( link ) ||
|
|
!Watch->WatcherImpl->linkAllowed( curPath, link ) ) {
|
|
skip = true;
|
|
} else {
|
|
dir = link;
|
|
}
|
|
} else {
|
|
if ( Watch->pathInWatches( dir ) || Watch->WatcherImpl->pathInWatches( dir ) ) {
|
|
skip = true;
|
|
}
|
|
}
|
|
|
|
if ( !skip ) {
|
|
handleAction( newdir, Actions::Add );
|
|
|
|
/// Creates the new directory watcher of the subfolder and check for new files
|
|
dw = new DirWatcherGeneric( this, Watch, dir, Recursive );
|
|
|
|
dw->addChilds();
|
|
|
|
dw->watch();
|
|
|
|
/// Add it to the list of directories
|
|
Directories[newdir] = dw;
|
|
}
|
|
|
|
return dw;
|
|
}
|
|
|
|
void DirWatcherGeneric::removeDirectory( std::string dir ) {
|
|
FileSystem::dirRemoveSlashAtEnd( dir );
|
|
dir = FileSystem::fileNameFromPath( dir );
|
|
|
|
DirWatcherGeneric* dw = NULL;
|
|
DirWatchMap::iterator dit;
|
|
|
|
/// Folder deleted
|
|
|
|
/// Search the folder, it should exists
|
|
dit = Directories.find( dir );
|
|
|
|
if ( dit != Directories.end() ) {
|
|
dw = dit->second;
|
|
|
|
/// Flag it as deleted so it fire the event for every file inside deleted
|
|
dw->Deleted = true;
|
|
|
|
/// Delete the DirWatcherGeneric
|
|
efSAFE_DELETE( dw );
|
|
|
|
/// Remove the directory from the map
|
|
Directories.erase( dit->first );
|
|
}
|
|
}
|
|
|
|
void DirWatcherGeneric::moveDirectory( std::string oldDir, std::string newDir ) {
|
|
FileSystem::dirRemoveSlashAtEnd( oldDir );
|
|
oldDir = FileSystem::fileNameFromPath( oldDir );
|
|
|
|
FileSystem::dirRemoveSlashAtEnd( newDir );
|
|
newDir = FileSystem::fileNameFromPath( newDir );
|
|
|
|
DirWatcherGeneric* dw = NULL;
|
|
DirWatchMap::iterator dit;
|
|
|
|
/// Directory existed?
|
|
dit = Directories.find( oldDir );
|
|
|
|
if ( dit != Directories.end() ) {
|
|
dw = dit->second;
|
|
|
|
/// Remove the directory from the map
|
|
Directories.erase( dit->first );
|
|
|
|
Directories[newDir] = dw;
|
|
|
|
dw->resetDirectory( newDir );
|
|
}
|
|
}
|
|
|
|
bool DirWatcherGeneric::pathInWatches( std::string path ) {
|
|
if ( DirSnap.DirectoryInfo.Filepath == path ) {
|
|
return true;
|
|
}
|
|
|
|
for ( DirWatchMap::iterator it = Directories.begin(); it != Directories.end(); ++it ) {
|
|
if ( it->second->pathInWatches( path ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // namespace efsw
|