Skip to content

Commit

Permalink
Merge pull request #162 from kreuli/master
Browse files Browse the repository at this point in the history
Customization of file watchers
  • Loading branch information
SpartanJ authored Aug 7, 2023
2 parents a2a0e47 + fa4a0ee commit 6187b49
Show file tree
Hide file tree
Showing 24 changed files with 368 additions and 49 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
bin/
build/
obj/
make/
lib/
Expand All @@ -10,6 +11,7 @@ CMakeFiles/
*.DS_Store
.qtc_clangd
.cmake
.vscode/
*.so
*.a
*.dylib
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ if(EFSW_INSTALL)
endif()

if(BUILD_TEST_APP)
# C++ test application
add_executable(efsw-test src/test/efsw-test.cpp)
target_link_libraries(efsw-test efsw-static)

# C test application
add_executable(efsw-test-stdc src/test/efsw-test.c)
target_link_libraries(efsw-test-stdc efsw-static)
endif()
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ efsw::WatchID watchID = fileWatcher->addWatch( "/tmp", listener, true );
// Adds another directory to watch. This time as non-recursive.
efsw::WatchID watchID2 = fileWatcher->addWatch( "/usr", listener, false );

// For Windows, adds another watch, specifying to use a bigger buffer, to not miss events
// (do not use for network locations, see efsw.hpp for details).
efsw::WatchID watchID3 = fileWatcher->addWatch( "c:\\temp", listener, true, { (BufferSize, 128*1024) } );

// Start watching asynchronously the directories
fileWatcher->watch();

Expand Down
37 changes: 33 additions & 4 deletions include/efsw/efsw.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,22 @@ enum efsw_error
EFSW_OUTOFSCOPE = -3,
EFSW_NOTREADABLE = -4,
EFSW_REMOTE = -5,
EFSW_UNSPECIFIED = -6
EFSW_WATCHER_FAILED = -6,
EFSW_UNSPECIFIED = -7
};

enum efsw_option
{
/// For Windows, the default buffer size of 63*1024 bytes sometimes is not enough and
/// file system events may be dropped. For that, using a different (bigger) buffer size
/// can be defined here, but note that this does not work for network drives,
/// because a buffer larger than 64K will fail the folder being watched, see
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
EFSW_OPT_WIN_BUFFER_SIZE = 1,
/// For Windows, per default all events are captured but we might only be interested
/// in a subset; the value of the option should be set to a bitwise or'ed set of
/// FILE_NOTIFY_CHANGE_* flags.
EFSW_OPT_WIN_NOTIFY_FILTER = 2
};

/// Basic interface for listening for file events.
Expand All @@ -94,6 +109,11 @@ typedef void (*efsw_pfn_fileaction_callback) (
void* param
);

typedef struct {
enum efsw_option option;
int value;
} efsw_watcher_option;

/**
* Creates a new file-watcher
* @param generic_mode Force the use of the Generic file watcher
Expand All @@ -103,15 +123,24 @@ efsw_watcher EFSW_API efsw_create(int generic_mode);
/// Release the file-watcher and unwatch any directories
void EFSW_API efsw_release(efsw_watcher watcher);

/// Retreive last error occured by file-watcher
/// Retrieve last error occured by file-watcher
EFSW_API const char* efsw_getlasterror();

/// Add a directory watch. Same as the other addWatch, but doesn't have recursive option.
/// For backwards compatibility.
/// Reset file-watcher last error
EFSW_API void efsw_clearlasterror();

/// Add a directory watch
/// On error returns WatchID with Error type.
efsw_watchid EFSW_API efsw_addwatch(efsw_watcher watcher, const char* directory,
efsw_pfn_fileaction_callback callback_fn, int recursive, void* param);

/// Add a directory watch, specifying options
/// @param options Pointer to an array of watcher options
/// @param nr_options Number of options referenced by \p options
efsw_watchid EFSW_API efsw_addwatch_withoptions(efsw_watcher watcher, const char* directory,
efsw_pfn_fileaction_callback callback_fn, int recursive, efsw_watcher_option *options,
int options_number, void* param);

/// Remove a directory watch. This is a brute force search O(nlogn).
void EFSW_API efsw_removewatch(efsw_watcher watcher, const char* directory);

Expand Down
53 changes: 50 additions & 3 deletions include/efsw/efsw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include <vector>
#include <string>
#include <vector>

#if defined( _WIN32 )
#ifdef EFSW_DYNAMIC
Expand Down Expand Up @@ -68,6 +69,7 @@ typedef long WatchID;
// forward declarations
class FileWatcherImpl;
class FileWatchListener;
class WatcherOption;

/// Actions to listen for. Rename will send two events, one for
/// the deletion of the old file, and one for the creation of the
Expand All @@ -90,27 +92,54 @@ typedef Actions::Action Action;
namespace Errors {

enum Error {
NoError = 0,
FileNotFound = -1,
FileRepeated = -2,
FileOutOfScope = -3,
FileNotReadable = -4,
FileRemote = -5, /** Directory in remote file system ( create a generic FileWatcher instance to
watch this directory ). */
Unspecified = -6
/// Directory in remote file system
/// ( create a generic FileWatcher instance to watch this directory ).
FileRemote = -5,
/// File system watcher failed to watch for changes.
WatcherFailed = -6,
Unspecified = -7
};

class EFSW_API Log {
public:
/// @return The last error logged
static std::string getLastErrorLog();

/// @return The code of the last error logged
static Error getLastErrorCode();

/// Reset last error
static void clearLastError();

/// Creates an error of the type specified
static Error createLastError( Error err, std::string log );
};

} // namespace Errors
typedef Errors::Error Error;

/// Optional file watcher settings.
namespace Options {
enum Option {
/// For Windows, the default buffer size of 63*1024 bytes sometimes is not enough and
/// file system events may be dropped. For that, using a different (bigger) buffer size
/// can be defined here, but note that this does not work for network drives,
/// because a buffer larger than 64K will fail the folder being watched, see
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
WinBufferSize = 1,
/// For Windows, per default all events are captured but we might only be interested
/// in a subset; the value of the option should be set to a bitwise or'ed set of
/// FILE_NOTIFY_CHANGE_* flags.
WinNotifyFilter = 2
};
}
typedef Options::Option Option;

/// Listens to files and directories and dispatches events
/// to notify the listener of files and directories changes.
/// @class FileWatcher
Expand All @@ -133,6 +162,15 @@ class EFSW_API FileWatcher {
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive );

/// Add a directory watch, allowing customization with options
/// @param directory The folder to be watched
/// @param watcher The listener to receive events
/// @param recursive Set this to true to include subdirectories
/// @param options Allows customization of a watcher
/// @return Returns the watch id for the directory or, on error, a WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options );

/// Remove a directory watch. This is a brute force search O(nlogn).
void removeWatch( const std::string& directory );

Expand Down Expand Up @@ -190,6 +228,15 @@ class FileWatchListener {
std::string oldFilename = "" ) = 0;
};

/// Optional, typically platform specific parameter for customization of a watcher.
/// @class WatcherOption
class WatcherOption {
public:
WatcherOption(Option option, int value) : mOption(option), mValue(value) {};
Option mOption;
int mValue;
};

} // namespace efsw

#endif
13 changes: 7 additions & 6 deletions src/efsw/FileWatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,18 @@ FileWatcher::~FileWatcher() {
}

WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher ) {
if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) {
return mImpl->addWatch( directory, watcher, false );
} else {
return Errors::Log::createLastError( Errors::FileRemote, directory );
}
return addWatch(directory, watcher, false, {});
}

WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive ) {
return addWatch(directory, watcher, recursive, {});
}

WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, const std::vector<WatcherOption> &options ) {
if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) {
return mImpl->addWatch( directory, watcher, recursive );
return mImpl->addWatch( directory, watcher, recursive, options );
} else {
return Errors::Log::createLastError( Errors::FileRemote, directory );
}
Expand Down
20 changes: 19 additions & 1 deletion src/efsw/FileWatcherCWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,35 @@ const char* efsw_getlasterror() {
return log_str.c_str();
}

EFSW_API void efsw_clearlasterror() {
efsw::Errors::Log::clearLastError();
}

efsw_watchid efsw_addwatch( efsw_watcher watcher, const char* directory,
efsw_pfn_fileaction_callback callback_fn, int recursive, void* param ) {
return efsw_addwatch_withoptions( watcher, directory, callback_fn, recursive, 0, 0, param );
}

efsw_watchid efsw_addwatch_withoptions(efsw_watcher watcher, const char* directory,
efsw_pfn_fileaction_callback callback_fn, int recursive,
efsw_watcher_option *options, int options_number,
void* param) {
Watcher_CAPI* callback = find_callback( watcher, callback_fn );

if ( callback == NULL ) {
callback = new Watcher_CAPI( watcher, callback_fn, param );
g_callbacks.push_back( callback );
}

std::vector<efsw::WatcherOption> watcher_options{};
for ( int i = 0; i < options_number; i++ ) {
efsw_watcher_option* option = &options[i];
watcher_options.emplace_back( efsw::WatcherOption{
static_cast<efsw::Option>(option->option), option->value } );
}

return ( (efsw::FileWatcher*)watcher )
->addWatch( std::string( directory ), callback, TOBOOL( recursive ) );
->addWatch( std::string( directory ), callback, TOBOOL( recursive ), watcher_options );
}

void efsw_removewatch( efsw_watcher watcher, const char* directory ) {
Expand Down
2 changes: 1 addition & 1 deletion src/efsw/FileWatcherFSEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ FileWatcherFSEvents::~FileWatcherFSEvents() {
}

WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive ) {
bool recursive, const std::vector<WatcherOption> &options ) {
std::string dir( directory );

FileInfo fi( dir );
Expand Down
3 changes: 2 additions & 1 deletion src/efsw/FileWatcherFSEvents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class FileWatcherFSEvents : public FileWatcherImpl {

/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive );
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options );

/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory );
Expand Down
2 changes: 1 addition & 1 deletion src/efsw/FileWatcherGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ FileWatcherGeneric::~FileWatcherGeneric() {
}

WatchID FileWatcherGeneric::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive ) {
bool recursive, const std::vector<WatcherOption> &options ) {
std::string dir( directory );

FileSystem::dirAddSlashAtEnd( dir );
Expand Down
3 changes: 2 additions & 1 deletion src/efsw/FileWatcherGeneric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class FileWatcherGeneric : public FileWatcherImpl {

/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive );
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options );

/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory );
Expand Down
12 changes: 12 additions & 0 deletions src/efsw/FileWatcherImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string
-1 != String::strStartsWith( curPath, link );
}

int FileWatcherImpl::getOptionValue( const std::vector<WatcherOption> &options,
Option option, int defaultValue) {
for ( int i = 0; i < options.size(); i++ ) {
if (options[i].mOption == option) {
return options[i].mValue;
}
}

return defaultValue;
}


} // namespace efsw
7 changes: 6 additions & 1 deletion src/efsw/FileWatcherImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FileWatcherImpl {
/// Add a directory watch
/// On error returns WatchID with Error type.
virtual WatchID addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive ) = 0;
bool recursive, const std::vector<WatcherOption> &options = {} ) = 0;

/// Remove a directory watch. This is a brute force lazy search O(nlogn).
virtual void removeWatch( const std::string& directory ) = 0;
Expand Down Expand Up @@ -47,9 +47,14 @@ class FileWatcherImpl {
/// Search if a directory already exists in the watches
virtual bool pathInWatches( const std::string& path ) = 0;


FileWatcher* mFileWatcher;
Atomic<bool> mInitOK;
bool mIsGeneric;

protected:
int getOptionValue( const std::vector<WatcherOption> &options,
Option option, int defaultValue );
};

} // namespace efsw
Expand Down
2 changes: 1 addition & 1 deletion src/efsw/FileWatcherInotify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ FileWatcherInotify::~FileWatcherInotify() {
}

WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive ) {
bool recursive, const std::vector<WatcherOption> &options ) {
if ( !mInitOK )
return Errors::Log::createLastError( Errors::Unspecified, directory );
Lock initLock( mInitLock );
Expand Down
3 changes: 2 additions & 1 deletion src/efsw/FileWatcherInotify.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class FileWatcherInotify : public FileWatcherImpl {

/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive );
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options );

/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory );
Expand Down
2 changes: 1 addition & 1 deletion src/efsw/FileWatcherKqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ FileWatcherKqueue::~FileWatcherKqueue() {
}

WatchID FileWatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive ) {
bool recursive, const std::vector<WatcherOption> &options ) {
static bool s_ug = false;

std::string dir( directory );
Expand Down
3 changes: 2 additions & 1 deletion src/efsw/FileWatcherKqueue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class FileWatcherKqueue : public FileWatcherImpl {

/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive );
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options );

/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory );
Expand Down
Loading

0 comments on commit 6187b49

Please sign in to comment.