Compare commits

..

No commits in common. "54b22488af1713eae250fbf30de43097bf6563f0" and "2b88e3928f86f05a57566166535cdb40b7a77878" have entirely different histories.

102 changed files with 22245 additions and 238 deletions

18
.clang-format Normal file
View File

@ -0,0 +1,18 @@
---
AlignEscapedNewlines: Left
AllowAllConstructorInitializersOnNextLine: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'true'
AllowShortFunctionsOnASingleLine: Inline
BreakConstructorInitializers: AfterColon
ColumnLimit: '100'
CompactNamespaces: 'true'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
ContinuationIndentWidth: '4'
IndentCaseLabels: 'true'
IndentWidth: '4'
PointerAlignment: Left
SpacesInParentheses: 'true'
TabWidth: '4'
UseTab: Always
...

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ CMakeFiles/
/cmake
/CMakeCache.txt
/cmake_install.cmake
/Makefile
*.DS_Store
.qtc_clangd
.cmake

192
CMakeLists.txt Normal file
View File

@ -0,0 +1,192 @@
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
project(efsw)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 11)
set(ESFW_MAIN_PROJECT OFF)
if((CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) AND(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME))
set(ESFW_MAIN_PROJECT ON)
endif()
include(GNUInstallDirs)
find_package(Threads REQUIRED)
option(VERBOSE "Build efsw with verbose mode.")
option(BUILD_SHARED_LIBS "Build efsw as a shared library" ON)
option(BUILD_STATIC_LIBS "Build efsw as a static library" ON)
option(BUILD_TEST_APP "Build the test app" ${ESFW_MAIN_PROJECT})
option(EFSW_INSTALL "Add efsw install targets" ${ESFW_MAIN_PROJECT})
add_library(efsw)
if(BUILD_STATIC_LIBS)
add_library(efsw-static STATIC)
target_include_directories(efsw-static
PRIVATE src/
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
endif()
set(EFSW_CPP_SOURCE
src/efsw/Debug.cpp
src/efsw/DirectorySnapshot.cpp
src/efsw/DirectorySnapshotDiff.cpp
src/efsw/DirWatcherGeneric.cpp
src/efsw/FileInfo.cpp
src/efsw/FileSystem.cpp
src/efsw/FileWatcher.cpp
src/efsw/FileWatcherCWrapper.cpp
src/efsw/FileWatcherGeneric.cpp
src/efsw/FileWatcherImpl.cpp
src/efsw/Log.cpp
src/efsw/String.cpp
src/efsw/System.cpp
src/efsw/Watcher.cpp
src/efsw/WatcherGeneric.cpp
)
target_include_directories(efsw
PRIVATE src/
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if(VERBOSE)
target_compile_definitions(efsw PRIVATE EFSW_VERBOSE)
endif()
target_compile_features(efsw PRIVATE cxx_std_11)
if(BUILD_SHARED_LIBS)
target_compile_definitions(efsw PRIVATE EFSW_DYNAMIC EFSW_EXPORTS)
endif()
# platforms
if(WIN32)
list(APPEND EFSW_CPP_SOURCE
src/efsw/platform/win/FileSystemImpl.cpp
src/efsw/platform/win/SystemImpl.cpp
)
else()
list(APPEND EFSW_CPP_SOURCE
src/efsw/platform/posix/FileSystemImpl.cpp
src/efsw/platform/posix/SystemImpl.cpp
)
endif()
# watcher implementations
if(APPLE)
list(APPEND EFSW_CPP_SOURCE
src/efsw/FileWatcherFSEvents.cpp
src/efsw/FileWatcherKqueue.cpp
src/efsw/WatcherFSEvents.cpp
src/efsw/WatcherKqueue.cpp
)
elseif(WIN32)
list(APPEND EFSW_CPP_SOURCE
src/efsw/FileWatcherWin32.cpp
src/efsw/WatcherWin32.cpp
)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
list(APPEND EFSW_CPP_SOURCE
src/efsw/FileWatcherInotify.cpp
src/efsw/WatcherInotify.cpp
)
find_path(EFSW_INOTIFY_H NAMES sys/inotify.h NO_CACHE)
if(EFSW_INOTIFY_H STREQUAL "EFSW_INOTIFY_H-NOTFOUND")
target_compile_definitions(efsw PRIVATE EFSW_INOTIFY_NOSYS)
endif()
elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
list(APPEND EFSW_CPP_SOURCE
src/efsw/FileWatcherKqueue.cpp
src/efsw/WatcherKqueue.cpp
)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR
(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))
target_compile_definitions(efsw PRIVATE _SCL_SECURE_NO_WARNINGS)
else()
target_compile_options(efsw PRIVATE -Wall -Wno-long-long -fPIC)
endif()
target_compile_definitions(efsw PRIVATE $<IF:$<CONFIG:Debug>,DEBUG,NDEBUG>)
if(APPLE)
set(MAC_LIBS "-framework CoreFoundation" "-framework CoreServices")
target_link_libraries(efsw PRIVATE ${MAC_LIBS})
if(BUILD_STATIC_LIBS)
target_link_libraries(efsw-static PRIVATE ${MAC_LIBS})
endif()
elseif(NOT(${CMAKE_SYSTEM_NAME} MATCHES "Haiku") AND NOT WIN32)
target_link_libraries(efsw PRIVATE Threads::Threads)
endif()
target_sources(efsw PRIVATE ${EFSW_CPP_SOURCE})
if(BUILD_STATIC_LIBS)
target_sources(efsw-static PRIVATE ${EFSW_CPP_SOURCE})
endif()
include(CMakePackageConfigHelpers)
set(packageDestDir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/efswConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/cmake/efswConfig.cmake
INSTALL_DESTINATION "${packageDestDir}"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
export(TARGETS efsw NAMESPACE efsw:: FILE ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Targets.cmake)
if(BUILD_STATIC_LIBS)
export(TARGETS efsw-static NAMESPACE efsw:: APPEND FILE ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Targets.cmake)
endif()
if(EFSW_INSTALL)
install(TARGETS efsw EXPORT efswExport
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES
include/efsw/efsw.h include/efsw/efsw.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/efsw
)
if(BUILD_STATIC_LIBS)
install(TARGETS efsw-static EXPORT efswExport
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
install(EXPORT efswExport NAMESPACE efsw:: DESTINATION "${packageDestDir}" FILE ${PROJECT_NAME}Targets.cmake)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/efswConfig.cmake DESTINATION "${packageDestDir}")
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()

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2020 Martín Lucas Golini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com)
http://code.google.com/p/simplefilewatcher/ also MIT licensed.

114
Makefile
View File

@ -1,114 +0,0 @@
EXECUTABLE_NAME = joomer-efsw-file-monitoring
PLATFORM = $(shell uname)
BUILD_TYPE ?= release# Default to release build if not specified
# Common paths
LIBEFSW_PATH = ../efsw
OBJ_DIR = obj/$(PLATFORM)/$(BUILD_TYPE)
BIN_DIR = bin/$(PLATFORM)/$(BUILD_TYPE)
OUTPUT_FILE = $(BIN_DIR)/$(EXECUTABLE_NAME)
# Platform-specific configuration
ifeq ($(PLATFORM), Darwin)
# macOS configuration
SDK_LIB_EXT = dylib
EFSW_LIB_NAME = libefsw.$(SDK_LIB_EXT)
MACOS_SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
# Compiler settings
CC = clang
CXX = clang++
# Architecture flags
ARCH_FLAGS = -arch arm64 -mmacosx-version-min=11.0 -isysroot $(MACOS_SDK_PATH)
# Linking flags - Use multiple rpath entries to look in executable directory
LINKER_FLAGS = $(ARCH_FLAGS) -framework Cocoa -framework IOKit -fvisibility=hidden -O5 \
-rpath @executable_path \
-rpath .
else
# Linux configuration
SDK_LIB_EXT = so
EFSW_LIB_NAME = libefsw.$(SDK_LIB_EXT)
# Compiler settings
CC = gcc
CXX = g++
# Architecture flags
ARCH_FLAGS = -m64 -D_FILE_OFFSET_BITS=64
# Linking flags
LINKER_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 -Wl,-rpath,'$$ORIGIN' -Wl,-rpath,'$$ORIGIN/lib'
# Platform-specific libraries
#PLIST_LIB = -lplist
endif
# Common include and library paths
INCLUDE_PATHS = -I$(LIBEFSW_PATH)/include -I$(LIBEFSW_PATH)/src
EFSW_LIB_PATH = $(LIBEFSW_PATH)/build
# Library flags
LIB_PATHS = -L$(EFSW_LIB_PATH)
LIBRARIES = -lm -ldl -lefsw
# Build type specific flags
ifeq ($(BUILD_TYPE), debug)
CPP_DEFINES = -D_DEBUG -DDL_USE_SHARED
COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -g -O0 $(INCLUDE_PATHS)
else
CPP_DEFINES = -DNDEBUG=1 -DDL_USE_SHARED
COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 $(INCLUDE_PATHS)
endif
# Language-specific flags
C_FLAGS = $(COMMON_FLAGS) -std=c17
CXX_FLAGS = $(COMMON_FLAGS) -std=c++17 -Wno-deprecated-declarations
# Objects
OBJECTS = $(EXECUTABLE_NAME).o
OBJECT_FILES = $(patsubst %,$(OBJ_DIR)/%,$(OBJECTS))
# Build rules
$(OBJ_DIR)/$(EXECUTABLE_NAME).o: $(EXECUTABLE_NAME).cpp
@mkdir -p $(@D)
$(CXX) -c -o $@ $< $(CXX_FLAGS) $(CPP_DEFINES)
$(OUTPUT_FILE): $(OBJECT_FILES)
@mkdir -p $(@D)
$(CXX) -o $@ $(OBJECT_FILES) $(LINKER_FLAGS) $(LIB_PATHS) $(LIBRARIES)
@echo "Copying libraries to $(BIN_DIR)..."
@cp $(EFSW_LIB_PATH)/$(EFSW_LIB_NAME) $(BIN_DIR)/$(EFSW_LIB_NAME)
@echo "Build complete: $(OUTPUT_FILE)"
# Add default target
all: $(OUTPUT_FILE)
.PHONY: clean cleanall all
clean:
rm -f $(OBJ_DIR)/$(EXECUTABLE_NAME).o
rm -f $(OUTPUT_FILE)
rm -f $(BIN_DIR)/*.dylib
rmdir $(OBJ_DIR) 2>/dev/null || true
rmdir $(BIN_DIR) 2>/dev/null || true
cleanall:
rm -f obj/*/release/*.o
rm -f obj/*/debug/*.o
rm -f bin/*/release/$(EXECUTABLE_NAME)
rm -f bin/*/debug/$(EXECUTABLE_NAME)
rm -f bin/*/release/$(SDK_LIB_FILE)
rm -f bin/*/debug/$(SDK_LIB_FILE)
rm -f bin/*/release/*.dylib
rm -f bin/*/debug/*.dylib
rmdir obj/*/release 2>/dev/null || true
rmdir obj/*/debug 2>/dev/null || true
rmdir bin/*/release 2>/dev/null || true
rmdir bin/*/debug 2>/dev/null || true
rmdir obj/* 2>/dev/null || true
rmdir bin/* 2>/dev/null || true
rmdir obj 2>/dev/null || true
rmdir bin 2>/dev/null || true

View File

@ -17,54 +17,51 @@ joomer-efsw-file-monitoring
```
learndir/
└── joomer-efsw-file-monitoring/
└── efsw
```
## Ubuntu Linux
- Download and Install Premake
## Ubuntu Linux (kasm-ubuntu)
```
mkdir learndir
cd learndir
git clone https://github.com/SpartanJ/efsw.git
mkdir -p efsw/build
cd efsw/build
cmake ..
make -j4
git clone https://git.indoodle.com/jason/joomer-efsw-file-monitoring.git
cd joomer-efsw-file-monitoring
premake5 gmake2
cd make/linux
make joomer-efsw-file-monitoring
cd ../..
git clone https://git.indoodle.com/jason/joomer-efsw-file-monitoring.git
cd joomer-efsw-file-monitoring
git switch ProposedCleanup
wget https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
wget https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h
make all -j4
mkdir bin/Linux/release/test
bin/Linux/release/joomer-efsw-file-monitoring
cd bin
mkdir test
./joomer-efsw-file-monitoring
```
## Windows
- Download Visual Studio Community Edition
## Windows (win10 enterprise)
- Download Visual Studio Community Edition 2022
- Run VisualStudioSetup.exe
- Workload = [x] Desktop development with C++
- Individual components = [x] Git For Windows
- Workload = [x] Desktop developemnt with C++
- Individial components = [x] Git For Windows
Run **x64 Native Tools Command Prompt for VS**
Run **x64 Native Tools Command Prompt for VS 2022**
```
mkdir learndir
cd learndir
git clone https://github.com/SpartanJ/efsw.git
cd efsw
mkdir build
cd build
cmake ..
msbuild efsw.sln /p:Configuration=Release /p:Platform=x64
cd ..
git clone https://git.indoodle.com/jason/joomer-efsw-file-monitoring.git
cd joomer-efsw-file-monitoring
git switch ProposedCleanup
premake5 vs2022
cd make/windows
msbuild joomer-efsw-file-monitoring.vcxproj /t:Build /p:Configuration=Debug /p:Platform=x64
cd ../..
cd bin
mkdir test
joomer-efsw-file-monitoring.exe
```
For a prototype app we can just dump the stb headers right into the joomer-efsw-file-monitoring/src dir for simplicity
```
curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h
msbuild joomer-efsw-file-monitoring.vcxproj /p:Configuration=release /p:Platform=x64 /p:PlatformToolset=v145
mkdir -p x64\release\test
x64\release\joomer-efsw-file-monitoring.exe
make all -j4
```

16
compile_flags.txt Normal file
View File

@ -0,0 +1,16 @@
-Wno-documentation-unknown-command
-Wno-unknown-warning-option
-Wno-unknown-pragmas
-std=c++11
-fsyntax-only
-Isrc
-Iinclude
-fmessage-length=0
-fdiagnostics-show-note-include-stack
-fretain-comments-from-system-headers
-fmacro-backtrace-limit=0
-ferror-limit=1000
-Wall
-Wextra
-x
c++-header

10
efswConfig.cmake.in Normal file
View File

@ -0,0 +1,10 @@
# - Config file for the @CMAKE_PROJECT_NAME@ package
@PACKAGE_INIT@
@DEPENDENCIES_SECTION@
include(CMakeFindDependencyMacro)
find_dependency(Threads)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")

197
include/efsw/efsw.h Normal file
View File

@ -0,0 +1,197 @@
/**
@author Sepul Sepehr Taghdisian
Copyright (c) 2024 Martín Lucas Golini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com)
http://code.google.com/p/simplefilewatcher/ also MIT licensed.
*/
/** This is the C API wrapper of EFSW */
#ifndef ESFW_H
#define ESFW_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined( _WIN32 )
#ifdef EFSW_DYNAMIC
// Windows platforms
#ifdef EFSW_EXPORTS
// From DLL side, we must export
#define EFSW_API __declspec( dllexport )
#else
// From client application side, we must import
#define EFSW_API __declspec( dllimport )
#endif
#else
// No specific directive needed for static build
#ifndef EFSW_API
#define EFSW_API
#endif
#endif
#else
#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS )
#define EFSW_API __attribute__( ( visibility( "default" ) ) )
#endif
// Other platforms don't need to define anything
#ifndef EFSW_API
#define EFSW_API
#endif
#endif
/// Type for a watch id
typedef long efsw_watchid;
/// Type for watcher
typedef void* efsw_watcher;
enum efsw_action {
EFSW_ADD = 1, /// Sent when a file is created or renamed
EFSW_DELETE = 2, /// Sent when a file is deleted or renamed
EFSW_MODIFIED = 3, /// Sent when a file is modified
EFSW_MOVED = 4 /// Sent when a file is moved
};
enum efsw_error {
EFSW_NOTFOUND = -1,
EFSW_REPEATED = -2,
EFSW_OUTOFSCOPE = -3,
EFSW_NOTREADABLE = -4,
EFSW_REMOTE = -5,
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,
/// For macOS (FSEvents backend), per default all modified event types are capture but we might
// only be interested in a subset; the value of the option should be set to a set of bitwise
// from:
// kFSEventStreamEventFlagItemFinderInfoMod
// kFSEventStreamEventFlagItemModified
// kFSEventStreamEventFlagItemInodeMetaMod
// Default configuration will set the 3 flags
EFSW_OPT_MAC_MODIFIED_FILTER = 3,
/// macOS sometimes informs incorrect or old file states that may confuse the consumer
/// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing
/// the number of events reported. This will have an small performance and memory impact as a
/// consequence.
EFSW_OPT_MAC_SANITIZE_EVENTS = 4,
/// Linux does not support natively recursive watchers. This means that when using recursive
/// watches efsw registers new watchers for each directory. If new file are created between
/// the time efsw takes to register the new directory those events might be missed. To avoid
/// missing new file notifications efsw will trigger synthetic new file events for existing
/// files in the new directroy watched. This might have the unintended consequence of sending
/// duplicated created events due to the system also emitting this event.
LINUX_PRODUCE_SYNTHETIC_EVENTS = 5,
};
/// Basic interface for listening for file events.
typedef void ( *efsw_pfn_fileaction_callback )( efsw_watcher watcher, efsw_watchid watchid,
const char* dir, const char* filename,
enum efsw_action action, const char* old_filename,
void* param );
typedef void ( *efsw_pfn_handle_missed_fileactions )( efsw_watcher watcher, efsw_watchid watchid,
const char* dir );
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
*/
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 );
/// Retrieve last error occured by file-watcher
EFSW_API const char* efsw_getlasterror();
/// 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,
efsw_pfn_handle_missed_fileactions callback_fn_missed_file_actions );
/// Remove a directory watch. This is a brute force search O(nlogn).
void EFSW_API efsw_removewatch( efsw_watcher watcher, const char* directory );
/// Remove a directory watch. This is a map lookup O(logn).
void EFSW_API efsw_removewatch_byid( efsw_watcher watcher, efsw_watchid watchid );
/// Starts watching ( in other thread )
void EFSW_API efsw_watch( efsw_watcher watcher );
/**
* Allow recursive watchers to follow symbolic links to other directories
* followSymlinks is disabled by default
*/
void EFSW_API efsw_follow_symlinks( efsw_watcher watcher, int enable );
/** @return If can follow symbolic links to directorioes */
int EFSW_API efsw_follow_symlinks_isenabled( efsw_watcher watcher );
/**
* When enable this it will allow symlinks to watch recursively out of the pointed directory.
* follorSymlinks must be enabled to this work.
* For example, added symlink to /home/folder, and the symlink points to /, this by default is not
* allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid great
* levels of recursion. Enabling this could lead in infinite recursion, and crash the watcher ( it
* will try not to avoid this ). Buy enabling out of scope links, it will allow this behavior.
* allowOutOfScopeLinks are disabled by default.
*/
void EFSW_API efsw_allow_outofscopelinks( efsw_watcher watcher, int allow );
/// @return Returns if out of scope links are allowed
int EFSW_API efsw_outofscopelinks_isallowed( efsw_watcher watcher );
#ifdef __cplusplus
}
#endif
#endif

267
include/efsw/efsw.hpp Normal file
View File

@ -0,0 +1,267 @@
/**
@author Martín Lucas Golini
Copyright (c) 2024 Martín Lucas Golini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com)
http://code.google.com/p/simplefilewatcher/ also MIT licensed.
*/
#ifndef ESFW_HPP
#define ESFW_HPP
#include <string>
#include <vector>
#if defined( _WIN32 )
#ifdef EFSW_DYNAMIC
// Windows platforms
#ifdef EFSW_EXPORTS
// From DLL side, we must export
#define EFSW_API __declspec( dllexport )
#else
// From client application side, we must import
#define EFSW_API __declspec( dllimport )
#endif
#else
// No specific directive needed for static build
#ifndef EFSW_API
#define EFSW_API
#endif
#endif
#else
#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS )
#ifndef EFSW_API
#define EFSW_API __attribute__( ( visibility( "default" ) ) )
#endif
#endif
// Other platforms don't need to define anything
#ifndef EFSW_API
#define EFSW_API
#endif
#endif
namespace efsw {
/// Type for a watch id
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
/// new file.
namespace Actions {
enum Action {
/// Sent when a file is created or renamed
Add = 1,
/// Sent when a file is deleted or renamed
Delete = 2,
/// Sent when a file is modified
Modified = 3,
/// Sent when a file is moved
Moved = 4
};
}
typedef Actions::Action Action;
/// Errors log namespace
namespace Errors {
enum Error {
NoError = 0,
FileNotFound = -1,
FileRepeated = -2,
FileOutOfScope = -3,
FileNotReadable = -4,
/// 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,
/// For macOS (FSEvents backend), per default all modified event types are capture but we might
/// only be interested in a subset; the value of the option should be set to a set of bitwise
/// from:
/// kFSEventStreamEventFlagItemFinderInfoMod
/// kFSEventStreamEventFlagItemModified
/// kFSEventStreamEventFlagItemInodeMetaMod
/// Default configuration will set the 3 flags
MacModifiedFilter = 3,
/// macOS sometimes informs incorrect or old file states that may confuse the consumer
/// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing
/// the number of events reported. This will have an small performance and memory impact as a
/// consequence.
MacSanitizeEvents = 4,
/// Linux does not support natively recursive watchers. This means that when using recursive
/// watches efsw registers new watchers for each directory. If new file are created between
/// the time efsw takes to register the new directory those events might be missed. To avoid
/// missing new file notifications efsw will trigger synthetic created file events for existing
/// files in the new directroy watched. This might have the unintended consequence of sending
/// duplicated created events due to the system also emitting this event.
LinuxProduceSyntheticEvents = 5,
};
}
typedef Options::Option Option;
/// Listens to files and directories and dispatches events
/// to notify the listener of files and directories changes.
/// @class FileWatcher
class EFSW_API FileWatcher {
public:
/// Default constructor, will use the default platform file watcher
FileWatcher();
/// Constructor that lets you force the use of the Generic File Watcher
explicit FileWatcher( bool useGenericFileWatcher );
virtual ~FileWatcher();
/// Add a directory watch. Same as the other addWatch, but doesn't have recursive option.
/// For backwards compatibility.
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher );
/// Add a directory watch
/// 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 );
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch( WatchID watchid );
/// Starts watching ( in other thread )
void watch();
/// @return Returns a list of the directories that are being watched
std::vector<std::string> directories();
/** Allow recursive watchers to follow symbolic links to other directories
* followSymlinks is disabled by default
*/
void followSymlinks( bool follow );
/** @return If can follow symbolic links to directorioes */
const bool& followSymlinks() const;
/** When enable this it will allow symlinks to watch recursively out of the pointed directory.
* follorSymlinks must be enabled to this work.
* For example, added symlink to /home/folder, and the symlink points to /, this by default is
* not allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid
* great levels of recursion. Enabling this could lead in infinite recursion, and crash the
* watcher ( it will try not to avoid this ). Buy enabling out of scope links, it will allow
* this behavior. allowOutOfScopeLinks are disabled by default.
*/
void allowOutOfScopeLinks( bool allow );
/// @return Returns if out of scope links are allowed
const bool& allowOutOfScopeLinks() const;
private:
/// The implementation
FileWatcherImpl* mImpl;
bool mFollowSymlinks;
bool mOutOfScopeLinks;
};
/// Basic interface for listening for file events.
/// @class FileWatchListener
class FileWatchListener {
public:
virtual ~FileWatchListener() {}
/// Handles the action file action
/// @param watchid The watch id for the directory
/// @param dir The directory
/// @param filename The filename that was accessed (not full path)
/// @param action Action that was performed
/// @param oldFilename The name of the file or directory moved
virtual void handleFileAction( WatchID watchid, const std::string& dir,
const std::string& filename, Action action,
std::string oldFilename = "" ) = 0;
/// Handles that have missed file actions
/// @param watchid The watch id for the directory
/// @param dir The directory
virtual void handleMissedFileActions( WatchID /*watchid*/,
const std::string& /*dir*/ ) {}
};
/// 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

View File

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="PseudoDebug|x64">
<Configuration>PseudoDebug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{7AEA0690-36B7-4596-9DEE-C3AB3C11D282}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='PseudoDebug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='PseudoDebug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='PseudoDebug|x64'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>PSEUDODEBUG;_CONSOLE;DL_USE_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\efsw\include;..\efsw\src</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>lib;..\efsw\build\Debug</AdditionalLibraryDirectories>
<AdditionalDependencies>efsw-static.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;DL_USE_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\efsw\include;..\efsw\src</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>lib;..\efsw\build\Release</AdditionalLibraryDirectories>
<AdditionalDependencies>efsw-static.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="$(PlatformToolset.Contains('Intel'))">
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PreprocessorDefinitions>_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<InterproceduralOptimization>NoIPO</InterproceduralOptimization>
</ClCompile>
<Link>
<InterproceduralOptimization>false</InterproceduralOptimization>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="joomer-efsw-file-monitoring.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

261
premake4.lua Normal file
View File

@ -0,0 +1,261 @@
newoption { trigger = "verbose", description = "Build efsw with verbose mode." }
newoption { trigger = "strip-symbols", description = "Strip debugging symbols in other file ( only for relwithdbginfo configuration )." }
newoption { trigger = "thread-sanitizer", description ="Compile with ThreadSanitizer." }
efsw_major_version = "1"
efsw_minor_version = "5"
efsw_patch_version = "0"
efsw_version = efsw_major_version .. "." .. efsw_minor_version .. "." .. efsw_patch_version
function get_include_paths()
local function _insert_include_paths( file )
local function _trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local paths = { }
local lines = file:read('*all')
for line in string.gmatch(lines, '([^\n]+)')
do
table.insert( paths, _trim( line ) )
end
file:close()
return paths
end
local file = io.popen( "echo | gcc -Wp,-v -x c++ - -fsyntax-only 2>&1 | grep -v '#' | grep '/'", 'r' )
local include_paths = _insert_include_paths( file )
if next(include_paths) == nil then
file = io.popen( "echo | clang++ -Wp,-v -x c++ - -fsyntax-only 2>&1 | grep -v '#' | grep '/' | grep -v 'nonexistent'", 'r' )
include_paths = _insert_include_paths( file )
if next(include_paths) == nil then
table.insert( include_paths, "/usr/include" )
table.insert( include_paths, "/usr/local/include" )
end
end
return include_paths
end
function inotify_header_exists()
local efsw_include_paths = get_include_paths()
for _,v in pairs( efsw_include_paths )
do
local cur_path = v .. "/sys/inotify.h"
if os.isfile( cur_path ) then
return true
end
end
return false
end
function string.starts(String,Start)
if ( _ACTION ) then
return string.sub(String,1,string.len(Start))==Start
end
return false
end
function is_vs()
return ( string.starts(_ACTION,"vs") )
end
function conf_warnings()
if not is_vs() then
buildoptions{ "-Wall -Wno-long-long" }
if not os.is("windows") then
buildoptions{ "-fPIC" }
end
else
defines { "_SCL_SECURE_NO_WARNINGS" }
end
if _OPTIONS["thread-sanitizer"] then
buildoptions { "-fsanitize=thread" }
linkoptions { "-fsanitize=thread" }
if not os.is("macosx") then
links { "tsan" }
end
end
end
function conf_links()
if not os.is("windows") and not os.is("haiku") then
links { "pthread" }
end
if os.is("macosx") then
links { "CoreFoundation.framework", "CoreServices.framework" }
end
end
function conf_excludes()
if os.is("windows") then
excludes { "src/efsw/WatcherKqueue.cpp", "src/efsw/WatcherFSEvents.cpp", "src/efsw/WatcherInotify.cpp", "src/efsw/FileWatcherKqueue.cpp", "src/efsw/FileWatcherInotify.cpp", "src/efsw/FileWatcherFSEvents.cpp" }
elseif os.is("linux") then
excludes { "src/efsw/WatcherKqueue.cpp", "src/efsw/WatcherFSEvents.cpp", "src/efsw/WatcherWin32.cpp", "src/efsw/FileWatcherKqueue.cpp", "src/efsw/FileWatcherWin32.cpp", "src/efsw/FileWatcherFSEvents.cpp" }
elseif os.is("macosx") then
excludes { "src/efsw/WatcherInotify.cpp", "src/efsw/WatcherWin32.cpp", "src/efsw/FileWatcherInotify.cpp", "src/efsw/FileWatcherWin32.cpp" }
elseif os.is("freebsd") then
excludes { "src/efsw/WatcherInotify.cpp", "src/efsw/WatcherWin32.cpp", "src/efsw/WatcherFSEvents.cpp", "src/efsw/FileWatcherInotify.cpp", "src/efsw/FileWatcherWin32.cpp", "src/efsw/FileWatcherFSEvents.cpp" }
end
if os.is("linux") and not inotify_header_exists() then
defines { "EFSW_INOTIFY_NOSYS" }
end
end
solution "efsw"
location("./make/" .. os.get() .. "/")
targetdir("./bin")
configurations { "debug", "release", "relwithdbginfo" }
if os.is("windows") then
osfiles = "src/efsw/platform/win/*.cpp"
else
osfiles = "src/efsw/platform/posix/*.cpp"
end
-- Activates verbose mode
if _OPTIONS["verbose"] then
defines { "EFSW_VERBOSE" }
end
if not is_vs() then
buildoptions { "-std=c++11" }
end
if os.is("macosx") then
-- Premake 4.4 needed for this
if not string.match(_PREMAKE_VERSION, "^4.[123]") then
local ver = os.getversion();
if not ( ver.majorversion >= 10 and ver.minorversion >= 5 ) then
defines { "EFSW_FSEVENTS_NOT_SUPPORTED" }
end
end
end
objdir("obj/" .. os.get() .. "/")
project "efsw-static-lib"
kind "StaticLib"
language "C++"
targetdir("./lib")
includedirs { "include", "src" }
files { "src/efsw/*.cpp", osfiles }
conf_excludes()
configuration "debug"
defines { "DEBUG" }
flags { "Symbols" }
targetname "efsw-static-debug"
conf_warnings()
configuration "release"
defines { "NDEBUG" }
flags { "Optimize" }
targetname "efsw-static-release"
conf_warnings()
configuration "relwithdbginfo"
defines { "NDEBUG" }
flags { "Optimize", "Symbols" }
targetname "efsw-static-reldbginfo"
conf_warnings()
project "efsw-test"
kind "ConsoleApp"
language "C++"
links { "efsw-static-lib" }
files { "src/test/*.cpp" }
includedirs { "include", "src" }
conf_links()
configuration "debug"
defines { "DEBUG" }
flags { "Symbols" }
targetname "efsw-test-debug"
conf_warnings()
configuration "release"
defines { "NDEBUG" }
flags { "Optimize" }
targetname "efsw-test-release"
conf_warnings()
configuration "relwithdbginfo"
defines { "NDEBUG" }
flags { "Optimize", "Symbols" }
targetname "efsw-test-reldbginfo"
conf_warnings()
project "efsw-test-stdc"
kind "ConsoleApp"
language "C"
links { "efsw-shared-lib" }
files { "src/test/*.c" }
includedirs { "include", "src" }
conf_links()
configuration "debug"
defines { "DEBUG" }
flags { "Symbols" }
targetname "efsw-test-stdc-debug"
conf_warnings()
configuration "release"
defines { "NDEBUG" }
flags { "Optimize" }
targetname "efsw-test-stdc-release"
conf_warnings()
configuration "relwithdbginfo"
defines { "NDEBUG" }
flags { "Optimize", "Symbols" }
targetname "efsw-test-stdc-reldbginfo"
conf_warnings()
project "efsw-shared-lib"
kind "SharedLib"
language "C++"
targetdir("./lib")
includedirs { "include", "src" }
files { "src/efsw/*.cpp", osfiles }
defines { "EFSW_DYNAMIC", "EFSW_EXPORTS" }
conf_excludes()
conf_links()
configuration "debug"
defines { "DEBUG" }
flags { "Symbols" }
targetname "efsw-debug"
conf_warnings()
configuration "release"
defines { "NDEBUG" }
flags { "Optimize" }
targetname "efsw"
conf_warnings()
configuration "relwithdbginfo"
defines { "NDEBUG" }
flags { "Optimize", "Symbols" }
targetname "efsw"
conf_warnings()
if os.is("linux") or os.is("bsd") or os.is("haiku") then
targetextension ( ".so." .. efsw_version )
postbuildcommands { "sh ../../project/build.reldbginfo.sh " .. efsw_major_version .. " " .. efsw_minor_version .. " " .. efsw_patch_version .. " " .. iif( _OPTIONS["strip-symbols"], "strip-symbols", "" ) }
end

217
premake5.lua Normal file
View File

@ -0,0 +1,217 @@
newoption { trigger = "verbose", description = "Build efsw with verbose mode." }
newoption { trigger = "strip-symbols", description = "Strip debugging symbols in other file ( only for relwithdbginfo configuration )." }
newoption { trigger = "thread-sanitizer", description ="Compile with ThreadSanitizer" }
efsw_major_version = "1"
efsw_minor_version = "5"
efsw_patch_version = "0"
efsw_version = efsw_major_version .. "." .. efsw_minor_version .. "." .. efsw_patch_version
function get_include_paths()
local function _insert_include_paths( file )
local function _trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local paths = { }
local lines = file:read('*all')
for line in string.gmatch(lines, '([^\n]+)')
do
table.insert( paths, _trim( line ) )
end
file:close()
return paths
end
local file = io.popen( "echo | gcc -Wp,-v -x c++ - -fsyntax-only 2>&1 | grep -v '#' | grep '/'", 'r' )
local include_paths = _insert_include_paths( file )
if next(include_paths) == nil then
file = io.popen( "echo | clang++ -Wp,-v -x c++ - -fsyntax-only 2>&1 | grep -v '#' | grep '/' | grep -v 'nonexistent'", 'r' )
include_paths = _insert_include_paths( file )
if next(include_paths) == nil then
table.insert( include_paths, "/usr/include" )
table.insert( include_paths, "/usr/local/include" )
end
end
return include_paths
end
function inotify_header_exists()
local efsw_include_paths = get_include_paths()
for _,v in pairs( efsw_include_paths )
do
local cur_path = v .. "/sys/inotify.h"
if os.isfile( cur_path ) then
return true
end
end
return false
end
function string.starts(String,Start)
if ( _ACTION ) then
return string.sub(String,1,string.len(Start))==Start
end
return false
end
function is_vs()
return ( string.starts(_ACTION,"vs") )
end
function conf_warnings()
if not is_vs() then
buildoptions{ "-Wall -Wno-long-long" }
if not os.istarget("windows") then
buildoptions{ "-fPIC" }
end
else
defines { "_SCL_SECURE_NO_WARNINGS" }
end
if _OPTIONS["thread-sanitizer"] then
buildoptions { "-fsanitize=thread" }
linkoptions { "-fsanitize=thread" }
if not os.istarget("macosx") then
links { "tsan" }
end
end
end
function conf_links()
if not os.istarget("windows") and not os.istarget("haiku") then
links { "pthread" }
end
if os.istarget("macosx") then
links { "CoreFoundation.framework", "CoreServices.framework" }
end
end
function conf_excludes()
if os.istarget("windows") then
excludes { "src/efsw/WatcherKqueue.cpp", "src/efsw/WatcherFSEvents.cpp", "src/efsw/WatcherInotify.cpp", "src/efsw/FileWatcherKqueue.cpp", "src/efsw/FileWatcherInotify.cpp", "src/efsw/FileWatcherFSEvents.cpp" }
elseif os.istarget("linux") then
excludes { "src/efsw/WatcherKqueue.cpp", "src/efsw/WatcherFSEvents.cpp", "src/efsw/WatcherWin32.cpp", "src/efsw/FileWatcherKqueue.cpp", "src/efsw/FileWatcherWin32.cpp", "src/efsw/FileWatcherFSEvents.cpp" }
elseif os.istarget("macosx") then
excludes { "src/efsw/WatcherInotify.cpp", "src/efsw/WatcherWin32.cpp", "src/efsw/FileWatcherInotify.cpp", "src/efsw/FileWatcherWin32.cpp" }
elseif os.istarget("bsd") then
excludes { "src/efsw/WatcherInotify.cpp", "src/efsw/WatcherWin32.cpp", "src/efsw/WatcherFSEvents.cpp", "src/efsw/FileWatcherInotify.cpp", "src/efsw/FileWatcherWin32.cpp", "src/efsw/FileWatcherFSEvents.cpp" }
end
if os.istarget("linux") and not inotify_header_exists() then
defines { "EFSW_INOTIFY_NOSYS" }
end
end
workspace "efsw"
location("./make/" .. os.target() .. "/")
targetdir("./bin")
configurations { "debug", "release", "relwithdbginfo" }
platforms { "x86_64", "x86", "ARM", "ARM64" }
-- GLOBAL CONFIGURATION (Applies to ALL projects below)
filter "configurations:debug"
defines { "DEBUG" }
symbols "On"
runtime "Debug" -- This forces /MDd everywhere
filter "configurations:release"
defines { "NDEBUG" }
optimize "On"
runtime "Release" -- This forces /MD everywhere
filter "configurations:relwithdbginfo"
defines { "NDEBUG" }
symbols "On"
optimize "On"
runtime "Release"
-- Reset filter so project-specific settings work
filter "*"
if os.istarget("windows") then
osfiles = "src/efsw/platform/win/*.cpp"
else
osfiles = "src/efsw/platform/posix/*.cpp"
end
-- Activates verbose mode
if _OPTIONS["verbose"] then
defines { "EFSW_VERBOSE" }
end
cppdialect "C++11"
objdir("obj/" .. os.target() .. "/")
filter "platforms:x86"
architecture "x86"
filter "platforms:x86_64"
architecture "x86_64"
filter "platforms:arm"
architecture "ARM"
filter "platforms:arm64"
architecture "ARM64"
project "efsw-static-lib"
kind "StaticLib"
language "C++"
targetdir("./lib")
includedirs { "include", "src" }
files { "src/efsw/*.cpp", osfiles }
conf_excludes()
project "efsw-test"
kind "ConsoleApp"
language "C++"
links { "efsw-static-lib" }
files { "src/test/efsw-test.cpp" }
includedirs { "include", "src" }
conf_links()
project "joomer-efsw-file-monitoring"
kind "ConsoleApp"
language "C++"
links { "efsw-static-lib" }
files { "src/joomer-efsw-file-monitoring.cpp" }
includedirs { "include", "src" }
conf_links()
project "efsw-test-stdc"
kind "ConsoleApp"
language "C"
links { "efsw-shared-lib" }
files { "src/test/*.c" }
includedirs { "include", "src" }
conf_links()
project "efsw-shared-lib"
kind "SharedLib"
language "C++"
targetdir("./lib")
includedirs { "include", "src" }
files { "src/efsw/*.cpp", osfiles }
defines { "EFSW_DYNAMIC", "EFSW_EXPORTS" }
conf_excludes()
conf_links()
if os.istarget("linux") or os.istarget("bsd") or os.istarget("haiku") then
targetextension ( ".so." .. efsw_version )
postbuildcommands { "sh ../../project/build.reldbginfo.sh " .. efsw_major_version .. " " .. efsw_minor_version .. " " .. efsw_patch_version .. " " .. iif( _OPTIONS["strip-symbols"], "strip-symbols", "" ) }
end

View File

@ -0,0 +1,9 @@
#!/bin/sh
cd ../../lib
ln -fs libefsw.so.$1.$2.$3 libefsw.so.$1
ln -fs libefsw.so.$1 libefsw.so
if [ "$4" == "strip-symbols" ]; then
objcopy --only-keep-debug libefsw.so.$1.$2.$3 libefsw.debug
objcopy --strip-debug libefsw.so.$1.$2.$3
fi

View File

@ -0,0 +1 @@
-std=c11

View File

@ -0,0 +1 @@
// ADD PREDEFINED MACROS HERE!

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,344 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 10.0.2, 2023-07-05T00:08:16. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{d43f4693-30c1-436c-b1d1-498aab2c2f8c}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.2">
<value type="QString" key="language">Nim</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">NimGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">3</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.BuildSystem</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">false</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">false</value>
<value type="QString" key="ClangTools.DiagnosticConfig"></value>
<value type="int" key="ClangTools.ParallelJobs">0</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">false</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
<valuemap type="QVariantMap" key="CppEditor.QuickFix">
<value type="bool" key="UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{6d057187-158a-4883-8d5b-d470a6b6b025}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/linux</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">gmake2</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake5</value>
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-j24 -e config=release_x86_64</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand">make</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-e config=release_x86_64</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/linux</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--thread-sanitizer --verbose gmake2</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake5</value>
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-j24</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand">make</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-e config=debug_x86_64</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/linux</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">gmake2</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake5</value>
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-e config=relwithdbginfo_x86_64</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand">make</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-e config=relwithdbginfo_x86_64</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">relwithdbginfo</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">0</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%{buildDir}/../../bin/efsw-test-debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="QString" key="RunConfiguration.Arguments">/home/programming/thebricks/fe/</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseTerminal">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory">%{buildDir}../../../</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.1">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">0</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%{buildDir}../../../bin/efsw-test-release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseTerminal">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory">%{buildDir}../../../</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.2">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">0</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%{buildDir}../../../bin/efsw-test-dbginfo</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">reldbginfo</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseTerminal">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory">%{buildDir}../../../</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">3</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1 @@
-std=c++11

View File

@ -0,0 +1,118 @@
../../CMakeLists.txt
../../include/efsw/efsw.hpp
../../premake5.lua
../../src/efsw/Atomic.hpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/Thread.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/System.cpp
../../src/efsw/platform/platformimpl.hpp
../../src/efsw/platform/posix/ThreadImpl.hpp
../../src/efsw/platform/posix/MutexImpl.hpp
../../src/efsw/platform/posix/SystemImpl.hpp
../../src/efsw/platform/posix/ThreadImpl.cpp
../../src/efsw/platform/posix/MutexImpl.cpp
../../src/efsw/platform/posix/SystemImpl.cpp
../../src/efsw/platform/win/ThreadImpl.hpp
../../src/efsw/platform/win/MutexImpl.hpp
../../src/efsw/platform/win/SystemImpl.hpp
../../src/efsw/platform/win/ThreadImpl.cpp
../../src/efsw/platform/win/MutexImpl.cpp
../../src/efsw/platform/win/SystemImpl.cpp
../../src/efsw/base.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/platform/posix/FileSystemImpl.hpp
../../src/efsw/platform/posix/FileSystemImpl.cpp
../../src/efsw/platform/win/FileSystemImpl.hpp
../../src/efsw/platform/win/FileSystemImpl.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/base.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/sophist.h
../../src/efsw/base.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Utf.inl
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/test/efsw-test.cpp
../../premake4.lua
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/Debug.hpp
../../src/efsw/Debug.cpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/Log.cpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/FileWatcherImpl.cpp
../../src/efsw/DirectorySnapshot.hpp
../../src/efsw/DirectorySnapshot.cpp
../../src/efsw/DirectorySnapshotDiff.hpp
../../src/efsw/DirectorySnapshotDiff.cpp
../../src/efsw/WatcherFSEvents.hpp
../../src/efsw/FileWatcherFSEvents.hpp
../../src/efsw/WatcherFSEvents.cpp
../../src/efsw/FileWatcherFSEvents.cpp
../../src/efsw/Watcher.hpp
../../src/efsw/Watcher.cpp
../../src/efsw/WatcherWin32.hpp
../../src/efsw/WatcherWin32.cpp
../../README.md
../../include/efsw/efsw.h
../../src/efsw/FileWatcherCWrapper.cpp
../../src/efsw/inotify-nosys.h

View File

@ -0,0 +1,2 @@
../../include
../../src

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,3 @@
// ADD PREDEFINED MACROS HERE!
#define EFSW_FSEVENTS_SUPPORTED
#define EFSW_USE_CXX11

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 12.0.1, 2024-09-03T01:51:40. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{49267ae2-f136-4b84-8041-cf11a20f6a32}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">true</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">4</value>
<value type="bool" key="ClangTools.PreferConfigFile">false</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop (arm-darwin-generic-mach_o-64bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop (arm-darwin-generic-mach_o-64bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{6d6b6d62-1e99-4e76-b5e2-cf731a0dbd92}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/macosx/</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--file=../../premake4.lua --thread-sanitizer --verbose gmake</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Command">/usr/local/bin/premake4</value>
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-j4</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/macosx/</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--file=../../premake4.lua gmake</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Command">/usr/local/bin/premake4</value>
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">-j4 config=release</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments">config=release</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="qlonglong" key="Analyzer.QmlProfiler.FlushInterval">0</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">/Users/prognoz/programming/efsw/bin/efsw-test-debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="int" key="RunConfiguration.UseCppDebugger">0</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="int" key="RunConfiguration.UseQmlDebugger">1</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseTerminal">false</value>
<value type="QString" key="RunConfiguration.WorkingDirectory">%{buildDir}/../../bin</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.1">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="qlonglong" key="Analyzer.QmlProfiler.FlushInterval">0</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">efsw-test</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="int" key="RunConfiguration.UseCppDebugger">1</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseTerminal">false</value>
<value type="QString" key="RunConfiguration.WorkingDirectory">%{buildDir}/../../bin/</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">2</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,185 @@
../../include/efsw/efsw.hpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/Thread.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/System.cpp
../../src/efsw/platform/platformimpl.hpp
../../src/efsw/platform/posix/ThreadImpl.hpp
../../src/efsw/platform/posix/MutexImpl.hpp
../../src/efsw/platform/posix/SystemImpl.hpp
../../src/efsw/platform/posix/ThreadImpl.cpp
../../src/efsw/platform/posix/MutexImpl.cpp
../../src/efsw/platform/posix/SystemImpl.cpp
../../src/efsw/platform/win/ThreadImpl.hpp
../../src/efsw/platform/win/MutexImpl.hpp
../../src/efsw/platform/win/SystemImpl.hpp
../../src/efsw/platform/win/ThreadImpl.cpp
../../src/efsw/platform/win/MutexImpl.cpp
../../src/efsw/platform/win/SystemImpl.cpp
../../src/efsw/base.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/platform/posix/FileSystemImpl.hpp
../../src/efsw/platform/posix/FileSystemImpl.cpp
../../src/efsw/platform/win/FileSystemImpl.hpp
../../src/efsw/platform/win/FileSystemImpl.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/base.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/sophist.h
../../src/efsw/base.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Utf.inl
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/test/efsw-test.cpp
../../premake4.lua
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/sophist.h
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/Utf.inl
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/Debug.cpp
../../src/efsw/FileWatcherImpl.cpp
../../src/efsw/DirectorySnapshotDiff.hpp
../../src/efsw/DirectorySnapshot.hpp
../../src/efsw/DirectorySnapshotDiff.cpp
../../src/efsw/DirectorySnapshot.cpp
../../src/efsw/WatcherFSEvents.hpp
../../src/efsw/FileWatcherFSEvents.hpp
../../src/efsw/WatcherFSEvents.cpp
../../src/efsw/FileWatcherFSEvents.cpp
../../src/efsw/WatcherWin32.hpp
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/WatcherFSEvents.hpp
../../src/efsw/Watcher.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/sophist.h
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileWatcherFSEvents.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/DirectorySnapshotDiff.hpp
../../src/efsw/DirectorySnapshot.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/WatcherWin32.cpp
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/WatcherFSEvents.cpp
../../src/efsw/Watcher.cpp
../../src/efsw/Utf.inl
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherImpl.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcherFSEvents.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/DirectorySnapshotDiff.cpp
../../src/efsw/DirectorySnapshot.cpp
../../src/efsw/Debug.cpp

View File

@ -0,0 +1,7 @@
../../src
../../include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/include
/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1 @@
// ADD PREDEFINED MACROS HERE!

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,425 @@
<<<<<<< HEAD
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.10.2, 2019-11-10T01:12:11. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{55fc4913-4acc-49e6-b0d5-ebf25d4d498e}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{eb5b6178-a7a7-439e-ab01-e63b057196a1}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">C:\programming\efsw\make\windows</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">true</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%{buildDir}\..\..\bin\efsw-test-debug.exe</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run C:\programming\efsw\bin\efsw-test-debug.exe</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory">%{buildDir}</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>
=======
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.10.2, 2019-11-10T01:12:11. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{55fc4913-4acc-49e6-b0d5-ebf25d4d498e}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{eb5b6178-a7a7-439e-ab01-e63b057196a1}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">C:\programming\efsw\make\windows</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">true</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%{buildDir}\..\..\bin\efsw-test-debug.exe</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run C:\programming\efsw\bin\efsw-test-debug.exe</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory">%{buildDir}</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>
>>>>>>> 0adf96dcdb6ba38e789d409ca1bf1fc149e60808

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,433 @@
<<<<<<< HEAD
../../include/efsw/efsw.hpp
../../premake5.lua
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/Thread.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/System.cpp
../../src/efsw/platform/platformimpl.hpp
../../src/efsw/platform/posix/ThreadImpl.hpp
../../src/efsw/platform/posix/MutexImpl.hpp
../../src/efsw/platform/posix/SystemImpl.hpp
../../src/efsw/platform/posix/ThreadImpl.cpp
../../src/efsw/platform/posix/MutexImpl.cpp
../../src/efsw/platform/posix/SystemImpl.cpp
../../src/efsw/platform/win/ThreadImpl.hpp
../../src/efsw/platform/win/MutexImpl.hpp
../../src/efsw/platform/win/SystemImpl.hpp
../../src/efsw/platform/win/ThreadImpl.cpp
../../src/efsw/platform/win/MutexImpl.cpp
../../src/efsw/platform/win/SystemImpl.cpp
../../src/efsw/base.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/platform/posix/FileSystemImpl.hpp
../../src/efsw/platform/posix/FileSystemImpl.cpp
../../src/efsw/platform/win/FileSystemImpl.hpp
../../src/efsw/platform/win/FileSystemImpl.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/base.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/sophist.h
../../src/efsw/base.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Utf.inl
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/test/efsw-test.cpp
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/sophist.h
../../src/efsw/Utf.inl
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/Debug.cpp
../../premake4.lua
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/sophist.h
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/Utf.inl
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherImpl.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/Debug.cpp
../../src/efsw/WatcherWin32.hpp
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/WatcherFSEvents.hpp
../../src/efsw/Watcher.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/sophist.h
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileWatcherFSEvents.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/DirectorySnapshotDiff.hpp
../../src/efsw/DirectorySnapshot.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/Utf.inl
../../src/efsw/WatcherWin32.cpp
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/WatcherFSEvents.cpp
../../src/efsw/Watcher.cpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherImpl.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcherFSEvents.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/DirectorySnapshotDiff.cpp
../../src/efsw/DirectorySnapshot.cpp
../../src/efsw/Debug.cpp
../../include/efsw/efsw.h
../../src/efsw/FileWatcherCWrapper.cpp
=======
../../include/efsw/efsw.hpp
../../premake5.lua
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/Thread.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/System.cpp
../../src/efsw/platform/platformimpl.hpp
../../src/efsw/platform/posix/ThreadImpl.hpp
../../src/efsw/platform/posix/MutexImpl.hpp
../../src/efsw/platform/posix/SystemImpl.hpp
../../src/efsw/platform/posix/ThreadImpl.cpp
../../src/efsw/platform/posix/MutexImpl.cpp
../../src/efsw/platform/posix/SystemImpl.cpp
../../src/efsw/platform/win/ThreadImpl.hpp
../../src/efsw/platform/win/MutexImpl.hpp
../../src/efsw/platform/win/SystemImpl.hpp
../../src/efsw/platform/win/ThreadImpl.cpp
../../src/efsw/platform/win/MutexImpl.cpp
../../src/efsw/platform/win/SystemImpl.cpp
../../src/efsw/base.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/platform/posix/FileSystemImpl.hpp
../../src/efsw/platform/posix/FileSystemImpl.cpp
../../src/efsw/platform/win/FileSystemImpl.hpp
../../src/efsw/platform/win/FileSystemImpl.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/base.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/sophist.h
../../src/efsw/base.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/Utf.inl
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/test/efsw-test.cpp
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/sophist.h
../../src/efsw/Utf.inl
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/Debug.cpp
../../premake4.lua
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/sophist.h
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/Utf.inl
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherImpl.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/Debug.cpp
../../src/efsw/WatcherWin32.hpp
../../src/efsw/WatcherKqueue.hpp
../../src/efsw/WatcherInotify.hpp
../../src/efsw/WatcherGeneric.hpp
../../src/efsw/WatcherFSEvents.hpp
../../src/efsw/Watcher.hpp
../../src/efsw/Utf.hpp
../../src/efsw/Thread.hpp
../../src/efsw/System.hpp
../../src/efsw/String.hpp
../../src/efsw/sophist.h
../../src/efsw/Mutex.hpp
../../src/efsw/FileWatcherWin32.hpp
../../src/efsw/FileWatcherKqueue.hpp
../../src/efsw/FileWatcherInotify.hpp
../../src/efsw/FileWatcherImpl.hpp
../../src/efsw/FileWatcherGeneric.hpp
../../src/efsw/FileWatcherFSEvents.hpp
../../src/efsw/FileSystem.hpp
../../src/efsw/FileInfo.hpp
../../src/efsw/DirWatcherGeneric.hpp
../../src/efsw/DirectorySnapshotDiff.hpp
../../src/efsw/DirectorySnapshot.hpp
../../src/efsw/Debug.hpp
../../src/efsw/base.hpp
../../src/efsw/Utf.inl
../../src/efsw/WatcherWin32.cpp
../../src/efsw/WatcherKqueue.cpp
../../src/efsw/WatcherInotify.cpp
../../src/efsw/WatcherGeneric.cpp
../../src/efsw/WatcherFSEvents.cpp
../../src/efsw/Watcher.cpp
../../src/efsw/Thread.cpp
../../src/efsw/System.cpp
../../src/efsw/String.cpp
../../src/efsw/Mutex.cpp
../../src/efsw/Log.cpp
../../src/efsw/FileWatcherWin32.cpp
../../src/efsw/FileWatcherKqueue.cpp
../../src/efsw/FileWatcherInotify.cpp
../../src/efsw/FileWatcherImpl.cpp
../../src/efsw/FileWatcherGeneric.cpp
../../src/efsw/FileWatcherFSEvents.cpp
../../src/efsw/FileWatcher.cpp
../../src/efsw/FileSystem.cpp
../../src/efsw/FileInfo.cpp
../../src/efsw/DirWatcherGeneric.cpp
../../src/efsw/DirectorySnapshotDiff.cpp
../../src/efsw/DirectorySnapshot.cpp
../../src/efsw/Debug.cpp
../../include/efsw/efsw.h
../../src/efsw/FileWatcherCWrapper.cpp
>>>>>>> 0adf96dcdb6ba38e789d409ca1bf1fc149e60808

View File

@ -0,0 +1,2 @@
../../src
../../include

33
src/efsw/Atomic.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef EFSW_ATOMIC_BOOL_HPP
#define EFSW_ATOMIC_BOOL_HPP
#include <efsw/base.hpp>
#include <atomic>
namespace efsw {
template <typename T> class Atomic {
public:
explicit Atomic( T set = false ) : set_( set ) {}
Atomic& operator=( T set ) {
set_.store( set, std::memory_order_release );
return *this;
}
explicit operator T() const {
return set_.load( std::memory_order_acquire );
}
T load() const {
return set_.load( std::memory_order_acquire );
}
private:
std::atomic<T> set_;
};
} // namespace efsw
#endif

81
src/efsw/Debug.cpp Normal file
View File

@ -0,0 +1,81 @@
#include <efsw/Debug.hpp>
#include <iostream>
#ifdef EFSW_COMPILER_MSVC
#define WIN32_LEAN_AND_MEAN
#include <crtdbg.h>
#include <windows.h>
#endif
#include <cassert>
#include <cstdarg>
#include <cstdio>
namespace efsw {
#ifdef DEBUG
void efREPORT_ASSERT( const char* File, int Line, const char* Exp ) {
#ifdef EFSW_COMPILER_MSVC
_CrtDbgReport( _CRT_ASSERT, File, Line, "", Exp );
DebugBreak();
#else
std::cout << "ASSERT: " << Exp << " file: " << File << " line: " << Line << std::endl;
#if defined( EFSW_COMPILER_GCC ) && defined( EFSW_32BIT ) && !defined( EFSW_ARM )
asm( "int3" );
#else
assert( false );
#endif
#endif
}
void efPRINT( const char* format, ... ) {
char buf[2048];
va_list args;
va_start( args, format );
#ifdef EFSW_COMPILER_MSVC
_vsnprintf_s( buf, sizeof( buf ), sizeof( buf ) / sizeof( buf[0] ), format, args );
#else
vsnprintf( buf, sizeof( buf ) / sizeof( buf[0] ), format, args );
#endif
va_end( args );
#ifdef EFSW_COMPILER_MSVC
OutputDebugStringA( buf );
#else
std::cout << buf;
#endif
}
void efPRINTC( unsigned int cond, const char* format, ... ) {
if ( 0 == cond )
return;
char buf[2048];
va_list args;
va_start( args, format );
#ifdef EFSW_COMPILER_MSVC
_vsnprintf_s( buf, efARRAY_SIZE( buf ), efARRAY_SIZE( buf ), format, args );
#else
vsnprintf( buf, sizeof( buf ) / sizeof( buf[0] ), format, args );
#endif
va_end( args );
#ifdef EFSW_COMPILER_MSVC
OutputDebugStringA( buf );
#else
std::cout << buf;
#endif
}
#endif
} // namespace efsw

62
src/efsw/Debug.hpp Normal file
View File

@ -0,0 +1,62 @@
#ifndef EFSW_DEBUG_HPP
#define EFSW_DEBUG_HPP
#include <efsw/base.hpp>
namespace efsw {
#ifdef DEBUG
void efREPORT_ASSERT( const char* File, const int Line, const char* Exp );
#define efASSERT( expr ) \
if ( !( expr ) ) { \
efREPORT_ASSERT( __FILE__, __LINE__, #expr ); \
}
#define efASSERTM( expr, msg ) \
if ( !( expr ) ) { \
efREPORT_ASSERT( __FILE__, __LINE__, #msg ); \
}
void efPRINT( const char* format, ... );
void efPRINTC( unsigned int cond, const char* format, ... );
#else
#define efASSERT( expr )
#define efASSERTM( expr, msg )
#ifndef EFSW_COMPILER_MSVC
#define efPRINT( format, args... ) \
{}
#define efPRINTC( cond, format, args... ) \
{}
#else
#define efPRINT
#define efPRINTC
#endif
#endif
#ifdef EFSW_VERBOSE
#define efDEBUG efPRINT
#define efDEBUGC efPRINTC
#else
#ifndef EFSW_COMPILER_MSVC
#define efDEBUG( format, args... ) \
{}
#define efDEBUGC( cond, format, args... ) \
{}
#else
#define efDEBUG( ... ) \
{}
#define efDEBUGC( ... ) \
{}
#endif
#endif
} // namespace efsw
#endif

View File

@ -0,0 +1,388 @@
#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

View File

@ -0,0 +1,57 @@
#ifndef EFSW_DIRWATCHERGENERIC_HPP
#define EFSW_DIRWATCHERGENERIC_HPP
#include <efsw/DirectorySnapshot.hpp>
#include <efsw/FileInfo.hpp>
#include <efsw/WatcherGeneric.hpp>
#include <map>
namespace efsw {
class DirWatcherGeneric {
public:
typedef std::map<std::string, DirWatcherGeneric*> DirWatchMap;
DirWatcherGeneric* Parent;
WatcherGeneric* Watch;
DirectorySnapshot DirSnap;
DirWatchMap Directories;
bool Recursive;
DirWatcherGeneric( DirWatcherGeneric* parent, WatcherGeneric* ws, const std::string& directory,
bool recursive, bool reportNewFiles = false );
~DirWatcherGeneric();
void watch( bool reportOwnChange = false );
void watchDir( std::string& dir );
static bool isDir( const std::string& directory );
bool pathInWatches( std::string path );
void addChilds( bool reportNewFiles = true );
DirWatcherGeneric* findDirWatcher( std::string dir );
DirWatcherGeneric* findDirWatcherFast( std::string dir );
protected:
bool Deleted;
DirWatcherGeneric* createDirectory( std::string newdir );
void removeDirectory( std::string dir );
void moveDirectory( std::string oldDir, std::string newDir );
void resetDirectory( std::string directory );
void handleAction( const std::string& filename, unsigned long action,
std::string oldFilename = "" );
};
} // namespace efsw
#endif

View File

@ -0,0 +1,212 @@
#include <efsw/DirectorySnapshot.hpp>
#include <efsw/FileSystem.hpp>
namespace efsw {
DirectorySnapshot::DirectorySnapshot() {}
DirectorySnapshot::DirectorySnapshot( std::string directory ) {
init( directory );
}
DirectorySnapshot::~DirectorySnapshot() {}
void DirectorySnapshot::init( std::string directory ) {
setDirectoryInfo( directory );
initFiles();
}
bool DirectorySnapshot::exists() {
return DirectoryInfo.exists();
}
void DirectorySnapshot::deleteAll( DirectorySnapshotDiff& Diff ) {
FileInfo fi;
for ( FileInfoMap::iterator it = Files.begin(); it != Files.end(); it++ ) {
fi = it->second;
if ( fi.isDirectory() ) {
Diff.DirsDeleted.push_back( fi );
} else {
Diff.FilesDeleted.push_back( fi );
}
}
Files.clear();
}
void DirectorySnapshot::setDirectoryInfo( std::string directory ) {
DirectoryInfo = FileInfo( directory );
}
void DirectorySnapshot::initFiles() {
Files = FileSystem::filesInfoFromPath( DirectoryInfo.Filepath );
FileInfoMap::iterator it = Files.begin();
std::vector<std::string> eraseFiles;
/// Remove all non regular files and non directories
for ( ; it != Files.end(); it++ ) {
if ( !it->second.isRegularFile() && !it->second.isDirectory() ) {
eraseFiles.push_back( it->first );
}
}
for ( std::vector<std::string>::iterator eit = eraseFiles.begin(); eit != eraseFiles.end();
eit++ ) {
Files.erase( *eit );
}
}
DirectorySnapshotDiff DirectorySnapshot::scan() {
DirectorySnapshotDiff Diff;
Diff.clear();
FileInfo curFI( DirectoryInfo.Filepath );
Diff.DirChanged = DirectoryInfo != curFI;
if ( Diff.DirChanged ) {
DirectoryInfo = curFI;
}
/// If the directory was erased, create the events for files and directories deletion
if ( !curFI.exists() ) {
deleteAll( Diff );
return Diff;
}
FileInfoMap files = FileSystem::filesInfoFromPath( DirectoryInfo.Filepath );
if ( files.empty() && Files.empty() ) {
return Diff;
}
FileInfo fi;
FileInfoMap FilesCpy;
FileInfoMap::iterator it;
FileInfoMap::iterator fiIt;
if ( Diff.DirChanged ) {
FilesCpy = Files;
}
for ( it = files.begin(); it != files.end(); it++ ) {
fi = it->second;
/// File existed before?
fiIt = Files.find( it->first );
if ( fiIt != Files.end() ) {
/// Erase from the file list copy
FilesCpy.erase( it->first );
/// File changed?
if ( ( *fiIt ).second != fi ) {
/// Update the new file info
Files[it->first] = fi;
/// handle modified event
if ( fi.isDirectory() ) {
Diff.DirsModified.push_back( fi );
} else {
Diff.FilesModified.push_back( fi );
}
}
}
/// Only add regular files or directories
else if ( fi.isRegularFile() || fi.isDirectory() ) {
/// New file found
Files[it->first] = fi;
FileInfoMap::iterator fit;
std::string oldFile = "";
/// Check if the same inode already existed
if ( ( fit = nodeInFiles( fi ) ) != Files.end() ) {
oldFile = fit->first;
/// Avoid firing a Delete event
FilesCpy.erase( fit->first );
/// Delete the old file name
Files.erase( fit->first );
if ( fi.isDirectory() ) {
Diff.DirsMoved.push_back( std::make_pair( oldFile, fi ) );
} else {
Diff.FilesMoved.push_back( std::make_pair( oldFile, fi ) );
}
} else {
if ( fi.isDirectory() ) {
Diff.DirsCreated.push_back( fi );
} else {
Diff.FilesCreated.push_back( fi );
}
}
}
}
if ( !Diff.DirChanged ) {
return Diff;
}
/// The files or directories that remains were deleted
for ( it = FilesCpy.begin(); it != FilesCpy.end(); it++ ) {
fi = it->second;
if ( fi.isDirectory() ) {
Diff.DirsDeleted.push_back( fi );
} else {
Diff.FilesDeleted.push_back( fi );
}
/// Remove the file or directory from the list of files
Files.erase( it->first );
}
return Diff;
}
FileInfoMap::iterator DirectorySnapshot::nodeInFiles( FileInfo& fi ) {
FileInfoMap::iterator it;
if ( FileInfo::inodeSupported() ) {
for ( it = Files.begin(); it != Files.end(); it++ ) {
if ( it->second.sameInode( fi ) && it->second.Filepath != fi.Filepath ) {
return it;
}
}
}
return Files.end();
}
void DirectorySnapshot::addFile( std::string path ) {
std::string name( FileSystem::fileNameFromPath( path ) );
Files[name] = FileInfo( path );
}
void DirectorySnapshot::removeFile( std::string path ) {
std::string name( FileSystem::fileNameFromPath( path ) );
FileInfoMap::iterator it = Files.find( name );
if ( Files.end() != it ) {
Files.erase( it );
}
}
void DirectorySnapshot::moveFile( std::string oldPath, std::string newPath ) {
removeFile( oldPath );
addFile( newPath );
}
void DirectorySnapshot::updateFile( std::string path ) {
addFile( path );
}
} // namespace efsw

View File

@ -0,0 +1,45 @@
#ifndef EFSW_DIRECTORYSNAPSHOT_HPP
#define EFSW_DIRECTORYSNAPSHOT_HPP
#include <efsw/DirectorySnapshotDiff.hpp>
namespace efsw {
class DirectorySnapshot {
public:
FileInfo DirectoryInfo;
FileInfoMap Files;
void setDirectoryInfo( std::string directory );
DirectorySnapshot();
DirectorySnapshot( std::string directory );
~DirectorySnapshot();
void init( std::string directory );
bool exists();
DirectorySnapshotDiff scan();
FileInfoMap::iterator nodeInFiles( FileInfo& fi );
void addFile( std::string path );
void removeFile( std::string path );
void moveFile( std::string oldPath, std::string newPath );
void updateFile( std::string path );
protected:
void initFiles();
void deleteAll( DirectorySnapshotDiff& Diff );
};
} // namespace efsw
#endif

View File

@ -0,0 +1,22 @@
#include <efsw/DirectorySnapshotDiff.hpp>
namespace efsw {
void DirectorySnapshotDiff::clear() {
FilesCreated.clear();
FilesModified.clear();
FilesMoved.clear();
FilesDeleted.clear();
DirsCreated.clear();
DirsModified.clear();
DirsMoved.clear();
DirsDeleted.clear();
}
bool DirectorySnapshotDiff::changed() {
return !FilesCreated.empty() || !FilesModified.empty() || !FilesMoved.empty() ||
!FilesDeleted.empty() || !DirsCreated.empty() || !DirsModified.empty() ||
!DirsMoved.empty() || !DirsDeleted.empty();
}
} // namespace efsw

View File

@ -0,0 +1,35 @@
#ifndef EFSW_DIRECTORYSNAPSHOTDIFF_HPP
#define EFSW_DIRECTORYSNAPSHOTDIFF_HPP
#include <efsw/FileInfo.hpp>
namespace efsw {
class DirectorySnapshotDiff {
public:
FileInfoList FilesDeleted;
FileInfoList FilesCreated;
FileInfoList FilesModified;
MovedList FilesMoved;
FileInfoList DirsDeleted;
FileInfoList DirsCreated;
FileInfoList DirsModified;
MovedList DirsMoved;
bool DirChanged;
void clear();
bool changed();
};
#define DiffIterator( FileInfoListName ) \
it = Diff.FileInfoListName.begin(); \
for ( ; it != Diff.FileInfoListName.end(); it++ )
#define DiffMovedIterator( MovedListName ) \
mit = Diff.MovedListName.begin(); \
for ( ; mit != Diff.MovedListName.end(); mit++ )
} // namespace efsw
#endif

240
src/efsw/FileInfo.cpp Normal file
View File

@ -0,0 +1,240 @@
#include <efsw/FileInfo.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/String.hpp>
#ifndef _DARWIN_FEATURE_64_BIT_INODE
#define _DARWIN_FEATURE_64_BIT_INODE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include <sys/stat.h>
#include <limits.h>
#include <stdlib.h>
#ifdef EFSW_COMPILER_MSVC
#ifndef S_ISDIR
#define S_ISDIR( f ) ( (f)&_S_IFDIR )
#endif
#ifndef S_ISREG
#define S_ISREG( f ) ( (f)&_S_IFREG )
#endif
#ifndef S_ISRDBL
#define S_ISRDBL( f ) ( (f)&_S_IREAD )
#endif
#else
#include <unistd.h>
#ifndef S_ISRDBL
#define S_ISRDBL( f ) ( (f)&S_IRUSR )
#endif
#endif
namespace efsw {
bool FileInfo::exists( const std::string& filePath ) {
FileInfo fi( filePath );
return fi.exists();
}
bool FileInfo::isLink( const std::string& filePath ) {
FileInfo fi( filePath, true );
return fi.isLink();
}
bool FileInfo::inodeSupported() {
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
return true;
#else
return false;
#endif
}
FileInfo::FileInfo() :
ModificationTime( 0 ), OwnerId( 0 ), GroupId( 0 ), Permissions( 0 ), Inode( 0 ) {}
FileInfo::FileInfo( const std::string& filepath ) :
Filepath( filepath ),
ModificationTime( 0 ),
OwnerId( 0 ),
GroupId( 0 ),
Permissions( 0 ),
Inode( 0 ) {
getInfo();
}
FileInfo::FileInfo( const std::string& filepath, bool linkInfo ) :
Filepath( filepath ),
ModificationTime( 0 ),
OwnerId( 0 ),
GroupId( 0 ),
Permissions( 0 ),
Inode( 0 ) {
if ( linkInfo ) {
getRealInfo();
} else {
getInfo();
}
}
void FileInfo::getInfo() {
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
if ( Filepath.size() == 3 && Filepath[1] == ':' && Filepath[2] == FileSystem::getOSSlash() ) {
Filepath += FileSystem::getOSSlash();
}
#endif
/// Why i'm doing this? stat in mingw32 doesn't work for directories if the dir path ends with a
/// path slash
bool slashAtEnd = FileSystem::slashAtEnd( Filepath );
if ( slashAtEnd ) {
FileSystem::dirRemoveSlashAtEnd( Filepath );
}
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
struct stat st;
int res = stat( Filepath.c_str(), &st );
#else
struct _stat st;
int res = _wstat( String::fromUtf8( Filepath ).toWideString().c_str(), &st );
#endif
if ( 0 == res ) {
ModificationTime = st.st_mtime;
Size = st.st_size;
OwnerId = st.st_uid;
GroupId = st.st_gid;
Permissions = st.st_mode;
Inode = st.st_ino;
}
if ( slashAtEnd ) {
FileSystem::dirAddSlashAtEnd( Filepath );
}
}
void FileInfo::getRealInfo() {
bool slashAtEnd = FileSystem::slashAtEnd( Filepath );
if ( slashAtEnd ) {
FileSystem::dirRemoveSlashAtEnd( Filepath );
}
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
struct stat st;
int res = lstat( Filepath.c_str(), &st );
#else
struct _stat st;
int res = _wstat( String::fromUtf8( Filepath ).toWideString().c_str(), &st );
#endif
if ( 0 == res ) {
ModificationTime = st.st_mtime;
Size = st.st_size;
OwnerId = st.st_uid;
GroupId = st.st_gid;
Permissions = st.st_mode;
Inode = st.st_ino;
}
if ( slashAtEnd ) {
FileSystem::dirAddSlashAtEnd( Filepath );
}
}
bool FileInfo::operator==( const FileInfo& Other ) const {
return ( ModificationTime == Other.ModificationTime && Size == Other.Size &&
OwnerId == Other.OwnerId && GroupId == Other.GroupId &&
Permissions == Other.Permissions && Inode == Other.Inode );
}
bool FileInfo::isDirectory() const {
return 0 != S_ISDIR( Permissions );
}
bool FileInfo::isRegularFile() const {
return 0 != S_ISREG( Permissions );
}
bool FileInfo::isReadable() const {
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
static bool isRoot = getuid() == 0;
return isRoot || 0 != S_ISRDBL( Permissions );
#else
return 0 != S_ISRDBL( Permissions );
#endif
}
bool FileInfo::isLink() const {
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
return S_ISLNK( Permissions );
#else
return false;
#endif
}
std::string FileInfo::linksTo() {
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
if ( isLink() ) {
char* ch = realpath( Filepath.c_str(), NULL );
if ( NULL != ch ) {
std::string tstr( ch );
free( ch );
return tstr;
}
}
#endif
return std::string( "" );
}
bool FileInfo::exists() {
bool slashAtEnd = FileSystem::slashAtEnd( Filepath );
if ( slashAtEnd ) {
FileSystem::dirRemoveSlashAtEnd( Filepath );
}
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
struct stat st;
int res = stat( Filepath.c_str(), &st );
#else
struct _stat st;
int res = _wstat( String::fromUtf8( Filepath ).toWideString().c_str(), &st );
#endif
if ( slashAtEnd ) {
FileSystem::dirAddSlashAtEnd( Filepath );
}
return 0 == res;
}
FileInfo& FileInfo::operator=( const FileInfo& Other ) {
this->Filepath = Other.Filepath;
this->Size = Other.Size;
this->ModificationTime = Other.ModificationTime;
this->GroupId = Other.GroupId;
this->OwnerId = Other.OwnerId;
this->Permissions = Other.Permissions;
this->Inode = Other.Inode;
return *this;
}
bool FileInfo::sameInode( const FileInfo& Other ) const {
return inodeSupported() && Inode == Other.Inode;
}
bool FileInfo::operator!=( const FileInfo& Other ) const {
return !( *this == Other );
}
} // namespace efsw

64
src/efsw/FileInfo.hpp Normal file
View File

@ -0,0 +1,64 @@
#ifndef EFSW_FILEINFO_HPP
#define EFSW_FILEINFO_HPP
#include <efsw/base.hpp>
#include <vector>
#include <map>
#include <string>
namespace efsw {
class FileInfo {
public:
static bool exists( const std::string& filePath );
static bool isLink( const std::string& filePath );
static bool inodeSupported();
FileInfo();
FileInfo( const std::string& filepath );
FileInfo( const std::string& filepath, bool linkInfo );
bool operator==( const FileInfo& Other ) const;
bool operator!=( const FileInfo& Other ) const;
FileInfo& operator=( const FileInfo& Other );
bool isDirectory() const;
bool isRegularFile() const;
bool isReadable() const;
bool sameInode( const FileInfo& Other ) const;
bool isLink() const;
std::string linksTo();
bool exists();
void getInfo();
void getRealInfo();
std::string Filepath;
Uint64 ModificationTime;
Uint64 Size;
Uint32 OwnerId;
Uint32 GroupId;
Uint32 Permissions;
Uint64 Inode;
};
typedef std::map<std::string, FileInfo> FileInfoMap;
typedef std::vector<FileInfo> FileInfoList;
typedef std::vector<std::pair<std::string, FileInfo>> MovedList;
} // namespace efsw
#endif

161
src/efsw/FileSystem.cpp Normal file
View File

@ -0,0 +1,161 @@
#include <cstring>
#include <efsw/FileSystem.hpp>
#include <efsw/platform/platformimpl.hpp>
#include <climits>
#if EFSW_OS == EFSW_OS_MACOSX
#include <CoreFoundation/CoreFoundation.h>
#endif
#if EFSW_OS == EFSW_OS_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
namespace efsw {
bool FileSystem::isDirectory( const std::string& path ) {
return Platform::FileSystem::isDirectory( path );
}
FileInfoMap FileSystem::filesInfoFromPath( std::string path ) {
dirAddSlashAtEnd( path );
return Platform::FileSystem::filesInfoFromPath( path );
}
char FileSystem::getOSSlash() {
return Platform::FileSystem::getOSSlash();
}
bool FileSystem::slashAtEnd( std::string& dir ) {
return ( dir.size() && dir[dir.size() - 1] == getOSSlash() );
}
void FileSystem::dirAddSlashAtEnd( std::string& dir ) {
if ( dir.size() >= 1 && dir[dir.size() - 1] != getOSSlash() ) {
dir.push_back( getOSSlash() );
}
}
void FileSystem::dirRemoveSlashAtEnd( std::string& dir ) {
if ( dir.size() >= 1 && dir[dir.size() - 1] == getOSSlash() ) {
dir.erase( dir.size() - 1 );
}
}
std::string FileSystem::fileNameFromPath( std::string filepath ) {
dirRemoveSlashAtEnd( filepath );
size_t pos = filepath.find_last_of( getOSSlash() );
if ( pos != std::string::npos ) {
return filepath.substr( pos + 1 );
}
return filepath;
}
std::string FileSystem::pathRemoveFileName( std::string filepath ) {
dirRemoveSlashAtEnd( filepath );
size_t pos = filepath.find_last_of( getOSSlash() );
if ( pos != std::string::npos ) {
return filepath.substr( 0, pos + 1 );
}
return filepath;
}
std::string FileSystem::getLinkRealPath( std::string dir, std::string& curPath ) {
FileSystem::dirRemoveSlashAtEnd( dir );
FileInfo fi( dir, true );
/// Check with lstat and see if it's a link
if ( fi.isLink() ) {
/// get the real path of the link
std::string link( fi.linksTo() );
/// get the current path of the directory without the link dir path
curPath = FileSystem::pathRemoveFileName( dir );
/// ensure that ends with the os directory slash
FileSystem::dirAddSlashAtEnd( link );
return link;
}
/// if it's not a link return nothing
return "";
}
std::string FileSystem::precomposeFileName( const std::string& name ) {
#if EFSW_OS == EFSW_OS_MACOSX
CFStringRef cfStringRef =
CFStringCreateWithCString( kCFAllocatorDefault, name.c_str(), kCFStringEncodingUTF8 );
CFMutableStringRef cfMutable = CFStringCreateMutableCopy( NULL, 0, cfStringRef );
CFStringNormalize( cfMutable, kCFStringNormalizationFormC );
const char* c_str = CFStringGetCStringPtr( cfMutable, kCFStringEncodingUTF8 );
if ( c_str != NULL ) {
std::string result( c_str );
CFRelease( cfStringRef );
CFRelease( cfMutable );
return result;
}
CFIndex length = CFStringGetLength( cfMutable );
CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 );
if ( maxSize == kCFNotFound ) {
CFRelease( cfStringRef );
CFRelease( cfMutable );
return std::string();
}
std::string result( maxSize + 1, '\0' );
if ( CFStringGetCString( cfMutable, &result[0], result.size(), kCFStringEncodingUTF8 ) ) {
result.resize( std::strlen( result.c_str() ) );
CFRelease( cfStringRef );
CFRelease( cfMutable );
} else {
result.clear();
}
return result;
#else
return name;
#endif
}
bool FileSystem::isRemoteFS( const std::string& directory ) {
return Platform::FileSystem::isRemoteFS( directory );
}
bool FileSystem::changeWorkingDirectory( const std::string& directory ) {
return Platform::FileSystem::changeWorkingDirectory( directory );
}
std::string FileSystem::getCurrentWorkingDirectory() {
return Platform::FileSystem::getCurrentWorkingDirectory();
}
std::string FileSystem::getRealPath( const std::string& path ) {
std::string realPath;
#if defined( EFSW_PLATFORM_POSIX )
char dir[PATH_MAX];
realpath( path.c_str(), &dir[0] );
realPath = std::string( dir );
#elif EFSW_OS == EFSW_OS_WIN
wchar_t dir[_MAX_PATH + 1];
GetFullPathNameW( String::fromUtf8( path ).toWideString().c_str(), _MAX_PATH, &dir[0],
nullptr );
realPath = String( dir ).toUtf8();
#else
#warning FileSystem::getRealPath() not implemented on this platform.
#endif
return realPath;
}
} // namespace efsw

43
src/efsw/FileSystem.hpp Normal file
View File

@ -0,0 +1,43 @@
#ifndef EFSW_FILESYSTEM_HPP
#define EFSW_FILESYSTEM_HPP
#include <efsw/FileInfo.hpp>
#include <efsw/base.hpp>
namespace efsw {
class FileSystem {
public:
static bool isDirectory( const std::string& path );
static FileInfoMap filesInfoFromPath( std::string path );
static char getOSSlash();
static bool slashAtEnd( std::string& dir );
static void dirAddSlashAtEnd( std::string& dir );
static void dirRemoveSlashAtEnd( std::string& dir );
static std::string fileNameFromPath( std::string filepath );
static std::string pathRemoveFileName( std::string filepath );
static std::string getLinkRealPath( std::string dir, std::string& curPath );
static std::string precomposeFileName( const std::string& name );
static bool isRemoteFS( const std::string& directory );
static bool changeWorkingDirectory( const std::string& path );
static std::string getCurrentWorkingDirectory();
static std::string getRealPath( const std::string& path );
};
} // namespace efsw
#endif

120
src/efsw/FileWatcher.cpp Normal file
View File

@ -0,0 +1,120 @@
#include <efsw/FileSystem.hpp>
#include <efsw/FileWatcherGeneric.hpp>
#include <efsw/FileWatcherImpl.hpp>
#include <efsw/efsw.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <efsw/FileWatcherWin32.hpp>
#define FILEWATCHER_IMPL FileWatcherWin32
#define BACKEND_NAME "Win32"
#elif EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY
#include <efsw/FileWatcherInotify.hpp>
#define FILEWATCHER_IMPL FileWatcherInotify
#define BACKEND_NAME "Inotify"
#elif EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE
#include <efsw/FileWatcherKqueue.hpp>
#define FILEWATCHER_IMPL FileWatcherKqueue
#define BACKEND_NAME "Kqueue"
#elif EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <efsw/FileWatcherFSEvents.hpp>
#define FILEWATCHER_IMPL FileWatcherFSEvents
#define BACKEND_NAME "FSEvents"
#else
#define FILEWATCHER_IMPL FileWatcherGeneric
#define BACKEND_NAME "Generic"
#endif
#include <efsw/Debug.hpp>
namespace efsw {
FileWatcher::FileWatcher() : mFollowSymlinks( false ), mOutOfScopeLinks( false ) {
efDEBUG( "Using backend: %s\n", BACKEND_NAME );
mImpl = new FILEWATCHER_IMPL( this );
if ( !mImpl->initOK() ) {
efSAFE_DELETE( mImpl );
efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME );
mImpl = new FileWatcherGeneric( this );
}
}
FileWatcher::FileWatcher( bool useGenericFileWatcher ) :
mFollowSymlinks( false ), mOutOfScopeLinks( false ) {
if ( useGenericFileWatcher ) {
efDEBUG( "Using backend: Generic\n" );
mImpl = new FileWatcherGeneric( this );
} else {
efDEBUG( "Using backend: %s\n", BACKEND_NAME );
mImpl = new FILEWATCHER_IMPL( this );
if ( !mImpl->initOK() ) {
efSAFE_DELETE( mImpl );
efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME );
mImpl = new FileWatcherGeneric( this );
}
}
}
FileWatcher::~FileWatcher() {
efSAFE_DELETE( mImpl );
}
WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher ) {
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, options );
} else {
return Errors::Log::createLastError( Errors::FileRemote, directory );
}
}
void FileWatcher::removeWatch( const std::string& directory ) {
mImpl->removeWatch( directory );
}
void FileWatcher::removeWatch( WatchID watchid ) {
mImpl->removeWatch( watchid );
}
void FileWatcher::watch() {
mImpl->watch();
}
std::vector<std::string> FileWatcher::directories() {
return mImpl->directories();
}
void FileWatcher::followSymlinks( bool follow ) {
mFollowSymlinks = follow;
}
const bool& FileWatcher::followSymlinks() const {
return mFollowSymlinks;
}
void FileWatcher::allowOutOfScopeLinks( bool allow ) {
mOutOfScopeLinks = allow;
}
const bool& FileWatcher::allowOutOfScopeLinks() const {
return mOutOfScopeLinks;
}
} // namespace efsw

View File

@ -0,0 +1,141 @@
#include <efsw/efsw.h>
#include <efsw/efsw.hpp>
#include <vector>
#define TOBOOL( i ) ( ( i ) == 0 ? false : true )
/*************************************************************************************************/
class Watcher_CAPI : public efsw::FileWatchListener {
public:
efsw_watcher mWatcher;
efsw_pfn_fileaction_callback mFn;
void* mParam;
efsw_pfn_handle_missed_fileactions mFnMissedFa;
public:
Watcher_CAPI( efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param,
efsw_pfn_handle_missed_fileactions fnfa ) :
mWatcher( watcher ), mFn( fn ), mParam( param ), mFnMissedFa( fnfa ) {}
void handleFileAction( efsw::WatchID watchid, const std::string& dir,
const std::string& filename, efsw::Action action,
std::string oldFilename = "" ) {
mFn( mWatcher, watchid, dir.c_str(), filename.c_str(), (enum efsw_action)action,
oldFilename.c_str(), mParam );
}
void handleMissedFileActions( efsw::WatchID watchid, const std::string& dir ) {
if ( mFnMissedFa ) {
mFnMissedFa( mWatcher, watchid, dir.c_str() );
}
}
};
/*************************************************************************************************
* globals
*/
static std::vector<Watcher_CAPI*> g_callbacks;
Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param ) {
for ( std::vector<Watcher_CAPI*>::iterator i = g_callbacks.begin(); i != g_callbacks.end();
++i ) {
Watcher_CAPI* callback = *i;
if ( callback->mFn == fn && callback->mWatcher == watcher && callback->mParam == param )
return *i;
}
return NULL;
}
Watcher_CAPI* remove_callback( efsw_watcher watcher ) {
std::vector<Watcher_CAPI*>::iterator i = g_callbacks.begin();
while ( i != g_callbacks.end() ) {
Watcher_CAPI* callback = *i;
if ( callback->mWatcher == watcher )
i = g_callbacks.erase( i );
else
++i;
}
return NULL;
}
/*************************************************************************************************/
efsw_watcher efsw_create( int generic_mode ) {
return ( efsw_watcher ) new efsw::FileWatcher( TOBOOL( generic_mode ) );
}
void efsw_release( efsw_watcher watcher ) {
remove_callback( watcher );
delete (efsw::FileWatcher*)watcher;
}
const char* efsw_getlasterror() {
static std::string log_str;
log_str = efsw::Errors::Log::getLastErrorLog();
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,
nullptr );
}
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,
efsw_pfn_handle_missed_fileactions callback_fn_missed_file_actions ) {
Watcher_CAPI* callback = find_callback( watcher, callback_fn, param );
if ( callback == NULL ) {
callback = new Watcher_CAPI( watcher, callback_fn, param, callback_fn_missed_file_actions );
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 ), watcher_options );
}
void efsw_removewatch( efsw_watcher watcher, const char* directory ) {
( (efsw::FileWatcher*)watcher )->removeWatch( std::string( directory ) );
}
void efsw_removewatch_byid( efsw_watcher watcher, efsw_watchid watchid ) {
( (efsw::FileWatcher*)watcher )->removeWatch( watchid );
}
void efsw_watch( efsw_watcher watcher ) {
( (efsw::FileWatcher*)watcher )->watch();
}
void efsw_follow_symlinks( efsw_watcher watcher, int enable ) {
( (efsw::FileWatcher*)watcher )->followSymlinks( TOBOOL( enable ) );
}
int efsw_follow_symlinks_isenabled( efsw_watcher watcher ) {
return (int)( (efsw::FileWatcher*)watcher )->followSymlinks();
}
void efsw_allow_outofscopelinks( efsw_watcher watcher, int allow ) {
( (efsw::FileWatcher*)watcher )->allowOutOfScopeLinks( TOBOOL( allow ) );
}
int efsw_outofscopelinks_isallowed( efsw_watcher watcher ) {
return (int)( (efsw::FileWatcher*)watcher )->allowOutOfScopeLinks();
}

View File

@ -0,0 +1,248 @@
#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/FileWatcherFSEvents.hpp>
#include <efsw/Lock.hpp>
#include <efsw/String.hpp>
#include <efsw/System.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <sys/utsname.h>
namespace efsw {
int getOSXReleaseNumber() {
static int osxR = -1;
if ( -1 == osxR ) {
struct utsname os;
if ( -1 != uname( &os ) ) {
std::string release( os.release );
size_t pos = release.find_first_of( '.' );
if ( pos != std::string::npos ) {
release = release.substr( 0, pos );
}
int rel = 0;
if ( String::fromString<int>( rel, release ) ) {
osxR = rel;
}
}
}
return osxR;
}
bool FileWatcherFSEvents::isGranular() {
return getOSXReleaseNumber() >= 11;
}
static std::string convertCFStringToStdString( CFStringRef cfString ) {
// Try to get the C string pointer directly
const char* cStr = CFStringGetCStringPtr( cfString, kCFStringEncodingUTF8 );
if ( cStr ) {
// If the pointer is valid, directly return a std::string from it
return std::string( cStr );
} else {
// If not, manually convert it
CFIndex length = CFStringGetLength( cfString );
CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 ) +
1; // +1 for null terminator
char* buffer = new char[maxSize];
if ( CFStringGetCString( cfString, buffer, maxSize, kCFStringEncodingUTF8 ) ) {
std::string result( buffer );
delete[] buffer;
return result;
} else {
delete[] buffer;
return "";
}
}
}
void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, void* userData,
size_t numEvents, void* eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[] ) {
WatcherFSEvents* watcher = static_cast<WatcherFSEvents*>( userData );
std::vector<FSEvent> events;
events.reserve( numEvents );
for ( size_t i = 0; i < numEvents; i++ ) {
if ( isGranular() ) {
CFDictionaryRef pathInfoDict =
static_cast<CFDictionaryRef>( CFArrayGetValueAtIndex( (CFArrayRef)eventPaths, i ) );
CFStringRef path = static_cast<CFStringRef>(
CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedDataPathKey ) );
CFNumberRef cfInode = static_cast<CFNumberRef>(
CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedFileIDKey ) );
if ( cfInode ) {
unsigned long inode = 0;
CFNumberGetValue( cfInode, kCFNumberLongType, &inode );
events.push_back( FSEvent( convertCFStringToStdString( path ), (long)eventFlags[i],
(Uint64)eventIds[i], inode ) );
}
} else {
events.push_back( FSEvent( std::string( ( (char**)eventPaths )[i] ),
(long)eventFlags[i], (Uint64)eventIds[i] ) );
}
}
watcher->handleActions( events );
watcher->process();
efDEBUG( "\n" );
}
FileWatcherFSEvents::FileWatcherFSEvents( FileWatcher* parent ) :
FileWatcherImpl( parent ), mLastWatchID( 0 ) {
mInitOK = true;
watch();
}
FileWatcherFSEvents::~FileWatcherFSEvents() {
mInitOK = false;
mWatchCond.notify_all();
WatchMap::iterator iter = mWatches.begin();
for ( ; iter != mWatches.end(); ++iter ) {
WatcherFSEvents* watch = iter->second;
efSAFE_DELETE( watch );
}
}
WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, const std::vector<WatcherOption>& options ) {
std::string dir( FileSystem::getRealPath( directory ) );
FileInfo fi( dir );
if ( !fi.isDirectory() ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
} else if ( !fi.isReadable() ) {
return Errors::Log::createLastError( Errors::FileNotReadable, dir );
}
FileSystem::dirAddSlashAtEnd( dir );
if ( pathInWatches( dir ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, directory );
}
/// Check if the directory is a symbolic link
std::string curPath;
std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
if ( "" != link ) {
/// If it's a symlink check if the realpath exists as a watcher, or
/// if the path is outside the current dir
if ( pathInWatches( link ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, directory );
} else if ( !linkAllowed( curPath, link ) ) {
return Errors::Log::createLastError( Errors::FileOutOfScope, dir );
} else {
dir = link;
}
}
mLastWatchID++;
WatcherFSEvents* pWatch = new WatcherFSEvents();
pWatch->Listener = watcher;
pWatch->ID = mLastWatchID;
pWatch->Directory = dir;
pWatch->Recursive = recursive;
pWatch->FWatcher = this;
pWatch->ModifiedFlags =
getOptionValue( options, Option::MacModifiedFilter, efswFSEventsModified );
pWatch->SanitizeEvents = getOptionValue( options, Option::MacSanitizeEvents, 0 ) != 0;
pWatch->init();
{
Lock lock( mWatchesLock );
mWatches.insert( std::make_pair( mLastWatchID, pWatch ) );
}
mWatchCond.notify_all();
return pWatch->ID;
}
void FileWatcherFSEvents::removeWatch( const std::string& directory ) {
Lock lock( mWatchesLock );
WatchMap::iterator iter = mWatches.begin();
for ( ; iter != mWatches.end(); ++iter ) {
if ( directory == iter->second->Directory ) {
removeWatch( iter->second->ID );
return;
}
}
}
void FileWatcherFSEvents::removeWatch( WatchID watchid ) {
Lock lock( mWatchesLock );
WatchMap::iterator iter = mWatches.find( watchid );
if ( iter == mWatches.end() )
return;
WatcherFSEvents* watch = iter->second;
mWatches.erase( iter );
efDEBUG( "Removed watch %s\n", watch->Directory.c_str() );
efSAFE_DELETE( watch );
}
void FileWatcherFSEvents::watch() {}
void FileWatcherFSEvents::handleAction( Watcher* /*watch*/, const std::string& /*filename*/,
unsigned long /*action*/, std::string /*oldFilename*/ ) {
/// Not used
}
std::vector<std::string> FileWatcherFSEvents::directories() {
std::vector<std::string> dirs;
Lock lock( mWatchesLock );
dirs.reserve( mWatches.size() );
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
dirs.push_back( std::string( it->second->Directory ) );
}
return dirs;
}
bool FileWatcherFSEvents::pathInWatches( const std::string& path ) {
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
if ( it->second->Directory == path ) {
return true;
}
}
return false;
}
} // namespace efsw
#endif

View File

@ -0,0 +1,80 @@
#ifndef EFSW_FILEWATCHERFSEVENTS_HPP
#define EFSW_FILEWATCHERFSEVENTS_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <dispatch/dispatch.h>
#include <efsw/WatcherFSEvents.hpp>
#include <map>
#include <vector>
#include <condition_variable>
#include <mutex>
namespace efsw {
/// Implementation for Win32 based on ReadDirectoryChangesW.
/// @class FileWatcherFSEvents
class FileWatcherFSEvents : public FileWatcherImpl {
friend class WatcherFSEvents;
public:
/// @return If FSEvents supports file-level notifications ( true if OS X >= 10.7 )
static bool isGranular();
/// type for a map from WatchID to WatcherWin32 pointer
typedef std::map<WatchID, WatcherFSEvents*> WatchMap;
FileWatcherFSEvents( FileWatcher* parent );
virtual ~FileWatcherFSEvents();
/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options ) override;
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory ) override;
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch( WatchID watchid ) override;
/// Updates the watcher. Must be called often.
void watch() override;
/// Handles the action
void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
std::string oldFilename = "" ) override;
/// @return Returns a list of the directories that are being watched
std::vector<std::string> directories() override;
protected:
static void FSEventCallback( ConstFSEventStreamRef streamRef, void* userData, size_t numEvents,
void* eventPaths, const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[] );
/// Vector of WatcherWin32 pointers
WatchMap mWatches;
/// The last watchid
WatchID mLastWatchID;
Mutex mWatchesLock;
bool pathInWatches( const std::string& path ) override;
std::mutex mWatchesMutex;
std::condition_variable mWatchCond;
};
} // namespace efsw
#endif
#endif

View File

@ -0,0 +1,158 @@
#include <efsw/FileSystem.hpp>
#include <efsw/FileWatcherGeneric.hpp>
#include <efsw/Lock.hpp>
#include <efsw/System.hpp>
namespace efsw {
FileWatcherGeneric::FileWatcherGeneric( FileWatcher* parent ) :
FileWatcherImpl( parent ), mThread( NULL ), mLastWatchID( 0 ) {
mInitOK = true;
mIsGeneric = true;
}
FileWatcherGeneric::~FileWatcherGeneric() {
mInitOK = false;
efSAFE_DELETE( mThread );
/// Delete the watches
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
efSAFE_DELETE( ( *it ) );
}
}
WatchID FileWatcherGeneric::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, const std::vector<WatcherOption>& options ) {
std::string dir( directory );
FileSystem::dirAddSlashAtEnd( dir );
FileInfo fi( dir );
if ( !fi.isDirectory() ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
} else if ( !fi.isReadable() ) {
return Errors::Log::createLastError( Errors::FileNotReadable, dir );
} else if ( pathInWatches( dir ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, dir );
}
std::string curPath;
std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
if ( "" != link ) {
if ( pathInWatches( link ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, dir );
} else if ( !linkAllowed( curPath, link ) ) {
return Errors::Log::createLastError( Errors::FileOutOfScope, dir );
} else {
dir = link;
}
}
mLastWatchID++;
WatcherGeneric* pWatch = new WatcherGeneric( mLastWatchID, dir, watcher, this, recursive );
Lock lock( mWatchesLock );
mWatches.push_back( pWatch );
return pWatch->ID;
}
void FileWatcherGeneric::removeWatch( const std::string& directory ) {
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
if ( ( *it )->Directory == directory ) {
WatcherGeneric* watch = ( *it );
Lock lock( mWatchesLock );
mWatches.erase( it );
efSAFE_DELETE( watch );
return;
}
}
}
void FileWatcherGeneric::removeWatch( WatchID watchid ) {
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
if ( ( *it )->ID == watchid ) {
WatcherGeneric* watch = ( *it );
Lock lock( mWatchesLock );
mWatches.erase( it );
efSAFE_DELETE( watch );
return;
}
}
}
void FileWatcherGeneric::watch() {
if ( NULL == mThread ) {
mThread = new Thread([this]{run();});
mThread->launch();
}
}
void FileWatcherGeneric::run() {
do {
{
Lock lock( mWatchesLock );
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
( *it )->watch();
}
}
if ( mInitOK )
System::sleep( 1000 );
} while ( mInitOK );
}
void FileWatcherGeneric::handleAction( Watcher*, const std::string&, unsigned long, std::string ) {
/// Not used
}
std::vector<std::string> FileWatcherGeneric::directories() {
std::vector<std::string> dirs;
Lock lock( mWatchesLock );
dirs.reserve( mWatches.size() );
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
dirs.push_back( ( *it )->Directory );
}
return dirs;
}
bool FileWatcherGeneric::pathInWatches( const std::string& path ) {
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
if ( ( *it )->Directory == path || ( *it )->pathInWatches( path ) ) {
return true;
}
}
return false;
}
} // namespace efsw

View File

@ -0,0 +1,61 @@
#ifndef EFSW_FILEWATCHERGENERIC_HPP
#define EFSW_FILEWATCHERGENERIC_HPP
#include <efsw/DirWatcherGeneric.hpp>
#include <efsw/FileWatcherImpl.hpp>
#include <efsw/WatcherGeneric.hpp>
#include <vector>
namespace efsw {
/// Implementation for Generic File Watcher.
/// @class FileWatcherGeneric
class FileWatcherGeneric : public FileWatcherImpl {
public:
typedef std::vector<WatcherGeneric*> WatchList;
FileWatcherGeneric( FileWatcher* parent );
virtual ~FileWatcherGeneric();
/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options ) override;
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory ) override;
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch( WatchID watchid ) override;
/// Updates the watcher. Must be called often.
void watch() override;
/// Handles the action
void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
std::string oldFilename = "" ) override;
/// @return Returns a list of the directories that are being watched
std::vector<std::string> directories() override;
protected:
Thread* mThread;
/// The last watchid
WatchID mLastWatchID;
/// Map of WatchID to WatchStruct pointers
WatchList mWatches;
Mutex mWatchesLock;
bool pathInWatches( const std::string& path ) override;
private:
void run();
};
} // namespace efsw
#endif

View File

@ -0,0 +1,34 @@
#include <efsw/FileWatcherImpl.hpp>
#include <efsw/String.hpp>
#include <efsw/System.hpp>
namespace efsw {
FileWatcherImpl::FileWatcherImpl( FileWatcher* parent ) :
mFileWatcher( parent ), mInitOK( false ), mIsGeneric( false ) {
System::maxFD();
}
FileWatcherImpl::~FileWatcherImpl() {}
bool FileWatcherImpl::initOK() {
return static_cast<bool>( mInitOK );
}
bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string& link ) {
return ( mFileWatcher->followSymlinks() && mFileWatcher->allowOutOfScopeLinks() ) ||
-1 != String::strStartsWith( curPath, link );
}
int FileWatcherImpl::getOptionValue( const std::vector<WatcherOption>& options, Option option,
int defaultValue ) {
for ( size_t i = 0; i < options.size(); i++ ) {
if ( options[i].mOption == option ) {
return options[i].mValue;
}
}
return defaultValue;
}
} // namespace efsw

View File

@ -0,0 +1,64 @@
#ifndef EFSW_FILEWATCHERIMPL_HPP
#define EFSW_FILEWATCHERIMPL_HPP
#include <efsw/Atomic.hpp>
#include <efsw/Mutex.hpp>
#include <efsw/Thread.hpp>
#include <efsw/Watcher.hpp>
#include <efsw/base.hpp>
#include <efsw/efsw.hpp>
namespace efsw {
class FileWatcherImpl {
public:
FileWatcherImpl( FileWatcher* parent );
virtual ~FileWatcherImpl();
/// Add a directory watch
/// On error returns WatchID with Error type.
virtual WatchID addWatch( const std::string& directory, FileWatchListener* watcher,
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;
/// Remove a directory watch. This is a map lookup O(logn).
virtual void removeWatch( WatchID watchid ) = 0;
/// Updates the watcher. Must be called often.
virtual void watch() = 0;
/// Handles the action
virtual void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
std::string oldFilename = "" ) = 0;
/// @return Returns a list of the directories that are being watched
virtual std::vector<std::string> directories() = 0;
/// @return true if the backend init successfully
virtual bool initOK();
/// @return If the link is allowed according to the current path and the state of out scope
/// links
virtual bool linkAllowed( const std::string& curPath, const std::string& link );
/// Search if a directory already exists in the watches
virtual bool pathInWatches( const std::string& path ) = 0;
protected:
friend class FileWatcher;
friend class DirWatcherGeneric;
FileWatcher* mFileWatcher;
Atomic<bool> mInitOK;
bool mIsGeneric;
int getOptionValue( const std::vector<WatcherOption>& options, Option option,
int defaultValue );
};
} // namespace efsw
#endif

View File

@ -0,0 +1,612 @@
#include <algorithm>
#include <efsw/FileWatcherInotify.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef EFSW_INOTIFY_NOSYS
#include <efsw/inotify-nosys.h>
#else
#include <sys/inotify.h>
#endif
#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/Lock.hpp>
#include <efsw/String.hpp>
#include <efsw/System.hpp>
#define BUFF_SIZE ( ( sizeof( struct inotify_event ) + FILENAME_MAX ) * 1024 )
namespace efsw {
FileWatcherInotify::FileWatcherInotify( FileWatcher* parent ) :
FileWatcherImpl( parent ), mFD( -1 ), mThread( NULL ), mIsTakingAction( false ) {
mFD = inotify_init();
if ( mFD < 0 ) {
efDEBUG( "Error: %s\n", strerror( errno ) );
} else {
mInitOK = true;
}
}
FileWatcherInotify::~FileWatcherInotify() {
mInitOK = false;
// There is deadlock when release FileWatcherInotify instance since its handAction
// function is still running and hangs in requiring lock without init lock captured.
while ( mIsTakingAction ) {
// It'd use condition-wait instead of sleep. Actually efsw has no such
// implementation so we just skip and sleep while for that to avoid deadlock.
usleep( 1000 );
};
Lock initLock( mInitLock );
efSAFE_DELETE( mThread );
Lock l( mWatchesLock );
Lock l2( mRealWatchesLock );
WatchMap::iterator iter = mWatches.begin();
WatchMap::iterator end = mWatches.end();
for ( ; iter != end; ++iter ) {
efSAFE_DELETE( iter->second );
}
mWatches.clear();
if ( mFD != -1 ) {
close( mFD );
mFD = -1;
}
}
WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, const std::vector<WatcherOption>& options ) {
if ( !mInitOK )
return Errors::Log::createLastError( Errors::Unspecified, directory );
Lock initLock( mInitLock );
bool syntheticEvents = getOptionValue( options, Options::LinuxProduceSyntheticEvents, 0 ) != 0;
return addWatch( directory, watcher, recursive, syntheticEvents, NULL );
}
WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, bool syntheticEvents, WatcherInotify* parent,
bool fromInternalEvent ) {
std::string dir( directory );
FileSystem::dirAddSlashAtEnd( dir );
FileInfo fi( dir );
if ( !fi.isDirectory() ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
} else if ( !fi.isReadable() ) {
return Errors::Log::createLastError( Errors::FileNotReadable, dir );
} else if ( pathInWatches( dir ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, directory );
} else if ( NULL != parent && FileSystem::isRemoteFS( dir ) ) {
return Errors::Log::createLastError( Errors::FileRemote, dir );
}
/// Check if the directory is a symbolic link
std::string curPath;
std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
if ( "" != link ) {
/// Avoid adding symlinks directories if it's now enabled
if ( NULL != parent && !mFileWatcher->followSymlinks() ) {
return Errors::Log::createLastError( Errors::FileOutOfScope, dir );
}
/// If it's a symlink check if the realpath exists as a watcher, or
/// if the path is outside the current dir
if ( pathInWatches( link ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, directory );
} else if ( !linkAllowed( curPath, link ) ) {
return Errors::Log::createLastError( Errors::FileOutOfScope, dir );
} else {
dir = link;
}
}
int wd = inotify_add_watch( mFD, dir.c_str(),
IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM |
IN_DELETE | IN_MODIFY );
if ( wd < 0 ) {
if ( errno == ENOENT ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
} else {
return Errors::Log::createLastError( Errors::Unspecified,
std::string( strerror( errno ) ) );
}
}
efDEBUG( "Added watch %s with id: %d\n", dir.c_str(), wd );
WatcherInotify* pWatch = new WatcherInotify();
pWatch->Listener = watcher;
pWatch->ID = parent ? parent->ID : wd;
pWatch->InotifyID = wd;
pWatch->Directory = dir;
pWatch->Recursive = recursive;
pWatch->Parent = parent;
pWatch->syntheticEvents = syntheticEvents;
{
Lock lock( mWatchesLock );
mWatches.insert( std::make_pair( wd, pWatch ) );
mWatchesRef[pWatch->Directory] = wd;
}
if ( NULL == pWatch->Parent ) {
Lock l( mRealWatchesLock );
mRealWatches[pWatch->InotifyID] = pWatch;
}
if ( pWatch->Recursive ) {
std::map<std::string, FileInfo> files = FileSystem::filesInfoFromPath( pWatch->Directory );
if ( fromInternalEvent && parent != NULL && syntheticEvents ) {
for ( const auto& file : files ) {
if ( file.second.isRegularFile() || file.second.isDirectory() ||
file.second.isLink() ) {
pWatch->Listener->handleFileAction(
pWatch->ID, pWatch->Directory,
FileSystem::fileNameFromPath( file.second.Filepath ), Actions::Add );
}
}
}
std::map<std::string, FileInfo>::iterator it = files.begin();
for ( ; it != files.end(); ++it ) {
if ( !mInitOK )
break;
const FileInfo& cfi = it->second;
if ( cfi.isDirectory() && cfi.isReadable() ) {
addWatch( cfi.Filepath, watcher, recursive, syntheticEvents, pWatch,
fromInternalEvent );
}
}
}
return wd;
}
void FileWatcherInotify::removeWatchLocked( WatchID watchid ) {
WatchMap::iterator iter = mWatches.find( watchid );
if ( iter == mWatches.end() )
return;
WatcherInotify* watch = iter->second;
for ( std::vector<std::pair<WatcherInotify*, std::string>>::iterator itm =
mMovedOutsideWatches.begin();
mMovedOutsideWatches.end() != itm; ++itm ) {
if ( itm->first == watch ) {
mMovedOutsideWatches.erase( itm );
break;
}
}
if ( watch->Recursive && NULL == watch->Parent ) {
WatchMap::iterator it = mWatches.begin();
std::vector<WatchID> eraseWatches;
for ( ; it != mWatches.end(); ++it )
if ( it->second != watch && it->second->inParentTree( watch ) )
eraseWatches.push_back( it->second->InotifyID );
for ( std::vector<WatchID>::iterator eit = eraseWatches.begin(); eit != eraseWatches.end();
++eit ) {
removeWatch( *eit );
}
}
mWatchesRef.erase( watch->Directory );
mWatches.erase( iter );
if ( NULL == watch->Parent ) {
WatchMap::iterator eraseit = mRealWatches.find( watch->InotifyID );
if ( eraseit != mRealWatches.end() ) {
mRealWatches.erase( eraseit );
}
}
int err = inotify_rm_watch( mFD, watchid );
if ( err < 0 ) {
efDEBUG( "Error removing watch %d: %s\n", watchid, strerror( errno ) );
} else {
efDEBUG( "Removed watch %s with id: %d\n", watch->Directory.c_str(), watchid );
}
efSAFE_DELETE( watch );
}
void FileWatcherInotify::removeWatch( const std::string& directory ) {
if ( !mInitOK )
return;
Lock initLock( mInitLock );
Lock lock( mWatchesLock );
Lock l( mRealWatchesLock );
std::unordered_map<std::string, WatchID>::iterator ref = mWatchesRef.find( directory );
if ( ref == mWatchesRef.end() )
return;
removeWatchLocked( ref->second );
}
void FileWatcherInotify::removeWatch( WatchID watchid ) {
if ( !mInitOK )
return;
Lock initLock( mInitLock );
Lock lock( mWatchesLock );
removeWatchLocked( watchid );
}
void FileWatcherInotify::watch() {
if ( NULL == mThread ) {
mThread = new Thread( [this] { run(); } );
mThread->launch();
}
}
Watcher* FileWatcherInotify::watcherContainsDirectory( std::string dir ) {
FileSystem::dirRemoveSlashAtEnd( dir );
std::string watcherPath = FileSystem::pathRemoveFileName( dir );
FileSystem::dirAddSlashAtEnd( watcherPath );
Lock lock( mWatchesLock );
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
Watcher* watcher = it->second;
if ( watcher->Directory == watcherPath )
return watcher;
}
return NULL;
}
void FileWatcherInotify::run() {
char* buff = new char[BUFF_SIZE];
memset( buff, 0, BUFF_SIZE );
WatcherInotify* curWatcher = NULL;
WatcherInotify* currentMoveFrom = NULL;
u_int32_t currentMoveCookie = -1;
bool lastWasMovedFrom = false;
std::string prevOldFileName;
do {
fd_set rfds;
FD_ZERO( &rfds );
FD_SET( mFD, &rfds );
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
if ( select( FD_SETSIZE, &rfds, NULL, NULL, &timeout ) > 0 ) {
ssize_t len;
len = read( mFD, buff, BUFF_SIZE );
if ( len != -1 ) {
ssize_t i = 0;
while ( i < len ) {
struct inotify_event* pevent = (struct inotify_event*)&buff[i];
{
curWatcher = NULL;
{
Lock lock( mWatchesLock );
auto wit = mWatches.find( pevent->wd );
if ( wit != mWatches.end() )
curWatcher = wit->second;
}
if ( curWatcher ) {
handleAction( curWatcher, (char*)pevent->name, pevent->mask );
if ( ( pevent->mask & IN_MOVED_TO ) && curWatcher == currentMoveFrom &&
pevent->cookie == currentMoveCookie ) {
/// make pair success
currentMoveFrom = NULL;
currentMoveCookie = -1;
} else if ( pevent->mask & IN_MOVED_FROM ) {
// Previous event was moved from and current event is moved from
// Treat it as a DELETE or moved ouside watches
if ( lastWasMovedFrom && currentMoveFrom ) {
mMovedOutsideWatches.push_back(
std::make_pair( currentMoveFrom, prevOldFileName ) );
}
currentMoveFrom = curWatcher;
currentMoveCookie = pevent->cookie;
} else {
/// Keep track of the IN_MOVED_FROM events to know
/// if the IN_MOVED_TO event is also fired
if ( currentMoveFrom ) {
if ( std::find_if( mMovedOutsideWatches.begin(),
mMovedOutsideWatches.end(),
[currentMoveFrom](
const std::pair<WatcherInotify*,
std::string>& moved ) {
return moved.first == currentMoveFrom;
} ) == mMovedOutsideWatches.end() ) {
mMovedOutsideWatches.push_back(
std::make_pair( currentMoveFrom, prevOldFileName ) );
} else {
efDEBUG( "Info: Tried to add watch to the moved outside "
"watches but it was already there, Watch ID: %d - "
"Address: %p - Path: \"%s\" - prevOldFileName: "
"\"%s\"\n",
pevent->wd, currentMoveFrom,
currentMoveFrom->Directory.c_str(),
prevOldFileName.c_str() );
}
}
currentMoveFrom = NULL;
currentMoveCookie = -1;
}
}
lastWasMovedFrom = ( pevent->mask & IN_MOVED_FROM ) != 0;
if ( pevent->mask & IN_MOVED_FROM )
prevOldFileName = std::string( (char*)pevent->name );
}
i += sizeof( struct inotify_event ) + pevent->len;
}
}
} else {
// Here means no event received
// If last event is IN_MOVED_FROM, we assume no IN_MOVED_TO
if ( currentMoveFrom ) {
if ( std::find_if(
mMovedOutsideWatches.begin(), mMovedOutsideWatches.end(),
[currentMoveFrom]( const std::pair<WatcherInotify*, std::string>& moved ) {
return moved.first == currentMoveFrom;
} ) == mMovedOutsideWatches.end() ) {
mMovedOutsideWatches.push_back(
std::make_pair( currentMoveFrom, currentMoveFrom->OldFileName ) );
} else {
efDEBUG( "Warning: Tried to add watch to the moved outside "
"watches but it was already there, Watch Address: %p\n",
currentMoveFrom );
}
}
currentMoveFrom = NULL;
currentMoveCookie = -1;
}
if ( !mMovedOutsideWatches.empty() ) {
// We need to make a copy since the element mMovedOutsideWatches could be modified
// during the iteration.
std::vector<std::pair<WatcherInotify*, std::string>> movedOutsideWatches(
mMovedOutsideWatches );
/// In case that the IN_MOVED_TO is never fired means that the file was moved to other
/// folder
for ( std::vector<std::pair<WatcherInotify*, std::string>>::iterator it =
movedOutsideWatches.begin();
it != movedOutsideWatches.end(); ++it ) {
// Skip if the watch has already being removed
if ( mMovedOutsideWatches.size() != movedOutsideWatches.size() ) {
bool found = false;
for ( std::vector<std::pair<WatcherInotify*, std::string>>::iterator itm =
mMovedOutsideWatches.begin();
mMovedOutsideWatches.end() != itm; ++itm ) {
if ( itm->first == it->first ) {
found = true;
break;
}
}
if ( !found )
continue;
}
Watcher* watch = it->first;
const std::string& oldFileName = it->second;
/// Check if the file move was a folder already being watched
std::vector<Watcher*> eraseWatches;
{
Lock lock( mWatchesLock );
for ( auto wit : mWatches ) {
Watcher* oldWatch = wit.second;
if ( oldWatch != watch &&
-1 != String::strStartsWith( watch->Directory + oldFileName + "/",
oldWatch->Directory ) ) {
eraseWatches.push_back( oldWatch );
}
}
}
/// Remove invalid watches
std::stable_sort( eraseWatches.begin(), eraseWatches.end(),
[]( const Watcher* left, const Watcher* right ) {
return left->Directory < right->Directory;
} );
if ( eraseWatches.empty() ) {
handleAction( watch, oldFileName, IN_DELETE );
} else {
for ( std::vector<Watcher*>::reverse_iterator eit = eraseWatches.rbegin();
eit != eraseWatches.rend(); ++eit ) {
Watcher* rmWatch = *eit;
/// Create Delete event for removed watches that have been moved too
if ( Watcher* cntWatch = watcherContainsDirectory( rmWatch->Directory ) ) {
handleAction( cntWatch,
FileSystem::fileNameFromPath( rmWatch->Directory ),
IN_DELETE );
}
}
}
}
mMovedOutsideWatches.clear();
}
} while ( mInitOK );
delete[] buff;
}
void FileWatcherInotify::checkForNewWatcher( Watcher* watch, std::string fpath ) {
FileSystem::dirAddSlashAtEnd( fpath );
/// If the watcher is recursive, checks if the new file is a folder, and creates a watcher
if ( watch->Recursive && FileSystem::isDirectory( fpath ) ) {
bool found = false;
{
Lock lock( mWatchesLock );
/// First check if exists
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
if ( it->second->Directory == fpath ) {
found = true;
break;
}
}
}
if ( !found ) {
WatcherInotify* iWatch = static_cast<WatcherInotify*>( watch );
addWatch( fpath, watch->Listener, watch->Recursive, iWatch->syntheticEvents,
static_cast<WatcherInotify*>( watch ), true );
}
}
}
void FileWatcherInotify::handleAction( Watcher* watch, const std::string& filename,
unsigned long action, std::string ) {
if ( !watch || !watch->Listener || !mInitOK ) {
return;
}
mIsTakingAction = true;
Lock initLock( mInitLock );
std::string fpath( watch->Directory + filename );
if ( IN_Q_OVERFLOW & action ) {
watch->Listener->handleMissedFileActions( watch->ID, watch->Directory );
} else if ( ( IN_CLOSE_WRITE & action ) || ( IN_MODIFY & action ) ) {
watch->Listener->handleFileAction( watch->ID, watch->Directory, filename,
Actions::Modified );
} else if ( IN_MOVED_TO & action ) {
/// If OldFileName doesn't exist means that the file has been moved from other folder, so we
/// just send the Add event
if ( watch->OldFileName.empty() ) {
watch->Listener->handleFileAction( watch->ID, watch->Directory, filename,
Actions::Add );
watch->Listener->handleFileAction( watch->ID, watch->Directory, filename,
Actions::Modified );
checkForNewWatcher( watch, fpath );
} else {
watch->Listener->handleFileAction( watch->ID, watch->Directory, filename,
Actions::Moved, watch->OldFileName );
}
if ( watch->Recursive && FileSystem::isDirectory( fpath ) ) {
/// Update the new directory path
std::string opath( watch->Directory + watch->OldFileName );
FileSystem::dirAddSlashAtEnd( opath );
FileSystem::dirAddSlashAtEnd( fpath );
Lock lock( mWatchesLock );
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
if ( it->second->Directory == opath ) {
it->second->Directory = fpath;
it->second->DirInfo = FileInfo( fpath );
} else if ( -1 != String::strStartsWith( opath, it->second->Directory ) ) {
it->second->Directory = fpath + it->second->Directory.substr( opath.size() );
it->second->DirInfo.Filepath = it->second->Directory;
}
}
}
watch->OldFileName = "";
} else if ( IN_CREATE & action ) {
watch->Listener->handleFileAction( watch->ID, watch->Directory, filename, Actions::Add );
checkForNewWatcher( watch, fpath );
} else if ( IN_MOVED_FROM & action ) {
watch->OldFileName = filename;
} else if ( IN_DELETE & action ) {
watch->Listener->handleFileAction( watch->ID, watch->Directory, filename, Actions::Delete );
FileSystem::dirAddSlashAtEnd( fpath );
/// If the file erased is a directory and recursive is enabled, removes the directory erased
if ( watch->Recursive ) {
Lock l( mWatchesLock );
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
if ( it->second->Directory == fpath ) {
removeWatchLocked( it->second->InotifyID );
break;
}
}
}
}
mIsTakingAction = false;
}
std::vector<std::string> FileWatcherInotify::directories() {
std::vector<std::string> dirs;
Lock l( mRealWatchesLock );
dirs.reserve( mRealWatches.size() );
WatchMap::iterator it = mRealWatches.begin();
for ( ; it != mRealWatches.end(); ++it )
dirs.push_back( it->second->Directory );
return dirs;
}
bool FileWatcherInotify::pathInWatches( const std::string& path ) {
Lock l( mRealWatchesLock );
/// Search in the real watches, since it must allow adding a watch already watched as a subdir
WatchMap::iterator it = mRealWatches.begin();
for ( ; it != mRealWatches.end(); ++it )
if ( it->second->Directory == path )
return true;
return false;
}
} // namespace efsw
#endif

View File

@ -0,0 +1,87 @@
#ifndef EFSW_FILEWATCHERLINUX_HPP
#define EFSW_FILEWATCHERLINUX_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY
#include <efsw/WatcherInotify.hpp>
#include <map>
#include <unordered_map>
#include <vector>
namespace efsw {
/// Implementation for Linux based on inotify.
/// @class FileWatcherInotify
class FileWatcherInotify : public FileWatcherImpl {
public:
/// type for a map from WatchID to WatchStruct pointer
typedef std::map<WatchID, WatcherInotify*> WatchMap;
FileWatcherInotify( FileWatcher* parent );
virtual ~FileWatcherInotify();
/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption>& options ) override;
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory ) override;
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch( WatchID watchid ) override;
/// Updates the watcher. Must be called often.
void watch() override;
/// Handles the action
void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
std::string oldFilename = "" ) override;
/// @return Returns a list of the directories that are being watched
std::vector<std::string> directories() override;
protected:
/// Map of WatchID to WatchStruct pointers
WatchMap mWatches;
/// User added watches
WatchMap mRealWatches;
std::unordered_map<std::string, WatchID> mWatchesRef;
/// inotify file descriptor
int mFD;
Thread* mThread;
Mutex mWatchesLock;
Mutex mRealWatchesLock;
Mutex mInitLock;
bool mIsTakingAction;
std::vector<std::pair<WatcherInotify*, std::string>> mMovedOutsideWatches;
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
bool syntheticEvents, WatcherInotify* parent = NULL,
bool fromInternalEvent = false );
bool pathInWatches( const std::string& path ) override;
private:
void run();
void removeWatchLocked( WatchID watchid );
void checkForNewWatcher( Watcher* watch, std::string fpath );
Watcher* watcherContainsDirectory( std::string dir );
};
} // namespace efsw
#endif
#endif

View File

@ -0,0 +1,229 @@
#include <efsw/FileWatcherKqueue.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <dirent.h>
#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/Lock.hpp>
#include <efsw/System.hpp>
#include <efsw/WatcherGeneric.hpp>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
namespace efsw {
FileWatcherKqueue::FileWatcherKqueue( FileWatcher* parent ) :
FileWatcherImpl( parent ),
mLastWatchID( 0 ),
mThread( NULL ),
mFileDescriptorCount( 1 ),
mAddingWatcher( false ) {
mTimeOut.tv_sec = 0;
mTimeOut.tv_nsec = 0;
mInitOK = true;
}
FileWatcherKqueue::~FileWatcherKqueue() {
WatchMap::iterator iter = mWatches.begin();
for ( ; iter != mWatches.end(); ++iter ) {
efSAFE_DELETE( iter->second );
}
mWatches.clear();
mInitOK = false;
efSAFE_DELETE( mThread );
}
WatchID FileWatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, const std::vector<WatcherOption>& options ) {
static bool s_ug = false;
std::string dir( directory );
FileSystem::dirAddSlashAtEnd( dir );
FileInfo fi( dir );
if ( !fi.isDirectory() ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
} else if ( !fi.isReadable() ) {
return Errors::Log::createLastError( Errors::FileNotReadable, dir );
} else if ( pathInWatches( dir ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, directory );
}
std::string curPath;
std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
if ( "" != link ) {
if ( pathInWatches( link ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, directory );
} else if ( !linkAllowed( curPath, link ) ) {
return Errors::Log::createLastError( Errors::FileOutOfScope, dir );
} else {
dir = link;
}
}
/// Check first if are enough file descriptors available to create another kqueue watcher,
/// otherwise it creates a generic watcher
if ( availablesFD() ) {
mAddingWatcher = true;
WatcherKqueue* watch = new WatcherKqueue( ++mLastWatchID, dir, watcher, recursive, this );
{
Lock lock( mWatchesLock );
mWatches.insert( std::make_pair( mLastWatchID, watch ) );
}
watch->addAll();
// if failed to open the directory... erase the watcher
if ( !watch->initOK() ) {
int le = watch->lastErrno();
mWatches.erase( watch->ID );
efSAFE_DELETE( watch );
mLastWatchID--;
// Probably the folder has too many files, create a generic watcher
if ( EACCES != le ) {
WatcherGeneric* genericWatch =
new WatcherGeneric( ++mLastWatchID, dir, watcher, this, recursive );
Lock lock( mWatchesLock );
mWatches.insert( std::make_pair( mLastWatchID, genericWatch ) );
} else {
return Errors::Log::createLastError( Errors::Unspecified, link );
}
}
mAddingWatcher = false;
} else {
if ( !s_ug ) {
efDEBUG( "Started using generic watcher, file descriptor limit reached: %ld\n",
mFileDescriptorCount );
s_ug = true;
}
WatcherGeneric* watch = new WatcherGeneric( ++mLastWatchID, dir, watcher, this, recursive );
Lock lock( mWatchesLock );
mWatches.insert( std::make_pair( mLastWatchID, watch ) );
}
return mLastWatchID;
}
void FileWatcherKqueue::removeWatch( const std::string& directory ) {
Lock lock( mWatchesLock );
WatchMap::iterator iter = mWatches.begin();
for ( ; iter != mWatches.end(); ++iter ) {
if ( directory == iter->second->Directory ) {
removeWatch( iter->first );
return;
}
}
}
void FileWatcherKqueue::removeWatch( WatchID watchid ) {
Lock lock( mWatchesLock );
WatchMap::iterator iter = mWatches.find( watchid );
if ( iter == mWatches.end() )
return;
Watcher* watch = iter->second;
mWatches.erase( iter );
efSAFE_DELETE( watch );
}
bool FileWatcherKqueue::isAddingWatcher() const {
return mAddingWatcher;
}
void FileWatcherKqueue::watch() {
if ( NULL == mThread ) {
mThread = new Thread([this]{run();});
mThread->launch();
}
}
void FileWatcherKqueue::run() {
do {
{
Lock lock( mWatchesLock );
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
it->second->watch();
}
}
System::sleep( 500 );
} while ( mInitOK );
}
void FileWatcherKqueue::handleAction( Watcher* watch, const std::string& filename,
unsigned long action, std::string oldFilename ) {}
std::vector<std::string> FileWatcherKqueue::directories() {
std::vector<std::string> dirs;
Lock lock( mWatchesLock );
dirs.reserve( mWatches.size() );
WatchMap::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
dirs.push_back( it->second->Directory );
}
return dirs;
}
bool FileWatcherKqueue::pathInWatches( const std::string& path ) {
WatchMap::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); ++it ) {
if ( it->second->Directory == path ) {
return true;
}
}
return false;
}
void FileWatcherKqueue::addFD() {
mFileDescriptorCount++;
}
void FileWatcherKqueue::removeFD() {
mFileDescriptorCount--;
}
bool FileWatcherKqueue::availablesFD() {
return mFileDescriptorCount <= (Int64)System::getMaxFD() - 500;
}
} // namespace efsw
#endif

View File

@ -0,0 +1,81 @@
#ifndef EFSW_FILEWATCHEROSX_HPP
#define EFSW_FILEWATCHEROSX_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <efsw/WatcherKqueue.hpp>
namespace efsw {
/// Implementation for OSX based on kqueue.
/// @class FileWatcherKqueue
class FileWatcherKqueue : public FileWatcherImpl {
friend class WatcherKqueue;
public:
FileWatcherKqueue( FileWatcher* parent );
virtual ~FileWatcherKqueue();
/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options ) override;
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory ) override;
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch( WatchID watchid ) override;
/// Updates the watcher. Must be called often.
void watch() override;
/// Handles the action
void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
std::string oldFilename = "" ) override;
/// @return Returns a list of the directories that are being watched
std::vector<std::string> directories() override;
protected:
/// Map of WatchID to WatchStruct pointers
WatchMap mWatches;
/// time out data
struct timespec mTimeOut;
/// WatchID allocator
int mLastWatchID;
Thread* mThread;
Mutex mWatchesLock;
std::vector<WatchID> mRemoveList;
long mFileDescriptorCount;
bool mAddingWatcher;
bool isAddingWatcher() const;
bool pathInWatches( const std::string& path ) override;
void addFD();
void removeFD();
bool availablesFD();
private:
void run();
};
} // namespace efsw
#endif
#endif

View File

@ -0,0 +1,267 @@
#include <efsw/FileSystem.hpp>
#include <efsw/FileWatcherWin32.hpp>
#include <efsw/Lock.hpp>
#include <efsw/String.hpp>
#include <efsw/System.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
namespace efsw {
FileWatcherWin32::FileWatcherWin32( FileWatcher* parent ) :
FileWatcherImpl( parent ), mLastWatchID( 0 ), mThread( NULL ) {
mIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 1 );
if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE )
mInitOK = true;
}
FileWatcherWin32::~FileWatcherWin32() {
mInitOK = false;
if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) {
PostQueuedCompletionStatus( mIOCP, 0, reinterpret_cast<ULONG_PTR>( this ), NULL );
}
efSAFE_DELETE( mThread );
removeAllWatches();
if ( mIOCP )
CloseHandle( mIOCP );
}
WatchID FileWatcherWin32::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, const std::vector<WatcherOption> &options ) {
std::string dir( directory );
FileInfo fi( dir );
if ( !fi.isDirectory() ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
} else if ( !fi.isReadable() ) {
return Errors::Log::createLastError( Errors::FileNotReadable, dir );
}
FileSystem::dirAddSlashAtEnd( dir );
Lock lock( mWatchesLock );
if ( pathInWatches( dir ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, dir );
}
WatchID watchid = ++mLastWatchID;
DWORD bufferSize = static_cast<DWORD>( getOptionValue(options, Option::WinBufferSize, 63 * 1024) );
DWORD notifyFilter = static_cast<DWORD>( getOptionValue(options, Option::WinNotifyFilter,
FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE) );
WatcherStructWin32* watch = CreateWatch( String::fromUtf8( dir ).toWideString().c_str(),
recursive, bufferSize, notifyFilter, mIOCP );
if ( NULL == watch ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
}
// Add the handle to the handles vector
watch->Watch->ID = watchid;
watch->Watch->Watch = this;
watch->Watch->Listener = watcher;
watch->Watch->DirName = new char[dir.length() + 1];
strcpy( watch->Watch->DirName, dir.c_str() );
mWatches.insert( watch );
return watchid;
}
void FileWatcherWin32::removeWatch( const std::string& directory ) {
Lock lock( mWatchesLock );
Watches::iterator iter = mWatches.begin();
for ( ; iter != mWatches.end(); ++iter ) {
if ( directory == ( *iter )->Watch->DirName ) {
removeWatch( *iter );
break;
}
}
}
void FileWatcherWin32::removeWatch( WatchID watchid ) {
Lock lock( mWatchesLock );
Watches::iterator iter = mWatches.begin();
for ( ; iter != mWatches.end(); ++iter ) {
// Find the watch ID
if ( ( *iter )->Watch->ID == watchid ) {
removeWatch( *iter );
return;
}
}
}
void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) {
Lock lock( mWatchesLock );
DestroyWatch( watch );
mWatches.erase( watch );
}
void FileWatcherWin32::watch() {
if ( NULL == mThread ) {
mThread = new Thread([this]{run();});
mThread->launch();
}
}
void FileWatcherWin32::removeAllWatches() {
Lock lock( mWatchesLock );
Watches::iterator iter = mWatches.begin();
for ( ; iter != mWatches.end(); ++iter ) {
DestroyWatch( ( *iter ) );
}
mWatches.clear();
}
void FileWatcherWin32::run() {
do {
if ( mInitOK && !mWatches.empty() ) {
DWORD numOfBytes = 0;
OVERLAPPED* ov = NULL;
ULONG_PTR compKey = 0;
BOOL res = FALSE;
while ( ( res = GetQueuedCompletionStatus( mIOCP, &numOfBytes, &compKey, &ov,
INFINITE ) ) != FALSE ) {
if ( compKey != 0 && compKey == reinterpret_cast<ULONG_PTR>( this ) ) {
break;
} else {
Lock lock( mWatchesLock );
if (mWatches.find( (WatcherStructWin32*)ov ) != mWatches.end())
WatchCallback( numOfBytes, ov );
}
}
} else {
System::sleep( 10 );
}
} while ( mInitOK );
removeAllWatches();
}
void FileWatcherWin32::handleAction( Watcher* watch, const std::string& filename,
unsigned long action, std::string /*oldFilename*/ ) {
Action fwAction;
switch ( action ) {
case FILE_ACTION_RENAMED_OLD_NAME:
watch->OldFileName = filename;
return;
case FILE_ACTION_ADDED:
fwAction = Actions::Add;
break;
case FILE_ACTION_RENAMED_NEW_NAME: {
fwAction = Actions::Moved;
std::string fpath( watch->Directory + filename );
// Update the directory path
if ( watch->Recursive && FileSystem::isDirectory( fpath ) ) {
// Update the new directory path
std::string opath( watch->Directory + watch->OldFileName );
FileSystem::dirAddSlashAtEnd( opath );
FileSystem::dirAddSlashAtEnd( fpath );
for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
if ( ( *it )->Watch->Directory == opath ) {
( *it )->Watch->Directory = fpath;
break;
}
}
}
std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName );
std::string realFilename = filename;
std::size_t sepPos = filename.find_last_of( "/\\" );
std::string oldFolderPath =
static_cast<WatcherWin32*>( watch )->DirName +
watch->OldFileName.substr( 0, watch->OldFileName.find_last_of( "/\\" ) );
if ( sepPos != std::string::npos ) {
folderPath +=
filename.substr( 0, sepPos + 1 < filename.size() ? sepPos + 1 : sepPos );
realFilename = filename.substr( sepPos + 1 );
}
if ( folderPath == oldFolderPath ) {
watch->Listener->handleFileAction(
watch->ID, folderPath, realFilename, fwAction,
FileSystem::fileNameFromPath( watch->OldFileName ) );
} else {
watch->Listener->handleFileAction( watch->ID,
static_cast<WatcherWin32*>( watch )->DirName,
filename, fwAction, watch->OldFileName );
}
return;
}
case FILE_ACTION_REMOVED:
fwAction = Actions::Delete;
break;
case FILE_ACTION_MODIFIED:
fwAction = Actions::Modified;
break;
default:
return;
};
std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName );
std::string realFilename = filename;
std::size_t sepPos = filename.find_last_of( "/\\" );
if ( sepPos != std::string::npos ) {
folderPath += filename.substr( 0, sepPos + 1 < filename.size() ? sepPos + 1 : sepPos );
realFilename = filename.substr( sepPos + 1 );
}
FileSystem::dirAddSlashAtEnd( folderPath );
watch->Listener->handleFileAction( watch->ID, folderPath, realFilename, fwAction );
}
std::vector<std::string> FileWatcherWin32::directories() {
std::vector<std::string> dirs;
Lock lock( mWatchesLock );
dirs.reserve( mWatches.size() );
for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
dirs.push_back( std::string( ( *it )->Watch->DirName ) );
}
return dirs;
}
bool FileWatcherWin32::pathInWatches( const std::string& path ) {
Lock lock( mWatchesLock );
for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
if ( ( *it )->Watch->DirName == path ) {
return true;
}
}
return false;
}
} // namespace efsw
#endif

View File

@ -0,0 +1,71 @@
#ifndef EFSW_FILEWATCHERWIN32_HPP
#define EFSW_FILEWATCHERWIN32_HPP
#include <efsw/base.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <efsw/WatcherWin32.hpp>
#include <map>
#include <unordered_set>
#include <vector>
namespace efsw {
/// Implementation for Win32 based on ReadDirectoryChangesW.
/// @class FileWatcherWin32
class FileWatcherWin32 : public FileWatcherImpl {
public:
/// type for a map from WatchID to WatcherWin32 pointer
typedef std::unordered_set<WatcherStructWin32*> Watches;
FileWatcherWin32( FileWatcher* parent );
virtual ~FileWatcherWin32();
/// Add a directory watch
/// On error returns WatchID with Error type.
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
const std::vector<WatcherOption> &options ) override;
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch( const std::string& directory ) override;
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch( WatchID watchid ) override;
/// Updates the watcher. Must be called often.
void watch() override;
/// Handles the action
void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
std::string oldFilename = "" ) override;
/// @return Returns a list of the directories that are being watched
std::vector<std::string> directories() override;
protected:
HANDLE mIOCP;
Watches mWatches;
/// The last watchid
WatchID mLastWatchID;
Thread* mThread;
Mutex mWatchesLock;
bool pathInWatches( const std::string& path ) override;
/// Remove all directory watches.
void removeAllWatches();
void removeWatch( WatcherStructWin32* watch );
private:
void run();
};
} // namespace efsw
#endif
#endif

11
src/efsw/Lock.hpp Normal file
View File

@ -0,0 +1,11 @@
#ifndef EFSW_LOCK_HPP
#define EFSW_LOCK_HPP
#include <mutex>
#include <efsw/Mutex.hpp>
namespace efsw {
using Lock = std::unique_lock<Mutex>;
} // namespace efsw
#endif

49
src/efsw/Log.cpp Normal file
View File

@ -0,0 +1,49 @@
#include <efsw/efsw.hpp>
#include <efsw/Debug.hpp>
namespace efsw { namespace Errors {
static std::string LastError = "";
static Error LastErrorCode = NoError;
std::string Log::getLastErrorLog() {
return LastError;
}
Error Log::getLastErrorCode() {
return LastErrorCode;
}
void Log::clearLastError() {
LastErrorCode = NoError;
LastError = "";
}
Error Log::createLastError( Error err, std::string log ) {
switch ( err ) {
case FileNotFound:
LastError = "File not found ( " + log + " )";
break;
case FileRepeated:
LastError = "File repeated in watches ( " + log + " )";
break;
case FileOutOfScope:
LastError = "Symlink file out of scope ( " + log + " )";
break;
case FileRemote:
LastError =
"File is located in a remote file system, use a generic watcher. ( " + log + " )";
break;
case WatcherFailed:
LastError = "File system watcher failed ( " + log + " )";
break;
case Unspecified:
default:
LastError = log;
}
efDEBUG( "%s\n", LastError.c_str() );
return err;
}
}} // namespace efsw::Errors

10
src/efsw/Mutex.hpp Normal file
View File

@ -0,0 +1,10 @@
#ifndef EFSW_MUTEX_HPP
#define EFSW_MUTEX_HPP
#include <mutex>
namespace efsw {
using Mutex = std::recursive_mutex;
} // namespace efsw
#endif

669
src/efsw/String.cpp Normal file
View File

@ -0,0 +1,669 @@
#include <efsw/String.hpp>
#include <efsw/Utf.hpp>
#include <iterator>
namespace efsw {
const std::size_t String::InvalidPos = StringType::npos;
std::vector<std::string> String::split( const std::string& str, const char& splitchar,
const bool& pushEmptyString ) {
std::vector<std::string> tmp;
std::string tmpstr;
for ( size_t i = 0; i < str.size(); i++ ) {
if ( str[i] == splitchar ) {
if ( pushEmptyString || tmpstr.size() ) {
tmp.push_back( tmpstr );
tmpstr = "";
}
} else {
tmpstr += str[i];
}
}
if ( tmpstr.size() ) {
tmp.push_back( tmpstr );
}
return tmp;
}
std::vector<String> String::split( const String& str, const Uint32& splitchar,
const bool& pushEmptyString ) {
std::vector<String> tmp;
String tmpstr;
for ( size_t i = 0; i < str.size(); i++ ) {
if ( str[i] == splitchar ) {
if ( pushEmptyString || tmpstr.size() ) {
tmp.push_back( tmpstr );
tmpstr = "";
}
} else {
tmpstr += str[i];
}
}
if ( tmpstr.size() ) {
tmp.push_back( tmpstr );
}
return tmp;
}
int String::strStartsWith( const std::string& start, const std::string& str ) {
int pos = -1;
size_t size = start.size();
if ( str.size() >= size ) {
for ( std::size_t i = 0; i < size; i++ ) {
if ( start[i] == str[i] ) {
pos = (int)i;
} else {
pos = -1;
break;
}
}
}
return pos;
}
int String::strStartsWith( const String& start, const String& str ) {
int pos = -1;
size_t size = start.size();
if ( str.size() >= size ) {
for ( std::size_t i = 0; i < size; i++ ) {
if ( start[i] == str[i] ) {
pos = (int)i;
} else {
pos = -1;
break;
}
}
}
return pos;
}
String::String() {}
String::String( char ansiChar, const std::locale& locale ) {
mString += Utf32::DecodeAnsi( ansiChar, locale );
}
#ifndef EFSW_NO_WIDECHAR
String::String( wchar_t wideChar ) {
mString += Utf32::DecodeWide( wideChar );
}
#endif
String::String( StringBaseType utf32Char ) {
mString += utf32Char;
}
String::String( const char* uf8String ) {
if ( uf8String ) {
std::size_t length = strlen( uf8String );
if ( length > 0 ) {
mString.reserve( length + 1 );
Utf8::ToUtf32( uf8String, uf8String + length, std::back_inserter( mString ) );
}
}
}
String::String( const std::string& utf8String ) {
mString.reserve( utf8String.length() + 1 );
Utf8::ToUtf32( utf8String.begin(), utf8String.end(), std::back_inserter( mString ) );
}
String::String( const char* ansiString, const std::locale& locale ) {
if ( ansiString ) {
std::size_t length = strlen( ansiString );
if ( length > 0 ) {
mString.reserve( length + 1 );
Utf32::FromAnsi( ansiString, ansiString + length, std::back_inserter( mString ),
locale );
}
}
}
String::String( const std::string& ansiString, const std::locale& locale ) {
mString.reserve( ansiString.length() + 1 );
Utf32::FromAnsi( ansiString.begin(), ansiString.end(), std::back_inserter( mString ), locale );
}
#ifndef EFSW_NO_WIDECHAR
String::String( const wchar_t* wideString ) {
if ( wideString ) {
std::size_t length = std::wcslen( wideString );
if ( length > 0 ) {
mString.reserve( length + 1 );
Utf32::FromWide( wideString, wideString + length, std::back_inserter( mString ) );
}
}
}
String::String( const std::wstring& wideString ) {
mString.reserve( wideString.length() + 1 );
Utf32::FromWide( wideString.begin(), wideString.end(), std::back_inserter( mString ) );
}
#endif
String::String( const StringBaseType* utf32String ) {
if ( utf32String )
mString = utf32String;
}
String::String( const StringType& utf32String ) : mString( utf32String ) {}
String::String( const String& str ) : mString( str.mString ) {}
String String::fromUtf8( const std::string& utf8String ) {
String::StringType utf32;
utf32.reserve( utf8String.length() + 1 );
Utf8::ToUtf32( utf8String.begin(), utf8String.end(), std::back_inserter( utf32 ) );
return String( utf32 );
}
String::operator std::string() const {
return toAnsiString();
}
std::string String::toAnsiString( const std::locale& locale ) const {
// Prepare the output string
std::string output;
output.reserve( mString.length() + 1 );
// Convert
Utf32::ToAnsi( mString.begin(), mString.end(), std::back_inserter( output ), 0, locale );
return output;
}
#ifndef EFSW_NO_WIDECHAR
std::wstring String::toWideString() const {
// Prepare the output string
std::wstring output;
output.reserve( mString.length() + 1 );
// Convert
Utf32::ToWide( mString.begin(), mString.end(), std::back_inserter( output ), 0 );
return output;
}
#endif
std::string String::toUtf8() const {
// Prepare the output string
std::string output;
output.reserve( mString.length() + 1 );
// Convert
Utf32::toUtf8( mString.begin(), mString.end(), std::back_inserter( output ) );
return output;
}
String& String::operator=( const String& right ) {
mString = right.mString;
return *this;
}
String& String::operator=( const StringBaseType& right ) {
mString = right;
return *this;
}
String& String::operator+=( const String& right ) {
mString += right.mString;
return *this;
}
String& String::operator+=( const StringBaseType& right ) {
mString += right;
return *this;
}
String::StringBaseType String::operator[]( std::size_t index ) const {
return mString[index];
}
String::StringBaseType& String::operator[]( std::size_t index ) {
return mString[index];
}
String::StringBaseType String::at( std::size_t index ) const {
return mString.at( index );
}
void String::push_back( StringBaseType c ) {
mString.push_back( c );
}
void String::swap( String& str ) {
mString.swap( str.mString );
}
void String::clear() {
mString.clear();
}
std::size_t String::size() const {
return mString.size();
}
std::size_t String::length() const {
return mString.length();
}
bool String::empty() const {
return mString.empty();
}
void String::erase( std::size_t position, std::size_t count ) {
mString.erase( position, count );
}
String& String::insert( std::size_t position, const String& str ) {
mString.insert( position, str.mString );
return *this;
}
String& String::insert( std::size_t pos1, const String& str, std::size_t pos2, std::size_t n ) {
mString.insert( pos1, str.mString, pos2, n );
return *this;
}
String& String::insert( size_t pos1, const char* s, size_t n ) {
String tmp( s );
mString.insert( pos1, tmp.data(), n );
return *this;
}
String& String::insert( size_t pos1, size_t n, char c ) {
mString.insert( pos1, n, c );
return *this;
}
String& String::insert( size_t pos1, const char* s ) {
String tmp( s );
mString.insert( pos1, tmp.data() );
return *this;
}
String::Iterator String::insert( Iterator p, char c ) {
return mString.insert( p, c );
}
void String::insert( Iterator p, size_t n, char c ) {
mString.insert( p, n, c );
}
const String::StringBaseType* String::c_str() const {
return mString.c_str();
}
const String::StringBaseType* String::data() const {
return mString.data();
}
String::Iterator String::begin() {
return mString.begin();
}
String::ConstIterator String::begin() const {
return mString.begin();
}
String::Iterator String::end() {
return mString.end();
}
String::ConstIterator String::end() const {
return mString.end();
}
String::ReverseIterator String::rbegin() {
return mString.rbegin();
}
String::ConstReverseIterator String::rbegin() const {
return mString.rbegin();
}
String::ReverseIterator String::rend() {
return mString.rend();
}
String::ConstReverseIterator String::rend() const {
return mString.rend();
}
void String::resize( std::size_t n, StringBaseType c ) {
mString.resize( n, c );
}
void String::resize( std::size_t n ) {
mString.resize( n );
}
std::size_t String::max_size() const {
return mString.max_size();
}
void String::reserve( size_t res_arg ) {
mString.reserve( res_arg );
}
std::size_t String::capacity() const {
return mString.capacity();
}
String& String::assign( const String& str ) {
mString.assign( str.mString );
return *this;
}
String& String::assign( const String& str, size_t pos, size_t n ) {
mString.assign( str.mString, pos, n );
return *this;
}
String& String::assign( const char* s, size_t n ) {
String tmp( s );
mString.assign( tmp.mString );
return *this;
}
String& String::assign( const char* s ) {
String tmp( s );
mString.assign( tmp.mString );
return *this;
}
String& String::assign( size_t n, char c ) {
mString.assign( n, c );
return *this;
}
String& String::append( const String& str ) {
mString.append( str.mString );
return *this;
}
String& String::append( const String& str, size_t pos, size_t n ) {
mString.append( str.mString, pos, n );
return *this;
}
String& String::append( const char* s, size_t n ) {
String tmp( s );
mString.append( tmp.mString );
return *this;
}
String& String::append( const char* s ) {
String tmp( s );
mString.append( tmp.mString );
return *this;
}
String& String::append( size_t n, char c ) {
mString.append( n, c );
return *this;
}
String& String::append( std::size_t n, StringBaseType c ) {
mString.append( n, c );
return *this;
}
String& String::replace( size_t pos1, size_t n1, const String& str ) {
mString.replace( pos1, n1, str.mString );
return *this;
}
String& String::replace( Iterator i1, Iterator i2, const String& str ) {
mString.replace( i1, i2, str.mString );
return *this;
}
String& String::replace( size_t pos1, size_t n1, const String& str, size_t pos2, size_t n2 ) {
mString.replace( pos1, n1, str.mString, pos2, n2 );
return *this;
}
String& String::replace( size_t pos1, size_t n1, const char* s, size_t n2 ) {
String tmp( s );
mString.replace( pos1, n1, tmp.data(), n2 );
return *this;
}
String& String::replace( Iterator i1, Iterator i2, const char* s, size_t n2 ) {
String tmp( s );
mString.replace( i1, i2, tmp.data(), n2 );
return *this;
}
String& String::replace( size_t pos1, size_t n1, const char* s ) {
String tmp( s );
mString.replace( pos1, n1, tmp.mString );
return *this;
}
String& String::replace( Iterator i1, Iterator i2, const char* s ) {
String tmp( s );
mString.replace( i1, i2, tmp.mString );
return *this;
}
String& String::replace( size_t pos1, size_t n1, size_t n2, char c ) {
mString.replace( pos1, n1, n2, (StringBaseType)c );
return *this;
}
String& String::replace( Iterator i1, Iterator i2, size_t n2, char c ) {
mString.replace( i1, i2, n2, (StringBaseType)c );
return *this;
}
std::size_t String::find( const String& str, std::size_t start ) const {
return mString.find( str.mString, start );
}
std::size_t String::find( const char* s, std::size_t pos, std::size_t n ) const {
return find( String( s ), pos );
}
std::size_t String::find( const char* s, std::size_t pos ) const {
return find( String( s ), pos );
}
size_t String::find( char c, std::size_t pos ) const {
return mString.find( (StringBaseType)c, pos );
}
std::size_t String::rfind( const String& str, std::size_t pos ) const {
return mString.rfind( str.mString, pos );
}
std::size_t String::rfind( const char* s, std::size_t pos, std::size_t n ) const {
return rfind( String( s ), pos );
}
std::size_t String::rfind( const char* s, std::size_t pos ) const {
return rfind( String( s ), pos );
}
std::size_t String::rfind( char c, std::size_t pos ) const {
return mString.rfind( c, pos );
}
std::size_t String::copy( StringBaseType* s, std::size_t n, std::size_t pos ) const {
return mString.copy( s, n, pos );
}
String String::substr( std::size_t pos, std::size_t n ) const {
return String( mString.substr( pos, n ) );
}
int String::compare( const String& str ) const {
return mString.compare( str.mString );
}
int String::compare( const char* s ) const {
return compare( String( s ) );
}
int String::compare( std::size_t pos1, std::size_t n1, const String& str ) const {
return mString.compare( pos1, n1, str.mString );
}
int String::compare( std::size_t pos1, std::size_t n1, const char* s ) const {
return compare( pos1, n1, String( s ) );
}
int String::compare( std::size_t pos1, std::size_t n1, const String& str, std::size_t pos2,
std::size_t n2 ) const {
return mString.compare( pos1, n1, str.mString, pos2, n2 );
}
int String::compare( std::size_t pos1, std::size_t n1, const char* s, std::size_t n2 ) const {
return compare( pos1, n1, String( s ), 0, n2 );
}
std::size_t String::find_first_of( const String& str, std::size_t pos ) const {
return mString.find_first_of( str.mString, pos );
}
std::size_t String::find_first_of( const char* s, std::size_t pos, std::size_t n ) const {
return find_first_of( String( s ), pos );
}
std::size_t String::find_first_of( const char* s, std::size_t pos ) const {
return find_first_of( String( s ), pos );
}
std::size_t String::find_first_of( StringBaseType c, std::size_t pos ) const {
return mString.find_first_of( c, pos );
}
std::size_t String::find_last_of( const String& str, std::size_t pos ) const {
return mString.find_last_of( str.mString, pos );
}
std::size_t String::find_last_of( const char* s, std::size_t pos, std::size_t n ) const {
return find_last_of( String( s ), pos );
}
std::size_t String::find_last_of( const char* s, std::size_t pos ) const {
return find_last_of( String( s ), pos );
}
std::size_t String::find_last_of( StringBaseType c, std::size_t pos ) const {
return mString.find_last_of( c, pos );
}
std::size_t String::find_first_not_of( const String& str, std::size_t pos ) const {
return mString.find_first_not_of( str.mString, pos );
}
std::size_t String::find_first_not_of( const char* s, std::size_t pos, std::size_t n ) const {
return find_first_not_of( String( s ), pos );
}
std::size_t String::find_first_not_of( const char* s, std::size_t pos ) const {
return find_first_not_of( String( s ), pos );
}
std::size_t String::find_first_not_of( StringBaseType c, std::size_t pos ) const {
return mString.find_first_not_of( c, pos );
}
std::size_t String::find_last_not_of( const String& str, std::size_t pos ) const {
return mString.find_last_not_of( str.mString, pos );
}
std::size_t String::find_last_not_of( const char* s, std::size_t pos, std::size_t n ) const {
return find_last_not_of( String( s ), pos );
}
std::size_t String::find_last_not_of( const char* s, std::size_t pos ) const {
return find_last_not_of( String( s ), pos );
}
std::size_t String::find_last_not_of( StringBaseType c, std::size_t pos ) const {
return mString.find_last_not_of( c, pos );
}
bool operator==( const String& left, const String& right ) {
return left.mString == right.mString;
}
bool operator!=( const String& left, const String& right ) {
return !( left == right );
}
bool operator<( const String& left, const String& right ) {
return left.mString < right.mString;
}
bool operator>( const String& left, const String& right ) {
return right < left;
}
bool operator<=( const String& left, const String& right ) {
return !( right < left );
}
bool operator>=( const String& left, const String& right ) {
return !( left < right );
}
String operator+( const String& left, const String& right ) {
String string = left;
string += right;
return string;
}
} // namespace efsw

630
src/efsw/String.hpp Normal file
View File

@ -0,0 +1,630 @@
/** NOTE:
* This code is based on the Utf implementation from SFML2. License zlib/png (
*http://www.sfml-dev.org/license.php ) The class was modified to fit efsw own needs. This is not
*the original implementation from SFML2. Functions and methods are the same that in std::string to
*facilitate portability.
**/
#ifndef EFSW_STRING_HPP
#define EFSW_STRING_HPP
#include <cstdlib>
#include <cstring>
#include <efsw/base.hpp>
#include <iostream>
#include <locale>
#include <sstream>
#include <string>
#include <vector>
namespace efsw {
/** @brief Utility string class that automatically handles conversions between types and encodings
* **/
class String {
public:
typedef char32_t StringBaseType;
typedef std::basic_string<StringBaseType> StringType;
typedef StringType::iterator Iterator; //! Iterator type
typedef StringType::const_iterator ConstIterator; //! Constant iterator type
typedef StringType::reverse_iterator ReverseIterator; //! Reverse Iterator type
typedef StringType::const_reverse_iterator ConstReverseIterator; //! Constant iterator type
static const std::size_t InvalidPos; ///< Represents an invalid position in the string
template <class T> static std::string toStr( const T& i ) {
std::ostringstream ss;
ss << i;
return ss.str();
}
/** Converts from a string to type */
template <class T>
static bool fromString( T& t, const std::string& s,
std::ios_base& ( *f )( std::ios_base& ) = std::dec ) {
std::istringstream iss( s );
return !( iss >> f >> t ).fail();
}
/** Converts from a String to type */
template <class T>
static bool fromString( T& t, const String& s,
std::ios_base& ( *f )( std::ios_base& ) = std::dec ) {
std::istringstream iss( s.toUtf8() );
return !( iss >> f >> t ).fail();
}
/** Split a string and hold it on a vector */
static std::vector<std::string> split( const std::string& str, const char& splitchar,
const bool& pushEmptyString = false );
/** Split a string and hold it on a vector */
static std::vector<String> split( const String& str, const Uint32& splitchar,
const bool& pushEmptyString = false );
/** Determine if a string starts with the string passed
** @param start The substring expected to start
** @param str The string to compare
** @return -1 if the substring is no in str, otherwise the size of the substring
*/
static int strStartsWith( const std::string& start, const std::string& str );
static int strStartsWith( const String& start, const String& str );
/** @brief Construct from an UTF-8 string to UTF-32 according
** @param uf8String UTF-8 string to convert
**/
static String fromUtf8( const std::string& utf8String );
/** @brief Default constructor
** This constructor creates an empty string.
**/
String();
/** @brief Construct from a single ANSI character and a locale
** The source character is converted to UTF-32 according
** to the given locale. If you want to use the current global
** locale, rather use the other constructor.
** @param ansiChar ANSI character to convert
** @param locale Locale to use for conversion
**/
String( char ansiChar, const std::locale& locale = std::locale() );
#ifndef EFSW_NO_WIDECHAR
/** @brief Construct from single wide character
** @param wideChar Wide character to convert
**/
String( wchar_t wideChar );
#endif
/** @brief Construct from single UTF-32 character
** @param utf32Char UTF-32 character to convert
**/
String( StringBaseType utf32Char );
/** @brief Construct from an from a null-terminated C-style UTF-8 string to UTF-32
** @param uf8String UTF-8 string to convert
**/
String( const char* uf8String );
/** @brief Construct from an UTF-8 string to UTF-32 according
** @param uf8String UTF-8 string to convert
**/
String( const std::string& utf8String );
/** @brief Construct from a null-terminated C-style ANSI string and a locale
** The source string is converted to UTF-32 according
** to the given locale. If you want to use the current global
** locale, rather use the other constructor.
** @param ansiString ANSI string to convert
** @param locale Locale to use for conversion
**/
String( const char* ansiString, const std::locale& locale );
/** @brief Construct from an ANSI string and a locale
** The source string is converted to UTF-32 according
** to the given locale. If you want to use the current global
** locale, rather use the other constructor.
** @param ansiString ANSI string to convert
** @param locale Locale to use for conversion
**/
String( const std::string& ansiString, const std::locale& locale );
#ifndef EFSW_NO_WIDECHAR
/** @brief Construct from null-terminated C-style wide string
** @param wideString Wide string to convert
**/
String( const wchar_t* wideString );
/** @brief Construct from a wide string
** @param wideString Wide string to convert
**/
String( const std::wstring& wideString );
#endif
/** @brief Construct from a null-terminated C-style UTF-32 string
** @param utf32String UTF-32 string to assign
**/
String( const StringBaseType* utf32String );
/** @brief Construct from an UTF-32 string
** @param utf32String UTF-32 string to assign
**/
String( const StringType& utf32String );
/** @brief Copy constructor
** @param str Instance to copy
**/
String( const String& str );
/** @brief Implicit cast operator to std::string (ANSI string)
** The current global locale is used for conversion. If you
** want to explicitely specify a locale, see toAnsiString.
** Characters that do not fit in the target encoding are
** discarded from the returned string.
** This operator is defined for convenience, and is equivalent
** to calling toAnsiString().
** @return Converted ANSI string
** @see toAnsiString, operator String
**/
operator std::string() const;
/** @brief Convert the unicode string to an ANSI string
** The UTF-32 string is converted to an ANSI string in
** the encoding defined by \a locale. If you want to use
** the current global locale, see the other overload
** of toAnsiString.
** Characters that do not fit in the target encoding are
** discarded from the returned string.
** @param locale Locale to use for conversion
** @return Converted ANSI string
** @see toWideString, operator std::string
**/
std::string toAnsiString( const std::locale& locale = std::locale() ) const;
#ifndef EFSW_NO_WIDECHAR
/** @brief Convert the unicode string to a wide string
** Characters that do not fit in the target encoding are
** discarded from the returned string.
** @return Converted wide string
** @see toAnsiString, operator String
**/
std::wstring toWideString() const;
#endif
std::string toUtf8() const;
/** @brief Overload of assignment operator
** @param right Instance to assign
** @return Reference to self
**/
String& operator=( const String& right );
String& operator=( const StringBaseType& right );
/** @brief Overload of += operator to append an UTF-32 string
** @param right String to append
** @return Reference to self
**/
String& operator+=( const String& right );
String& operator+=( const StringBaseType& right );
/** @brief Overload of [] operator to access a character by its position
** This function provides read-only access to characters.
** Note: this function doesn't throw if \a index is out of range.
** @param index Index of the character to get
** @return Character at position \a index
**/
StringBaseType operator[]( std::size_t index ) const;
/** @brief Overload of [] operator to access a character by its position
** This function provides read and write access to characters.
** Note: this function doesn't throw if \a index is out of range.
** @param index Index of the character to get
** @return Reference to the character at position \a index
**/
StringBaseType& operator[]( std::size_t index );
/** @brief Get character in string
** Performs a range check, throwing an exception of type out_of_range in case that pos is not an
*actual position in the string.
** @return The character at position pos in the string.
*/
StringBaseType at( std::size_t index ) const;
/** @brief clear the string
** This function removes all the characters from the string.
** @see empty, erase
**/
void clear();
/** @brief Get the size of the string
** @return Number of characters in the string
** @see empty
**/
std::size_t size() const;
/** @see size() */
std::size_t length() const;
/** @brief Check whether the string is empty or not
** @return True if the string is empty (i.e. contains no character)
** @see clear, size
**/
bool empty() const;
/** @brief Erase one or more characters from the string
** This function removes a sequence of \a count characters
** starting from \a position.
** @param position Position of the first character to erase
** @param count Number of characters to erase
**/
void erase( std::size_t position, std::size_t count = 1 );
/** @brief Insert one or more characters into the string
** This function inserts the characters of \a str
** into the string, starting from \a position.
** @param position Position of insertion
** @param str Characters to insert
**/
String& insert( std::size_t position, const String& str );
String& insert( std::size_t pos1, const String& str, std::size_t pos2, std::size_t n );
String& insert( std::size_t pos1, const char* s, std::size_t n );
String& insert( std::size_t pos1, const char* s );
String& insert( std::size_t pos1, size_t n, char c );
Iterator insert( Iterator p, char c );
void insert( Iterator p, std::size_t n, char c );
template <class InputIterator>
void insert( Iterator p, InputIterator first, InputIterator last ) {
mString.insert( p, first, last );
}
/** @brief Find a sequence of one or more characters in the string
** This function searches for the characters of \a str
** into the string, starting from \a start.
** @param str Characters to find
** @param start Where to begin searching
** @return Position of \a str in the string, or String::InvalidPos if not found
**/
std::size_t find( const String& str, std::size_t start = 0 ) const;
std::size_t find( const char* s, std::size_t pos, std::size_t n ) const;
std::size_t find( const char* s, std::size_t pos = 0 ) const;
std::size_t find( char c, std::size_t pos = 0 ) const;
/** @brief Get a pointer to the C-style array of characters
** This functions provides a read-only access to a
** null-terminated C-style representation of the string.
** The returned pointer is temporary and is meant only for
** immediate use, thus it is not recommended to store it.
** @return Read-only pointer to the array of characters
**/
const StringBaseType* c_str() const;
/** @brief Get string data
** Notice that no terminating null character is appended (see member c_str for such a
*functionality).
** The returned array points to an internal location which should not be modified directly in
*the program.
** Its contents are guaranteed to remain unchanged only until the next call to a non-constant
*member function of the string object.
** @return Pointer to an internal array containing the same content as the string.
**/
const StringBaseType* data() const;
/** @brief Return an iterator to the beginning of the string
** @return Read-write iterator to the beginning of the string characters
** @see end
**/
Iterator begin();
/** @brief Return an iterator to the beginning of the string
** @return Read-only iterator to the beginning of the string characters
** @see end
**/
ConstIterator begin() const;
/** @brief Return an iterator to the beginning of the string
** The end iterator refers to 1 position past the last character;
** thus it represents an invalid character and should never be
** accessed.
** @return Read-write iterator to the end of the string characters
** @see begin
**/
Iterator end();
/** @brief Return an iterator to the beginning of the string
** The end iterator refers to 1 position past the last character;
** thus it represents an invalid character and should never be
** accessed.
** @return Read-only iterator to the end of the string characters
** @see begin
**/
ConstIterator end() const;
/** @brief Return an reverse iterator to the beginning of the string
** @return Read-write reverse iterator to the beginning of the string characters
** @see end
**/
ReverseIterator rbegin();
/** @brief Return an reverse iterator to the beginning of the string
** @return Read-only reverse iterator to the beginning of the string characters
** @see end
**/
ConstReverseIterator rbegin() const;
/** @brief Return an reverse iterator to the beginning of the string
** The end reverse iterator refers to 1 position past the last character;
** thus it represents an invalid character and should never be
** accessed.
** @return Read-write reverse iterator to the end of the string characters
** @see begin
**/
ReverseIterator rend();
/** @brief Return an reverse iterator to the beginning of the string
** The end reverse iterator refers to 1 position past the last character;
** thus it represents an invalid character and should never be
** accessed.
** @return Read-only reverse iterator to the end of the string characters
** @see begin
**/
ConstReverseIterator rend() const;
/** @brief Resize String */
void resize( std::size_t n, StringBaseType c );
/** @brief Resize String */
void resize( std::size_t n );
/** @return Maximum size of string */
std::size_t max_size() const;
/** @brief Request a change in capacity */
void reserve( size_t res_arg = 0 );
/** @return Size of allocated storage */
std::size_t capacity() const;
/** @brief Append character to string */
void push_back( StringBaseType c );
/** @brief Swap contents with another string */
void swap( String& str );
String& assign( const String& str );
String& assign( const String& str, std::size_t pos, std::size_t n );
String& assign( const char* s, std::size_t n );
String& assign( const char* s );
String& assign( std::size_t n, char c );
template <class InputIterator> String& assign( InputIterator first, InputIterator last ) {
mString.assign( first, last );
return *this;
}
String& append( const String& str );
String& append( const String& str, std::size_t pos, std::size_t n );
String& append( const char* s, std::size_t n );
String& append( const char* s );
String& append( std::size_t n, char c );
String& append( std::size_t n, StringBaseType c );
template <class InputIterator> String& append( InputIterator first, InputIterator last ) {
mString.append( first, last );
return *this;
}
String& replace( std::size_t pos1, std::size_t n1, const String& str );
String& replace( Iterator i1, Iterator i2, const String& str );
String& replace( std::size_t pos1, std::size_t n1, const String& str, std::size_t pos2,
std::size_t n2 );
String& replace( std::size_t pos1, std::size_t n1, const char* s, std::size_t n2 );
String& replace( Iterator i1, Iterator i2, const char* s, std::size_t n2 );
String& replace( std::size_t pos1, std::size_t n1, const char* s );
String& replace( Iterator i1, Iterator i2, const char* s );
String& replace( std::size_t pos1, std::size_t n1, std::size_t n2, char c );
String& replace( Iterator i1, Iterator i2, std::size_t n2, char c );
template <class InputIterator>
String& replace( Iterator i1, Iterator i2, InputIterator j1, InputIterator j2 ) {
mString.replace( i1, i2, j1, j2 );
return *this;
}
std::size_t rfind( const String& str, std::size_t pos = StringType::npos ) const;
std::size_t rfind( const char* s, std::size_t pos, std::size_t n ) const;
std::size_t rfind( const char* s, std::size_t pos = StringType::npos ) const;
std::size_t rfind( char c, std::size_t pos = StringType::npos ) const;
String substr( std::size_t pos = 0, std::size_t n = StringType::npos ) const;
std::size_t copy( StringBaseType* s, std::size_t n, std::size_t pos = 0 ) const;
int compare( const String& str ) const;
int compare( const char* s ) const;
int compare( std::size_t pos1, std::size_t n1, const String& str ) const;
int compare( std::size_t pos1, std::size_t n1, const char* s ) const;
int compare( std::size_t pos1, std::size_t n1, const String& str, std::size_t pos2,
std::size_t n2 ) const;
int compare( std::size_t pos1, std::size_t n1, const char* s, std::size_t n2 ) const;
std::size_t find_first_of( const String& str, std::size_t pos = 0 ) const;
std::size_t find_first_of( const char* s, std::size_t pos, std::size_t n ) const;
std::size_t find_first_of( const char* s, std::size_t pos = 0 ) const;
std::size_t find_first_of( StringBaseType c, std::size_t pos = 0 ) const;
std::size_t find_last_of( const String& str, std::size_t pos = StringType::npos ) const;
std::size_t find_last_of( const char* s, std::size_t pos, std::size_t n ) const;
std::size_t find_last_of( const char* s, std::size_t pos = StringType::npos ) const;
std::size_t find_last_of( StringBaseType c, std::size_t pos = StringType::npos ) const;
std::size_t find_first_not_of( const String& str, std::size_t pos = 0 ) const;
std::size_t find_first_not_of( const char* s, std::size_t pos, std::size_t n ) const;
std::size_t find_first_not_of( const char* s, std::size_t pos = 0 ) const;
std::size_t find_first_not_of( StringBaseType c, std::size_t pos = 0 ) const;
std::size_t find_last_not_of( const String& str, std::size_t pos = StringType::npos ) const;
std::size_t find_last_not_of( const char* s, std::size_t pos, std::size_t n ) const;
std::size_t find_last_not_of( const char* s, std::size_t pos = StringType::npos ) const;
std::size_t find_last_not_of( StringBaseType c, std::size_t pos = StringType::npos ) const;
private:
friend bool operator==( const String& left, const String& right );
friend bool operator<( const String& left, const String& right );
StringType mString; ///< Internal string of UTF-32 characters
};
/** @relates String
** @brief Overload of == operator to compare two UTF-32 strings
** @param left Left operand (a string)
** @param right Right operand (a string)
** @return True if both strings are equal
**/
bool operator==( const String& left, const String& right );
/** @relates String
** @brief Overload of != operator to compare two UTF-32 strings
** @param left Left operand (a string)
** @param right Right operand (a string)
** @return True if both strings are different
**/
bool operator!=( const String& left, const String& right );
/** @relates String
** @brief Overload of < operator to compare two UTF-32 strings
** @param left Left operand (a string)
** @param right Right operand (a string)
** @return True if \a left is alphabetically lesser than \a right
**/
bool operator<( const String& left, const String& right );
/** @relates String
** @brief Overload of > operator to compare two UTF-32 strings
** @param left Left operand (a string)
** @param right Right operand (a string)
** @return True if \a left is alphabetically greater than \a right
**/
bool operator>( const String& left, const String& right );
/** @relates String
** @brief Overload of <= operator to compare two UTF-32 strings
** @param left Left operand (a string)
** @param right Right operand (a string)
** @return True if \a left is alphabetically lesser or equal than \a right
**/
bool operator<=( const String& left, const String& right );
/** @relates String
** @brief Overload of >= operator to compare two UTF-32 strings
** @param left Left operand (a string)
** @param right Right operand (a string)
** @return True if \a left is alphabetically greater or equal than \a right
**/
bool operator>=( const String& left, const String& right );
/** @relates String
** @brief Overload of binary + operator to concatenate two strings
** @param left Left operand (a string)
** @param right Right operand (a string)
** @return Concatenated string
**/
String operator+( const String& left, const String& right );
} // namespace efsw
#endif
/** @class efsw::String
** @ingroup system
** efsw::String is a utility string class defined mainly for
** convenience. It is a Unicode string (implemented using
** UTF-32), thus it can store any character in the world
** (european, chinese, arabic, hebrew, etc.).
** It automatically handles conversions from/to ANSI and
** wide strings, so that you can work with standard string
** classes and still be compatible with functions taking a
** efsw::String.
** @code
** efsw::String s;
** std::string s1 = s; // automatically converted to ANSI string
** String s2 = s; // automatically converted to wide string
** s = "hello"; // automatically converted from ANSI string
** s = L"hello"; // automatically converted from wide string
** s += 'a'; // automatically converted from ANSI string
** s += L'a'; // automatically converted from wide string
** @endcode
** Conversions involving ANSI strings use the default user locale. However
** it is possible to use a custom locale if necessary:
** @code
** std::locale locale;
** efsw::String s;
** ...
** std::string s1 = s.toAnsiString(locale);
** s = efsw::String("hello", locale);
** @endcode
**
** efsw::String defines the most important functions of the
** standard std::string class: removing, random access, iterating,
** appending, comparing, etc. However it is a simple class
** provided for convenience, and you may have to consider using
** a more optimized class if your program requires complex string
** handling. The automatic conversion functions will then take
** care of converting your string to efsw::String whenever EE
** requires it.
**
** Please note that EE also defines a low-level, generic
** interface for Unicode handling, see the efsw::Utf classes.
**
** All credits to Laurent Gomila, i just modified and expanded a little bit the implementation.
**/

22
src/efsw/System.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <efsw/System.hpp>
#include <efsw/platform/platformimpl.hpp>
namespace efsw {
void System::sleep( const unsigned long& ms ) {
Platform::System::sleep( ms );
}
std::string System::getProcessPath() {
return Platform::System::getProcessPath();
}
void System::maxFD() {
Platform::System::maxFD();
}
Uint64 System::getMaxFD() {
return Platform::System::getMaxFD();
}
} // namespace efsw

25
src/efsw/System.hpp Normal file
View File

@ -0,0 +1,25 @@
#ifndef EFSW_SYSTEM_HPP
#define EFSW_SYSTEM_HPP
#include <efsw/base.hpp>
namespace efsw {
class System {
public:
/// Sleep for x milliseconds
static void sleep( const unsigned long& ms );
/// @return The process binary path
static std::string getProcessPath();
/// Maximize the number of file descriptors allowed per process in the current OS
static void maxFD();
/// @return The number of supported file descriptors for the process
static Uint64 getMaxFD();
};
} // namespace efsw
#endif

49
src/efsw/Thread.hpp Normal file
View File

@ -0,0 +1,49 @@
#ifndef EFSW_THREAD_HPP
#define EFSW_THREAD_HPP
#include <efsw/base.hpp>
#include <functional>
#include <memory>
#include <thread>
namespace efsw {
/** @brief Thread manager class */
class Thread {
public:
Thread(std::function<void()> fun)
: mFun{std::move(fun)}
{
}
~Thread()
{
wait();
}
/** Launch the thread */
void launch()
{
if (!mThread)
mThread.reset(new std::thread{std::move(mFun)});
}
/** Wait the thread until end */
void wait()
{
if (mThread)
{
mThread->join();
mThread.reset();
}
}
private:
std::unique_ptr<std::thread> mThread;
std::function<void()> mFun;
};
} // namespace efsw
#endif

721
src/efsw/Utf.hpp Normal file
View File

@ -0,0 +1,721 @@
/** NOTE:
* This code is based on the Utf implementation from SFML2. License zlib/png (
*http://www.sfml-dev.org/license.php ) The class was modified to fit efsw own needs. This is not
*the original implementation from SFML2.
* */
#ifndef EFSW_UTF_HPP
#define EFSW_UTF_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <cstdlib>
#include <efsw/base.hpp>
#include <locale>
#include <string>
namespace efsw {
template <unsigned int N> class Utf;
////////////////////////////////////////////////////////////
/// \brief Specialization of the Utf template for UTF-8
///
////////////////////////////////////////////////////////////
template <> class Utf<8> {
public:
////////////////////////////////////////////////////////////
/// \brief Decode a single UTF-8 character
///
/// Decoding a character means finding its unique 32-bits
/// code (called the codepoint) in the Unicode standard.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Codepoint of the decoded UTF-8 character
/// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-8 character
///
/// Encoding a character means converting a unique 32-bits
/// code (called the codepoint) in the target encoding, UTF-8.
///
/// \param input Codepoint to encode as UTF-8
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to UTF-8 (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out> static Out Encode( Uint32 input, Out output, Uint8 replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Advance to the next UTF-8 character
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In> static In Next( In begin, In end );
////////////////////////////////////////////////////////////
/// \brief Count the number of characters of a UTF-8 sequence
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element, thus the
/// total size can be different from (begin - end).
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In> static std::size_t Count( In begin, In end );
////////////////////////////////////////////////////////////
/// \brief Convert an ANSI characters range to UTF-8
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the \a locale parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() );
////////////////////////////////////////////////////////////
/// \brief Convert a wide characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out FromWide( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-8 characters range to ANSI characters
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the \a locale parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToAnsi( In begin, In end, Out output, char replacement = 0,
const std::locale& locale = std::locale() );
#ifndef EFSW_NO_WIDECHAR
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-8 characters range to wide characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 );
#endif
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-8 characters range to latin-1 (ISO-5589-1) characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToLatin1( In begin, In end, Out output, char replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-8 characters range to UTF-8
///
/// This functions does nothing more than a direct copy;
/// it is defined only to provide the same interface as other
/// specializations of the efsw::Utf<> template, and allow
/// generic code to be written on top of it.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-8 characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-8 characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output );
};
////////////////////////////////////////////////////////////
/// \brief Specialization of the Utf template for UTF-16
///
////////////////////////////////////////////////////////////
template <> class Utf<16> {
public:
////////////////////////////////////////////////////////////
/// \brief Decode a single UTF-16 character
///
/// Decoding a character means finding its unique 32-bits
/// code (called the codepoint) in the Unicode standard.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Codepoint of the decoded UTF-16 character
/// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-16 character
///
/// Encoding a character means converting a unique 32-bits
/// code (called the codepoint) in the target encoding, UTF-16.
///
/// \param input Codepoint to encode as UTF-16
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to UTF-16 (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out> static Out Encode( Uint32 input, Out output, Uint16 replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Advance to the next UTF-16 character
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In> static In Next( In begin, In end );
////////////////////////////////////////////////////////////
/// \brief Count the number of characters of a UTF-16 sequence
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element, thus the
/// total size can be different from (begin - end).
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In> static std::size_t Count( In begin, In end );
////////////////////////////////////////////////////////////
/// \brief Convert an ANSI characters range to UTF-16
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the \a locale parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() );
////////////////////////////////////////////////////////////
/// \brief Convert a wide characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out FromWide( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to ANSI characters
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the \a locale parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToAnsi( In begin, In end, Out output, char replacement = 0,
const std::locale& locale = std::locale() );
#ifndef EFSW_NO_WIDECHAR
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to wide characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 );
#endif
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToLatin1( In begin, In end, Out output, char replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-16 characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-16 characters range to UTF-16
///
/// This functions does nothing more than a direct copy;
/// it is defined only to provide the same interface as other
/// specializations of the efsw::Utf<> template, and allow
/// generic code to be written on top of it.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-16 characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output );
};
////////////////////////////////////////////////////////////
/// \brief Specialization of the Utf template for UTF-32
///
////////////////////////////////////////////////////////////
template <> class Utf<32> {
public:
////////////////////////////////////////////////////////////
/// \brief Decode a single UTF-32 character
///
/// Decoding a character means finding its unique 32-bits
/// code (called the codepoint) in the Unicode standard.
/// For UTF-32, the character value is the same as the codepoint.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Codepoint of the decoded UTF-32 character
/// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-32 character
///
/// Encoding a character means converting a unique 32-bits
/// code (called the codepoint) in the target encoding, UTF-32.
/// For UTF-32, the codepoint is the same as the character value.
///
/// \param input Codepoint to encode as UTF-32
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to UTF-32 (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out> static Out Encode( Uint32 input, Out output, Uint32 replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Advance to the next UTF-32 character
///
/// This function is trivial for UTF-32, which can store
/// every character in a single storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In> static In Next( In begin, In end );
////////////////////////////////////////////////////////////
/// \brief Count the number of characters of a UTF-32 sequence
///
/// This function is trivial for UTF-32, which can store
/// every character in a single storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In> static std::size_t Count( In begin, In end );
////////////////////////////////////////////////////////////
/// \brief Convert an ANSI characters range to UTF-32
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the \a locale parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() );
////////////////////////////////////////////////////////////
/// \brief Convert a wide characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out FromWide( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-32 characters range to ANSI characters
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the \a locale parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToAnsi( In begin, In end, Out output, char replacement = 0,
const std::locale& locale = std::locale() );
#ifndef EFSW_NO_WIDECHAR
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-32 characters range to wide characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 );
#endif
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out ToLatin1( In begin, In end, Out output, char replacement = 0 );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-32 characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-32 characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-32 characters range to UTF-32
///
/// This functions does nothing more than a direct copy;
/// it is defined only to provide the same interface as other
/// specializations of the efsw::Utf<> template, and allow
/// generic code to be written on top of it.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output );
////////////////////////////////////////////////////////////
/// \brief Decode a single ANSI character to UTF-32
///
/// This function does not exist in other specializations
/// of efsw::Utf<>, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param input Input ANSI character
/// \param locale Locale to use for conversion
///
/// \return Converted character
///
////////////////////////////////////////////////////////////
template <typename In>
static Uint32 DecodeAnsi( In input, const std::locale& locale = std::locale() );
////////////////////////////////////////////////////////////
/// \brief Decode a single wide character to UTF-32
///
/// This function does not exist in other specializations
/// of efsw::Utf<>, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param input Input wide character
///
/// \return Converted character
///
////////////////////////////////////////////////////////////
template <typename In> static Uint32 DecodeWide( In input );
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-32 character to ANSI
///
/// This function does not exist in other specializations
/// of efsw::Utf<>, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param codepoint Iterator pointing to the beginning of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement if the input character is not convertible to ANSI (use 0 to
/// skip it) \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out>
static Out EncodeAnsi( Uint32 codepoint, Out output, char replacement = 0,
const std::locale& locale = std::locale() );
#ifndef EFSW_NO_WIDECHAR
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-32 character to wide
///
/// This function does not exist in other specializations
/// of efsw::Utf<>, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param codepoint Iterator pointing to the beginning of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement if the input character is not convertible to wide (use 0 to
/// skip it)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out>
static Out EncodeWide( Uint32 codepoint, Out output, wchar_t replacement = 0 );
#endif
};
#include "Utf.inl"
// Make typedefs to get rid of the template syntax
typedef Utf<8> Utf8;
typedef Utf<16> Utf16;
typedef Utf<32> Utf32;
} // namespace efsw
#endif
////////////////////////////////////////////////////////////
/// \class efsw::Utf
/// \ingroup system
///
/// Utility class providing generic functions for UTF conversions.
///
/// efsw::Utf is a low-level, generic interface for counting, iterating,
/// encoding and decoding Unicode characters and strings. It is able
/// to handle ANSI, wide, UTF-8, UTF-16 and UTF-32 encodings.
///
/// efsw::Utf<X> functions are all static, these classes are not meant to
/// be instanciated. All the functions are template, so that you
/// can use any character / string type for a given encoding.
///
/// It has 3 specializations:
/// \li efsw::Utf<8> (typedef'd to efsw::Utf8)
/// \li efsw::Utf<16> (typedef'd to efsw::Utf16)
/// \li efsw::Utf<32> (typedef'd to efsw::Utf32)
///
////////////////////////////////////////////////////////////

576
src/efsw/Utf.inl Normal file
View File

@ -0,0 +1,576 @@
// References :
// http://www.unicode.org/
// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c
// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.h
// http://people.w3.org/rishida/scripts/uniview/conversion
////////////////////////////////////////////////////////////
template <typename In> In Utf<8>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) {
// Some useful precomputed data
static const int trailing[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
static const Uint32 offsets[6] = { 0x00000000, 0x00003080, 0x000E2080,
0x03C82080, 0xFA082080, 0x82082080 };
// Decode the character
int trailingBytes = trailing[static_cast<Uint8>( *begin )];
if ( begin + trailingBytes < end ) {
output = 0;
switch ( trailingBytes ) {
case 5:
output += static_cast<Uint8>( *begin++ );
output <<= 6;
case 4:
output += static_cast<Uint8>( *begin++ );
output <<= 6;
case 3:
output += static_cast<Uint8>( *begin++ );
output <<= 6;
case 2:
output += static_cast<Uint8>( *begin++ );
output <<= 6;
case 1:
output += static_cast<Uint8>( *begin++ );
output <<= 6;
case 0:
output += static_cast<Uint8>( *begin++ );
}
output -= offsets[trailingBytes];
} else {
// Incomplete character
begin = end;
output = replacement;
}
return begin;
}
template <typename Out> Out Utf<8>::Encode( Uint32 input, Out output, Uint8 replacement ) {
// Some useful precomputed data
static const Uint8 firstBytes[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
// Encode the character
if ( ( input > 0x0010FFFF ) || ( ( input >= 0xD800 ) && ( input <= 0xDBFF ) ) ) {
// Invalid character
if ( replacement )
*output++ = replacement;
} else {
// Valid character
// Get the number of bytes to write
int bytesToWrite = 1;
if ( input < 0x80 )
bytesToWrite = 1;
else if ( input < 0x800 )
bytesToWrite = 2;
else if ( input < 0x10000 )
bytesToWrite = 3;
else if ( input <= 0x0010FFFF )
bytesToWrite = 4;
// Extract the bytes to write
Uint8 bytes[4];
switch ( bytesToWrite ) {
case 4:
bytes[3] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF );
input >>= 6;
case 3:
bytes[2] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF );
input >>= 6;
case 2:
bytes[1] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF );
input >>= 6;
case 1:
bytes[0] = static_cast<Uint8>( input | firstBytes[bytesToWrite] );
}
// Add them to the output
const Uint8* currentByte = bytes;
switch ( bytesToWrite ) {
case 4:
*output++ = *currentByte++;
case 3:
*output++ = *currentByte++;
case 2:
*output++ = *currentByte++;
case 1:
*output++ = *currentByte++;
}
}
return output;
}
template <typename In> In Utf<8>::Next( In begin, In end ) {
Uint32 codepoint;
return Decode( begin, end, codepoint );
}
template <typename In> std::size_t Utf<8>::Count( In begin, In end ) {
std::size_t length = 0;
while ( begin < end ) {
begin = Next( begin, end );
++length;
}
return length;
}
template <typename In, typename Out>
Out Utf<8>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) {
while ( begin < end ) {
Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale );
output = Encode( codepoint, output );
}
return output;
}
template <typename In, typename Out> Out Utf<8>::FromWide( In begin, In end, Out output ) {
while ( begin < end ) {
Uint32 codepoint = Utf<32>::DecodeWide( *begin++ );
output = Encode( codepoint, output );
}
return output;
}
template <typename In, typename Out> Out Utf<8>::FromLatin1( In begin, In end, Out output ) {
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while ( begin < end )
output = Encode( *begin++, output );
return output;
}
template <typename In, typename Out>
Out Utf<8>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale );
}
return output;
}
#ifndef EFSW_NO_WIDECHAR
template <typename In, typename Out>
Out Utf<8>::ToWide( In begin, In end, Out output, wchar_t replacement ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
output = Utf<32>::EncodeWide( codepoint, output, replacement );
}
return output;
}
#endif
template <typename In, typename Out>
Out Utf<8>::ToLatin1( In begin, In end, Out output, char replacement ) {
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
*output++ = codepoint < 256 ? static_cast<char>( codepoint ) : replacement;
}
return output;
}
template <typename In, typename Out> Out Utf<8>::toUtf8( In begin, In end, Out output ) {
while ( begin < end )
*output++ = *begin++;
return output;
}
template <typename In, typename Out> Out Utf<8>::ToUtf16( In begin, In end, Out output ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
output = Utf<16>::Encode( codepoint, output );
}
return output;
}
template <typename In, typename Out> Out Utf<8>::ToUtf32( In begin, In end, Out output ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
*output++ = codepoint;
}
return output;
}
template <typename In> In Utf<16>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) {
Uint16 first = *begin++;
// If it's a surrogate pair, first convert to a single UTF-32 character
if ( ( first >= 0xD800 ) && ( first <= 0xDBFF ) ) {
if ( begin < end ) {
Uint32 second = *begin++;
if ( ( second >= 0xDC00 ) && ( second <= 0xDFFF ) ) {
// The second element is valid: convert the two elements to a UTF-32 character
output = static_cast<Uint32>( ( ( first - 0xD800 ) << 10 ) + ( second - 0xDC00 ) +
0x0010000 );
} else {
// Invalid character
output = replacement;
}
} else {
// Invalid character
begin = end;
output = replacement;
}
} else {
// We can make a direct copy
output = first;
}
return begin;
}
template <typename Out> Out Utf<16>::Encode( Uint32 input, Out output, Uint16 replacement ) {
if ( input < 0xFFFF ) {
// The character can be copied directly, we just need to check if it's in the valid range
if ( ( input >= 0xD800 ) && ( input <= 0xDFFF ) ) {
// Invalid character (this range is reserved)
if ( replacement )
*output++ = replacement;
} else {
// Valid character directly convertible to a single UTF-16 character
*output++ = static_cast<Uint16>( input );
}
} else if ( input > 0x0010FFFF ) {
// Invalid character (greater than the maximum unicode value)
if ( replacement )
*output++ = replacement;
} else {
// The input character will be converted to two UTF-16 elements
input -= 0x0010000;
*output++ = static_cast<Uint16>( ( input >> 10 ) + 0xD800 );
*output++ = static_cast<Uint16>( ( input & 0x3FFUL ) + 0xDC00 );
}
return output;
}
template <typename In> In Utf<16>::Next( In begin, In end ) {
Uint32 codepoint;
return Decode( begin, end, codepoint );
}
template <typename In> std::size_t Utf<16>::Count( In begin, In end ) {
std::size_t length = 0;
while ( begin < end ) {
begin = Next( begin, end );
++length;
}
return length;
}
template <typename In, typename Out>
Out Utf<16>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) {
while ( begin < end ) {
Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale );
output = Encode( codepoint, output );
}
return output;
}
template <typename In, typename Out> Out Utf<16>::FromWide( In begin, In end, Out output ) {
while ( begin < end ) {
Uint32 codepoint = Utf<32>::DecodeWide( *begin++ );
output = Encode( codepoint, output );
}
return output;
}
template <typename In, typename Out> Out Utf<16>::FromLatin1( In begin, In end, Out output ) {
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while ( begin < end )
*output++ = *begin++;
return output;
}
template <typename In, typename Out>
Out Utf<16>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale );
}
return output;
}
#ifndef EFSW_NO_WIDECHAR
template <typename In, typename Out>
Out Utf<16>::ToWide( In begin, In end, Out output, wchar_t replacement ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
output = Utf<32>::EncodeWide( codepoint, output, replacement );
}
return output;
}
#endif
template <typename In, typename Out>
Out Utf<16>::ToLatin1( In begin, In end, Out output, char replacement ) {
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while ( begin < end ) {
*output++ = *begin < 256 ? static_cast<char>( *begin ) : replacement;
begin++;
}
return output;
}
template <typename In, typename Out> Out Utf<16>::toUtf8( In begin, In end, Out output ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
output = Utf<8>::Encode( codepoint, output );
}
return output;
}
template <typename In, typename Out> Out Utf<16>::ToUtf16( In begin, In end, Out output ) {
while ( begin < end )
*output++ = *begin++;
return output;
}
template <typename In, typename Out> Out Utf<16>::ToUtf32( In begin, In end, Out output ) {
while ( begin < end ) {
Uint32 codepoint;
begin = Decode( begin, end, codepoint );
*output++ = codepoint;
}
return output;
}
template <typename In> In Utf<32>::Decode( In begin, In end, Uint32& output, Uint32 ) {
output = *begin++;
return begin;
}
template <typename Out> Out Utf<32>::Encode( Uint32 input, Out output, Uint32 replacement ) {
*output++ = input;
return output;
}
template <typename In> In Utf<32>::Next( In begin, In end ) {
return ++begin;
}
template <typename In> std::size_t Utf<32>::Count( In begin, In end ) {
return begin - end;
}
template <typename In, typename Out>
Out Utf<32>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) {
while ( begin < end )
*output++ = DecodeAnsi( *begin++, locale );
return output;
}
template <typename In, typename Out> Out Utf<32>::FromWide( In begin, In end, Out output ) {
while ( begin < end )
*output++ = DecodeWide( *begin++ );
return output;
}
template <typename In, typename Out> Out Utf<32>::FromLatin1( In begin, In end, Out output ) {
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while ( begin < end )
*output++ = *begin++;
return output;
}
template <typename In, typename Out>
Out Utf<32>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) {
while ( begin < end )
output = EncodeAnsi( *begin++, output, replacement, locale );
return output;
}
#ifndef EFSW_NO_WIDECHAR
template <typename In, typename Out>
Out Utf<32>::ToWide( In begin, In end, Out output, wchar_t replacement ) {
while ( begin < end )
output = EncodeWide( *begin++, output, replacement );
return output;
}
#endif
template <typename In, typename Out>
Out Utf<32>::ToLatin1( In begin, In end, Out output, char replacement ) {
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while ( begin < end ) {
*output++ = *begin < 256 ? static_cast<char>( *begin ) : replacement;
begin++;
}
return output;
}
template <typename In, typename Out> Out Utf<32>::toUtf8( In begin, In end, Out output ) {
while ( begin < end )
output = Utf<8>::Encode( *begin++, output );
return output;
}
template <typename In, typename Out> Out Utf<32>::ToUtf16( In begin, In end, Out output ) {
while ( begin < end )
output = Utf<16>::Encode( *begin++, output );
return output;
}
template <typename In, typename Out> Out Utf<32>::ToUtf32( In begin, In end, Out output ) {
while ( begin < end )
*output++ = *begin++;
return output;
}
template <typename In> Uint32 Utf<32>::DecodeAnsi( In input, const std::locale& locale ) {
// On Windows, gcc's standard library (glibc++) has almost
// no support for Unicode stuff. As a consequence, in this
// context we can only use the default locale and ignore
// the one passed as parameter.
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \
( defined( __GLIBCPP__ ) || \
defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \
!( defined( __SGI_STL_PORT ) || \
defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */
wchar_t character = 0;
mbtowc( &character, &input, 1 );
return static_cast<Uint32>( character );
#else
// Get the facet of the locale which deals with character conversion
#ifndef EFSW_NO_WIDECHAR
const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>( locale );
#else
const std::ctype<char>& facet = std::use_facet<std::ctype<char>>( locale );
#endif
// Use the facet to convert each character of the input string
return static_cast<Uint32>( facet.widen( input ) );
#endif
}
template <typename In> Uint32 Utf<32>::DecodeWide( In input ) {
// The encoding of wide characters is not well defined and is left to the system;
// however we can safely assume that it is UCS-2 on Windows and
// UCS-4 on Unix systems.
// In both cases, a simple copy is enough (UCS-2 is a subset of UCS-4,
// and UCS-4 *is* UTF-32).
return input;
}
template <typename Out>
Out Utf<32>::EncodeAnsi( Uint32 codepoint, Out output, char replacement,
const std::locale& locale ) {
// On Windows, gcc's standard library (glibc++) has almost
// no support for Unicode stuff. As a consequence, in this
// context we can only use the default locale and ignore
// the one passed as parameter.
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \
( defined( __GLIBCPP__ ) || \
defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \
!( defined( __SGI_STL_PORT ) || \
defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */
char character = 0;
if ( wctomb( &character, static_cast<wchar_t>( codepoint ) ) >= 0 )
*output++ = character;
else if ( replacement )
*output++ = replacement;
return output;
#else
// Get the facet of the locale which deals with character conversion
#ifndef EFSW_NO_WIDECHAR
const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>( locale );
#else
const std::ctype<char>& facet = std::use_facet<std::ctype<char>>( locale );
#endif
// Use the facet to convert each character of the input string
*output++ = facet.narrow( static_cast<wchar_t>( codepoint ), replacement );
return output;
#endif
}
#ifndef EFSW_NO_WIDECHAR
template <typename Out>
Out Utf<32>::EncodeWide( Uint32 codepoint, Out output, wchar_t replacement ) {
// The encoding of wide characters is not well defined and is left to the system;
// however we can safely assume that it is UCS-2 on Windows and
// UCS-4 on Unix systems.
// For UCS-2 we need to check if the source characters fits in (UCS-2 is a subset of UCS-4).
// For UCS-4 we can do a direct copy (UCS-4 *is* UTF-32).
switch ( sizeof( wchar_t ) ) {
case 4: {
*output++ = static_cast<wchar_t>( codepoint );
break;
}
default: {
if ( ( codepoint <= 0xFFFF ) && ( ( codepoint < 0xD800 ) || ( codepoint > 0xDFFF ) ) ) {
*output++ = static_cast<wchar_t>( codepoint );
} else if ( replacement ) {
*output++ = replacement;
}
break;
}
}
return output;
}
#endif

10
src/efsw/Watcher.cpp Normal file
View File

@ -0,0 +1,10 @@
#include <efsw/Watcher.hpp>
namespace efsw {
Watcher::Watcher() : ID( 0 ), Directory( "" ), Listener( NULL ), Recursive( false ) {}
Watcher::Watcher( WatchID id, std::string directory, FileWatchListener* listener, bool recursive ) :
ID( id ), Directory( directory ), Listener( listener ), Recursive( recursive ) {}
} // namespace efsw

29
src/efsw/Watcher.hpp Normal file
View File

@ -0,0 +1,29 @@
#ifndef EFSW_WATCHERIMPL_HPP
#define EFSW_WATCHERIMPL_HPP
#include <efsw/base.hpp>
#include <efsw/efsw.hpp>
namespace efsw {
/** @brief Base Watcher class */
class Watcher {
public:
Watcher();
Watcher( WatchID id, std::string directory, FileWatchListener* listener, bool recursive );
virtual ~Watcher() {}
virtual void watch() {}
WatchID ID;
std::string Directory;
FileWatchListener* Listener;
bool Recursive;
std::string OldFileName;
};
} // namespace efsw
#endif

View File

@ -0,0 +1,223 @@
#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/FileWatcherFSEvents.hpp>
#include <efsw/WatcherFSEvents.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
namespace efsw {
WatcherFSEvents::WatcherFSEvents() :
Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ) {}
WatcherFSEvents::~WatcherFSEvents() {
if ( NULL != FSStream ) {
FSEventStreamStop( FSStream );
FSEventStreamInvalidate( FSStream );
FSEventStreamRelease( FSStream );
}
efSAFE_DELETE( WatcherGen );
}
void WatcherFSEvents::init() {
CFStringRef CFDirectory =
CFStringCreateWithCString( NULL, Directory.c_str(), kCFStringEncodingUTF8 );
CFArrayRef CFDirectoryArray = CFArrayCreate( NULL, (const void**)&CFDirectory, 1, NULL );
Uint32 streamFlags = kFSEventStreamCreateFlagNone;
if ( FileWatcherFSEvents::isGranular() ) {
streamFlags = efswFSEventStreamCreateFlagFileEvents | efswFSEventStreamCreateFlagNoDefer |
efswFSEventStreamCreateFlagUseExtendedData |
efswFSEventStreamCreateFlagUseCFTypes;
} else {
WatcherGen = new WatcherGeneric( ID, Directory, Listener, FWatcher.load(), Recursive );
}
FSEventStreamContext ctx;
/* Initialize context */
ctx.version = 0;
ctx.info = this;
ctx.retain = NULL;
ctx.release = NULL;
ctx.copyDescription = NULL;
dispatch_queue_t queue = dispatch_queue_create( NULL, NULL );
FSStream =
FSEventStreamCreate( kCFAllocatorDefault, &FileWatcherFSEvents::FSEventCallback, &ctx,
CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0., streamFlags );
FSEventStreamSetDispatchQueue( FSStream, queue );
FSEventStreamStart( FSStream );
CFRelease( CFDirectoryArray );
CFRelease( CFDirectory );
}
void WatcherFSEvents::sendFileAction( WatchID watchid, const std::string& dir,
const std::string& filename, Action action,
std::string oldFilename ) {
Listener->handleFileAction( watchid, FileSystem::precomposeFileName( dir ),
FileSystem::precomposeFileName( filename ), action,
FileSystem::precomposeFileName( oldFilename ) );
}
void WatcherFSEvents::sendMissedFileActions( WatchID watchid,
const std::string& dir) {
Listener->handleMissedFileActions( watchid,
FileSystem::precomposeFileName( dir ) );
}
void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path,
std::string& dirPath, std::string& filePath, Uint64 inode ) {
if ( ( flags & efswFSEventStreamEventFlagItemCreated ) && FileInfo::exists( path ) &&
( !SanitizeEvents || FilesAdded.find( inode ) != FilesAdded.end() ) ) {
sendFileAction( ID, dirPath, filePath, Actions::Add );
if ( SanitizeEvents )
FilesAdded.insert( inode );
}
if ( flags & ModifiedFlags ) {
sendFileAction( ID, dirPath, filePath, Actions::Modified );
}
if ( ( flags & efswFSEventStreamEventFlagItemRemoved ) && !FileInfo::exists( path ) ) {
// Since i don't know the order, at least i try to keep the data consistent with the real
// state
sendFileAction( ID, dirPath, filePath, Actions::Delete );
if ( SanitizeEvents )
FilesAdded.erase( inode );
}
}
void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) {
size_t esize = events.size();
for ( size_t i = 0; i < esize; i++ ) {
FSEvent& event = events[i];
if ( event.Flags &
( kFSEventStreamEventFlagUserDropped | kFSEventStreamEventFlagKernelDropped |
kFSEventStreamEventFlagMustScanSubDirs) ) {
efDEBUG( "Rescan/Drop event for watch: %s - flags: 0x%x\n", Directory.c_str(), event.Flags );
std::string dirPath = Directory;
FileSystem::dirRemoveSlashAtEnd( dirPath );
sendMissedFileActions(ID, dirPath );
continue;
}
if ( event.Flags &
( kFSEventStreamEventFlagEventIdsWrapped | kFSEventStreamEventFlagHistoryDone |
kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount |
kFSEventStreamEventFlagRootChanged ) ) {
continue;
}
if ( !Recursive ) {
/** In case that is not recursive the watcher, ignore the events from subfolders */
if ( event.Path.find_last_of( FileSystem::getOSSlash() ) != Directory.size() - 1 ) {
continue;
}
}
if ( FileWatcherFSEvents::isGranular() ) {
std::string dirPath( FileSystem::pathRemoveFileName( event.Path ) );
std::string filePath( FileSystem::fileNameFromPath( event.Path ) );
if ( event.Flags &
( efswFSEventStreamEventFlagItemCreated | efswFSEventStreamEventFlagItemRemoved |
efswFSEventStreamEventFlagItemRenamed ) ) {
if ( dirPath != Directory ) {
DirsChanged.insert( dirPath );
}
}
// This is a mess. But it's FSEvents faults, because shrinks events from the same file
// in one single event ( so there's no order for them ) For example a file could have
// been added modified and erased, but i can't know if first was erased and then added
// and modified, or added, then modified and then erased. I don't know what they were
// thinking by doing this...
efDEBUG( "Event in: %s - flags: 0x%x\n", event.Path.c_str(), event.Flags );
if ( event.Flags & efswFSEventStreamEventFlagItemRenamed ) {
if ( ( i + 1 < esize ) &&
( events[i + 1].Flags & efswFSEventStreamEventFlagItemRenamed ) &&
( events[i + 1].inode == event.inode ) ) {
FSEvent& nEvent = events[i + 1];
std::string newDir( FileSystem::pathRemoveFileName( nEvent.Path ) );
std::string newFilepath( FileSystem::fileNameFromPath( nEvent.Path ) );
if ( event.Path != nEvent.Path ) {
if ( dirPath == newDir ) {
if ( !FileInfo::exists( event.Path ) ||
0 == strcasecmp( event.Path.c_str(), nEvent.Path.c_str() ) ) {
sendFileAction( ID, dirPath, newFilepath, Actions::Moved,
filePath );
} else {
sendFileAction( ID, dirPath, filePath, Actions::Moved,
newFilepath );
}
} else {
sendFileAction( ID, dirPath, filePath, Actions::Delete );
sendFileAction( ID, newDir, newFilepath, Actions::Add );
if ( nEvent.Flags & ModifiedFlags ) {
sendFileAction( ID, newDir, newFilepath, Actions::Modified );
}
}
} else {
handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath, event.inode );
}
if ( nEvent.Flags & ( efswFSEventStreamEventFlagItemCreated |
efswFSEventStreamEventFlagItemRemoved |
efswFSEventStreamEventFlagItemRenamed ) ) {
if ( newDir != Directory ) {
DirsChanged.insert( newDir );
}
}
// Skip the renamed file
i++;
} else if ( FileInfo::exists( event.Path ) ) {
sendFileAction( ID, dirPath, filePath, Actions::Add );
if ( event.Flags & ModifiedFlags ) {
sendFileAction( ID, dirPath, filePath, Actions::Modified );
}
} else {
sendFileAction( ID, dirPath, filePath, Actions::Delete );
}
} else {
handleAddModDel( event.Flags, event.Path, dirPath, filePath, event.inode );
}
} else {
efDEBUG( "Directory: %s changed\n", event.Path.c_str() );
DirsChanged.insert( event.Path );
}
}
}
void WatcherFSEvents::process() {
std::unordered_set<std::string>::iterator it = DirsChanged.begin();
for ( ; it != DirsChanged.end(); it++ ) {
if ( !FileWatcherFSEvents::isGranular() ) {
WatcherGen->watchDir( ( *it ) );
} else {
sendFileAction( ID, FileSystem::pathRemoveFileName( ( *it ) ),
FileSystem::fileNameFromPath( ( *it ) ), Actions::Modified );
}
}
DirsChanged.clear();
}
} // namespace efsw
#endif

View File

@ -0,0 +1,89 @@
#ifndef EFSW_WATCHERINOTIFY_HPP
#define EFSW_WATCHERINOTIFY_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <efsw/FileInfo.hpp>
#include <efsw/WatcherGeneric.hpp>
#include <unordered_set>
#include <vector>
namespace efsw {
/* OSX < 10.7 has no file events */
/* So i declare the events constants */
enum FSEventEvents {
efswFSEventStreamCreateFlagUseCFTypes = 0x00000001,
efswFSEventStreamCreateFlagNoDefer = 0x00000002,
efswFSEventStreamCreateFlagFileEvents = 0x00000010,
efswFSEventStreamCreateFlagUseExtendedData = 0x00000040,
efswFSEventStreamEventFlagItemCreated = 0x00000100,
efswFSEventStreamEventFlagItemRemoved = 0x00000200,
efswFSEventStreamEventFlagItemInodeMetaMod = 0x00000400,
efswFSEventStreamEventFlagItemRenamed = 0x00000800,
efswFSEventStreamEventFlagItemModified = 0x00001000,
efswFSEventStreamEventFlagItemFinderInfoMod = 0x00002000,
efswFSEventStreamEventFlagItemChangeOwner = 0x00004000,
efswFSEventStreamEventFlagItemXattrMod = 0x00008000,
efswFSEventStreamEventFlagItemIsFile = 0x00010000,
efswFSEventStreamEventFlagItemIsDir = 0x00020000,
efswFSEventStreamEventFlagItemIsSymlink = 0x00040000,
efswFSEventsModified = efswFSEventStreamEventFlagItemFinderInfoMod |
efswFSEventStreamEventFlagItemModified |
efswFSEventStreamEventFlagItemInodeMetaMod
};
class FileWatcherFSEvents;
class FSEvent {
public:
FSEvent( std::string path, long flags, Uint64 id, Uint64 inode = 0 ) :
Path( path ), Flags( flags ), Id( id ), inode( inode ) {}
std::string Path;
long Flags{ 0 };
Uint64 Id{ 0 };
Uint64 inode{ 0 };
};
class WatcherFSEvents : public Watcher {
public:
WatcherFSEvents();
~WatcherFSEvents();
void init();
void handleActions( std::vector<FSEvent>& events );
void process();
Atomic<FileWatcherFSEvents*> FWatcher;
FSEventStreamRef FSStream;
Uint64 ModifiedFlags{ efswFSEventsModified };
bool SanitizeEvents{ false };
protected:
void handleAddModDel( const Uint32& flags, const std::string& path, std::string& dirPath,
std::string& filePath, Uint64 inode );
WatcherGeneric* WatcherGen;
std::unordered_set<std::string> DirsChanged;
std::unordered_set<Uint64> FilesAdded;
void sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename,
Action action, std::string oldFilename = "" );
void sendMissedFileActions( WatchID watchid, const std::string& dir);
};
} // namespace efsw
#endif
#endif

View File

@ -0,0 +1,33 @@
#include <efsw/DirWatcherGeneric.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/WatcherGeneric.hpp>
namespace efsw {
WatcherGeneric::WatcherGeneric( WatchID id, const std::string& directory, FileWatchListener* fwl,
FileWatcherImpl* fw, bool recursive ) :
Watcher( id, directory, fwl, recursive ), WatcherImpl( fw ), DirWatch( NULL ) {
FileSystem::dirAddSlashAtEnd( Directory );
DirWatch = new DirWatcherGeneric( NULL, this, directory, recursive, false );
DirWatch->addChilds( false );
}
WatcherGeneric::~WatcherGeneric() {
efSAFE_DELETE( DirWatch );
}
void WatcherGeneric::watch() {
DirWatch->watch();
}
void WatcherGeneric::watchDir( std::string dir ) {
DirWatch->watchDir( dir );
}
bool WatcherGeneric::pathInWatches( std::string path ) {
return DirWatch->pathInWatches( path );
}
} // namespace efsw

View File

@ -0,0 +1,29 @@
#ifndef EFSW_WATCHERGENERIC_HPP
#define EFSW_WATCHERGENERIC_HPP
#include <efsw/FileWatcherImpl.hpp>
namespace efsw {
class DirWatcherGeneric;
class WatcherGeneric : public Watcher {
public:
FileWatcherImpl* WatcherImpl;
DirWatcherGeneric* DirWatch;
WatcherGeneric( WatchID id, const std::string& directory, FileWatchListener* fwl,
FileWatcherImpl* fw, bool recursive );
~WatcherGeneric();
void watch() override;
void watchDir( std::string dir );
bool pathInWatches( std::string path );
};
} // namespace efsw
#endif

View File

@ -0,0 +1,21 @@
#include <efsw/WatcherInotify.hpp>
namespace efsw {
WatcherInotify::WatcherInotify() : Watcher(), Parent( NULL ) {}
bool WatcherInotify::inParentTree( WatcherInotify* parent ) {
WatcherInotify* tNext = Parent;
while ( NULL != tNext ) {
if ( tNext == parent ) {
return true;
}
tNext = tNext->Parent;
}
return false;
}
} // namespace efsw

View File

@ -0,0 +1,24 @@
#ifndef EFSW_WATCHERINOTIFY_HPP
#define EFSW_WATCHERINOTIFY_HPP
#include <efsw/FileInfo.hpp>
#include <efsw/FileWatcherImpl.hpp>
namespace efsw {
class WatcherInotify : public Watcher {
public:
WatcherInotify();
bool inParentTree( WatcherInotify* parent );
WatcherInotify* Parent;
WatchID InotifyID;
FileInfo DirInfo;
bool syntheticEvents{ false };
};
} // namespace efsw
#endif

566
src/efsw/WatcherKqueue.cpp Normal file
View File

@ -0,0 +1,566 @@
#include <efsw/WatcherKqueue.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <dirent.h>
#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/FileWatcherKqueue.hpp>
#include <efsw/String.hpp>
#include <efsw/System.hpp>
#include <efsw/WatcherGeneric.hpp>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#define KEVENT_RESERVE_VALUE ( 10 )
#ifndef O_EVTONLY
#define O_EVTONLY ( O_RDONLY | O_NONBLOCK )
#endif
namespace efsw {
int comparator( const void* ke1, const void* ke2 ) {
const KEvent* kev1 = reinterpret_cast<const KEvent*>( ke1 );
const KEvent* kev2 = reinterpret_cast<const KEvent*>( ke2 );
if ( NULL != kev2->udata ) {
FileInfo* fi1 = reinterpret_cast<FileInfo*>( kev1->udata );
FileInfo* fi2 = reinterpret_cast<FileInfo*>( kev2->udata );
return strcmp( fi1->Filepath.c_str(), fi2->Filepath.c_str() );
}
return 1;
}
WatcherKqueue::WatcherKqueue( WatchID watchid, const std::string& dirname,
FileWatchListener* listener, bool recursive,
FileWatcherKqueue* watcher, WatcherKqueue* parent ) :
Watcher( watchid, dirname, listener, recursive ),
mLastWatchID( 0 ),
mChangeListCount( 0 ),
mKqueue( kqueue() ),
mWatcher( watcher ),
mParent( parent ),
mInitOK( true ),
mErrno( 0 ) {
if ( -1 == mKqueue ) {
efDEBUG(
"kqueue() returned invalid descriptor for directory %s. File descriptors count: %ld\n",
Directory.c_str(), mWatcher->mFileDescriptorCount );
mInitOK = false;
mErrno = errno;
} else {
mWatcher->addFD();
}
}
WatcherKqueue::~WatcherKqueue() {
// Remove the childs watchers ( sub-folders watches )
removeAll();
for ( size_t i = 0; i < mChangeListCount; i++ ) {
if ( NULL != mChangeList[i].udata ) {
FileInfo* fi = reinterpret_cast<FileInfo*>( mChangeList[i].udata );
efSAFE_DELETE( fi );
}
}
close( mKqueue );
mWatcher->removeFD();
}
void WatcherKqueue::addAll() {
if ( -1 == mKqueue ) {
return;
}
// scan directory and call addFile(name, false) on each file
FileSystem::dirAddSlashAtEnd( Directory );
efDEBUG( "addAll(): Added folder: %s\n", Directory.c_str() );
// add base dir
int fd = open( Directory.c_str(), O_EVTONLY );
if ( -1 == fd ) {
efDEBUG( "addAll(): Couldn't open folder: %s\n", Directory.c_str() );
if ( EACCES != errno ) {
mInitOK = false;
}
mErrno = errno;
return;
}
mDirSnap.setDirectoryInfo( Directory );
mDirSnap.scan();
mChangeList.resize( KEVENT_RESERVE_VALUE );
// Creates the kevent for the folder
EV_SET( &mChangeList[0], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME, 0, 0 );
mWatcher->addFD();
// Get the files and directories from the directory
FileInfoMap files = FileSystem::filesInfoFromPath( Directory );
for ( FileInfoMap::iterator it = files.begin(); it != files.end(); it++ ) {
FileInfo& fi = it->second;
if ( fi.isRegularFile() ) {
// Add the regular files kevent
addFile( fi.Filepath, false );
} else if ( Recursive && fi.isDirectory() && fi.isReadable() ) {
// Create another watcher for the subfolders ( if recursive )
WatchID id = addWatch( fi.Filepath, Listener, Recursive, this );
// If the watcher is not adding the watcher means that the directory was created
if ( id > 0 && !mWatcher->isAddingWatcher() ) {
handleFolderAction( fi.Filepath, Actions::Add );
}
}
}
}
void WatcherKqueue::removeAll() {
efDEBUG( "removeAll(): Removing all child watchers\n" );
std::vector<WatchID> erase;
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ ) {
efDEBUG( "removeAll(): Removed child watcher %s\n", it->second->Directory.c_str() );
erase.push_back( it->second->ID );
}
for ( std::vector<WatchID>::iterator eit = erase.begin(); eit != erase.end(); eit++ ) {
removeWatch( *eit );
}
}
void WatcherKqueue::addFile( const std::string& name, bool emitEvents ) {
efDEBUG( "addFile(): Added: %s\n", name.c_str() );
// Open the file to get the file descriptor
int fd = open( name.c_str(), O_EVTONLY );
if ( fd == -1 ) {
efDEBUG( "addFile(): Could open file descriptor for %s. File descriptor count: %ld\n",
name.c_str(), mWatcher->mFileDescriptorCount );
Errors::Log::createLastError( Errors::FileNotReadable, name );
if ( EACCES != errno ) {
mInitOK = false;
}
mErrno = errno;
return;
}
mWatcher->addFD();
// increase the file kevent file count
mChangeListCount++;
if ( mChangeListCount + KEVENT_RESERVE_VALUE > mChangeList.size() &&
mChangeListCount % KEVENT_RESERVE_VALUE == 0 ) {
size_t reserve_size = mChangeList.size() + KEVENT_RESERVE_VALUE;
mChangeList.resize( reserve_size );
efDEBUG( "addFile(): Reserverd more KEvents space for %s, space reserved %ld, list actual "
"size %ld.\n",
Directory.c_str(), reserve_size, mChangeListCount );
}
// create entry
FileInfo* entry = new FileInfo( name );
// set the event data at the end of the list
EV_SET( &mChangeList[mChangeListCount], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME, 0, (void*)entry );
// qsort sort the list by name
qsort( &mChangeList[1], mChangeListCount, sizeof( KEvent ), comparator );
// handle action
if ( emitEvents ) {
handleAction( name, Actions::Add );
}
}
void WatcherKqueue::removeFile( const std::string& name, bool emitEvents ) {
efDEBUG( "removeFile(): Trying to remove file: %s\n", name.c_str() );
// bsearch
KEvent target;
// Create a temporary file info to search the kevent ( searching the directory )
FileInfo tempEntry( name );
target.udata = &tempEntry;
// Search the kevent
KEvent* ke = (KEvent*)bsearch( &target, &mChangeList[0], mChangeListCount + 1, sizeof( KEvent ),
comparator );
// Trying to remove a non-existing file?
if ( !ke ) {
Errors::Log::createLastError( Errors::FileNotFound, name );
efDEBUG( "File not removed\n" );
return;
}
efDEBUG( "File removed\n" );
// handle action
if ( emitEvents ) {
handleAction( name, Actions::Delete );
}
// Delete the user data ( FileInfo ) from the kevent closed
FileInfo* del = reinterpret_cast<FileInfo*>( ke->udata );
efSAFE_DELETE( del );
// close the file descriptor from the kevent
close( ke->ident );
mWatcher->removeFD();
memset( ke, 0, sizeof( KEvent ) );
// move end to current
memcpy( ke, &mChangeList[mChangeListCount], sizeof( KEvent ) );
memset( &mChangeList[mChangeListCount], 0, sizeof( KEvent ) );
--mChangeListCount;
}
void WatcherKqueue::rescan() {
efDEBUG( "rescan(): Rescanning: %s\n", Directory.c_str() );
DirectorySnapshotDiff Diff = mDirSnap.scan();
if ( Diff.DirChanged ) {
sendDirChanged();
}
if ( Diff.changed() ) {
FileInfoList::iterator it;
MovedList::iterator mit;
/// Files
DiffIterator( FilesCreated ) {
addFile( ( *it ).Filepath );
}
DiffIterator( FilesModified ) {
handleAction( ( *it ).Filepath, Actions::Modified );
}
DiffIterator( FilesDeleted ) {
removeFile( ( *it ).Filepath );
}
DiffMovedIterator( FilesMoved ) {
handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first );
removeFile( Directory + ( *mit ).first, false );
addFile( ( *mit ).second.Filepath, false );
}
/// Directories
DiffIterator( DirsCreated ) {
handleFolderAction( ( *it ).Filepath, Actions::Add );
addWatch( ( *it ).Filepath, Listener, Recursive, this );
}
DiffIterator( DirsModified ) {
handleFolderAction( ( *it ).Filepath, Actions::Modified );
}
DiffIterator( DirsDeleted ) {
handleFolderAction( ( *it ).Filepath, Actions::Delete );
Watcher* watch = findWatcher( ( *it ).Filepath );
if ( NULL != watch ) {
removeWatch( watch->ID );
}
}
DiffMovedIterator( DirsMoved ) {
moveDirectory( Directory + ( *mit ).first, ( *mit ).second.Filepath );
}
}
}
WatchID WatcherKqueue::watchingDirectory( std::string dir ) {
Watcher* watch = findWatcher( dir );
if ( NULL != watch ) {
return watch->ID;
}
return Errors::FileNotFound;
}
void WatcherKqueue::handleAction( const std::string& filename, efsw::Action action,
const std::string& oldFilename ) {
Listener->handleFileAction( ID, Directory, FileSystem::fileNameFromPath( filename ), action,
FileSystem::fileNameFromPath( oldFilename ) );
}
void WatcherKqueue::handleFolderAction( std::string filename, efsw::Action action,
const std::string& oldFilename ) {
FileSystem::dirRemoveSlashAtEnd( filename );
handleAction( filename, action, oldFilename );
}
void WatcherKqueue::sendDirChanged() {
if ( NULL != mParent ) {
Listener->handleFileAction( mParent->ID, mParent->Directory,
FileSystem::fileNameFromPath( Directory ), Actions::Modified );
}
}
void WatcherKqueue::watch() {
if ( -1 == mKqueue ) {
return;
}
int nev = 0;
KEvent event;
// First iterate the childs, to get the events from the deepest folder, to the watcher childs
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
it->second->watch();
}
bool needScan = false;
// Then we get the the events of the current folder
while ( !mChangeList.empty() &&
( nev = kevent( mKqueue, mChangeList.data(), mChangeListCount + 1, &event, 1,
&mWatcher->mTimeOut ) ) != 0 ) {
// An error ocurred?
if ( nev == -1 ) {
efDEBUG( "watch(): Error on directory %s\n", Directory.c_str() );
perror( "kevent" );
break;
} else {
FileInfo* entry = NULL;
// If udate == NULL means that it is the fisrt element of the change list, the folder.
// otherwise it is an event of some file inside the folder
if ( ( entry = reinterpret_cast<FileInfo*>( event.udata ) ) != NULL ) {
efDEBUG( "watch(): File: %s ", entry->Filepath.c_str() );
// If the event flag is delete... the file was deleted
if ( event.fflags & NOTE_DELETE ) {
efDEBUG( "deleted\n" );
mDirSnap.removeFile( entry->Filepath );
removeFile( entry->Filepath );
} else if ( event.fflags & NOTE_EXTEND || event.fflags & NOTE_WRITE ||
event.fflags & NOTE_ATTRIB ) {
// The file was modified
efDEBUG( "modified\n" );
FileInfo fi( entry->Filepath );
if ( fi != *entry ) {
*entry = fi;
mDirSnap.updateFile( entry->Filepath );
handleAction( entry->Filepath, efsw::Actions::Modified );
}
} else if ( event.fflags & NOTE_RENAME ) {
efDEBUG( "moved\n" );
needScan = true;
}
} else {
needScan = true;
}
}
}
if ( needScan ) {
rescan();
}
}
Watcher* WatcherKqueue::findWatcher( const std::string path ) {
WatchMap::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); it++ ) {
if ( it->second->Directory == path ) {
return it->second;
}
}
return NULL;
}
void WatcherKqueue::moveDirectory( std::string oldPath, std::string newPath, bool emitEvents ) {
// Update the directory path if it's a watcher
std::string opath2( oldPath );
FileSystem::dirAddSlashAtEnd( opath2 );
Watcher* watch = findWatcher( opath2 );
if ( NULL != watch ) {
watch->Directory = opath2;
}
if ( emitEvents ) {
handleFolderAction( newPath, efsw::Actions::Moved, oldPath );
}
}
WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher,
bool recursive, WatcherKqueue* parent ) {
static bool s_ug = false;
std::string dir( directory );
FileSystem::dirAddSlashAtEnd( dir );
// This should never happen here
if ( !FileSystem::isDirectory( dir ) ) {
return Errors::Log::createLastError( Errors::FileNotFound, dir );
} else if ( pathInWatches( dir ) || pathInParent( dir ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, directory );
} else if ( NULL != parent && FileSystem::isRemoteFS( dir ) ) {
return Errors::Log::createLastError( Errors::FileRemote, dir );
}
std::string curPath;
std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
if ( "" != link ) {
/// Avoid adding symlinks directories if it's now enabled
if ( NULL != parent && !mWatcher->mFileWatcher->followSymlinks() ) {
return Errors::Log::createLastError( Errors::FileOutOfScope, dir );
}
if ( pathInWatches( link ) || pathInParent( link ) ) {
return Errors::Log::createLastError( Errors::FileRepeated, link );
} else if ( !mWatcher->linkAllowed( curPath, link ) ) {
return Errors::Log::createLastError( Errors::FileOutOfScope, link );
} else {
dir = link;
}
}
if ( mWatcher->availablesFD() ) {
WatcherKqueue* watch =
new WatcherKqueue( ++mLastWatchID, dir, watcher, recursive, mWatcher, parent );
mWatches.insert( std::make_pair( mLastWatchID, watch ) );
watch->addAll();
// if failed to open the directory... erase the watcher
if ( !watch->initOK() ) {
int le = watch->lastErrno();
mWatches.erase( watch->ID );
efSAFE_DELETE( watch );
mLastWatchID--;
// Probably the folder has too many files, create a generic watcher
if ( EACCES != le ) {
WatcherGeneric* watch =
new WatcherGeneric( ++mLastWatchID, dir, watcher, mWatcher, recursive );
mWatches.insert( std::make_pair( mLastWatchID, watch ) );
} else {
return Errors::Log::createLastError( Errors::Unspecified, link );
}
}
} else {
if ( !s_ug ) {
efDEBUG( "Started using WatcherGeneric, reached file descriptors limit: %ld.\n",
mWatcher->mFileDescriptorCount );
s_ug = true;
}
WatcherGeneric* watch =
new WatcherGeneric( ++mLastWatchID, dir, watcher, mWatcher, recursive );
mWatches.insert( std::make_pair( mLastWatchID, watch ) );
}
return mLastWatchID;
}
bool WatcherKqueue::initOK() {
return mInitOK;
}
void WatcherKqueue::removeWatch( WatchID watchid ) {
WatchMap::iterator iter = mWatches.find( watchid );
if ( iter == mWatches.end() )
return;
Watcher* watch = iter->second;
mWatches.erase( iter );
efSAFE_DELETE( watch );
}
bool WatcherKqueue::pathInWatches( const std::string& path ) {
return NULL != findWatcher( path );
}
bool WatcherKqueue::pathInParent( const std::string& path ) {
WatcherKqueue* pNext = mParent;
while ( NULL != pNext ) {
if ( pNext->pathInWatches( path ) ) {
return true;
}
pNext = pNext->mParent;
}
if ( mWatcher->pathInWatches( path ) ) {
return true;
}
if ( path == Directory ) {
return true;
}
return false;
}
int WatcherKqueue::lastErrno() {
return mErrno;
}
} // namespace efsw
#endif

View File

@ -0,0 +1,97 @@
#ifndef EFSW_WATCHEROSX_HPP
#define EFSW_WATCHEROSX_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <efsw/DirectorySnapshot.hpp>
#include <map>
#include <sys/event.h>
#include <sys/types.h>
#include <vector>
namespace efsw {
class FileWatcherKqueue;
class WatcherKqueue;
typedef struct kevent KEvent;
/// type for a map from WatchID to WatcherKqueue pointer
typedef std::map<WatchID, Watcher*> WatchMap;
class WatcherKqueue : public Watcher {
public:
WatcherKqueue( WatchID watchid, const std::string& dirname, FileWatchListener* listener,
bool recursive, FileWatcherKqueue* watcher, WatcherKqueue* parent = NULL );
virtual ~WatcherKqueue();
void addFile( const std::string& name, bool emitEvents = true );
void removeFile( const std::string& name, bool emitEvents = true );
// called when the directory is actually changed
// means a file has been added or removed
// rescans the watched directory adding/removing files and sending notices
void rescan();
void handleAction( const std::string& filename, efsw::Action action,
const std::string& oldFilename = "" );
void handleFolderAction( std::string filename, efsw::Action action,
const std::string& oldFilename = "" );
void addAll();
void removeAll();
WatchID watchingDirectory( std::string dir );
void watch() override;
WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
WatcherKqueue* parent );
void removeWatch( WatchID watchid );
bool initOK();
int lastErrno();
protected:
WatchMap mWatches;
int mLastWatchID;
// index 0 is always the directory
std::vector<KEvent> mChangeList;
size_t mChangeListCount;
DirectorySnapshot mDirSnap;
/// The descriptor for the kqueue
int mKqueue;
FileWatcherKqueue* mWatcher;
WatcherKqueue* mParent;
bool mInitOK;
int mErrno;
bool pathInWatches( const std::string& path );
bool pathInParent( const std::string& path );
Watcher* findWatcher( const std::string path );
void moveDirectory( std::string oldPath, std::string newPath, bool emitEvents = true );
void sendDirChanged();
};
} // namespace efsw
#endif
#endif

268
src/efsw/WatcherWin32.cpp Normal file
View File

@ -0,0 +1,268 @@
#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/String.hpp>
#include <efsw/WatcherWin32.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <algorithm>
namespace efsw {
struct EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX {
DWORD NextEntryOffset;
DWORD Action;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastModificationTime;
LARGE_INTEGER LastChangeTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER AllocatedLength;
LARGE_INTEGER FileSize;
DWORD FileAttributes;
DWORD ReparsePointTag;
LARGE_INTEGER FileId;
LARGE_INTEGER ParentFileId;
DWORD FileNameLength;
WCHAR FileName[1];
};
typedef EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX* EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX;
typedef BOOL( WINAPI* EFSW_LPREADDIRECTORYCHANGESEXW )( HANDLE hDirectory, LPVOID lpBuffer,
DWORD nBufferLength, BOOL bWatchSubtree,
DWORD dwNotifyFilter, LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
DWORD ReadDirectoryNotifyInformationClass );
static EFSW_LPREADDIRECTORYCHANGESEXW pReadDirectoryChangesExW = NULL;
#define EFSW_ReadDirectoryNotifyExtendedInformation 2
static void initReadDirectoryChangesEx() {
static bool hasInit = false;
if ( !hasInit ) {
hasInit = true;
HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" );
if ( !hModule )
return;
pReadDirectoryChangesExW =
(EFSW_LPREADDIRECTORYCHANGESEXW)GetProcAddress( hModule, "ReadDirectoryChangesExW" );
}
}
void WatchCallbackOld( WatcherWin32* pWatch ) {
PFILE_NOTIFY_INFORMATION pNotify;
size_t offset = 0;
do {
bool skip = false;
pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset];
offset += pNotify->NextEntryOffset;
int count =
WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL );
if ( count == 0 )
continue;
std::string nfile( count, '\0' );
count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count,
NULL, NULL );
if ( FILE_ACTION_MODIFIED == pNotify->Action ) {
FileInfo fifile( std::string( pWatch->DirName ) + nfile );
if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime &&
pWatch->LastModifiedEvent.file.Size == fifile.Size &&
pWatch->LastModifiedEvent.fileName == nfile ) {
skip = true;
}
pWatch->LastModifiedEvent.fileName = nfile;
pWatch->LastModifiedEvent.file = fifile;
}
if ( !skip ) {
pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action );
}
} while ( pNotify->NextEntryOffset != 0 );
}
void WatchCallbackEx( WatcherWin32* pWatch ) {
EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX pNotify;
size_t offset = 0;
do {
bool skip = false;
pNotify = (EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX)&pWatch->Buffer[offset];
offset += pNotify->NextEntryOffset;
int count =
WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL );
if ( count == 0 )
continue;
std::string nfile( count, '\0' );
count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count,
NULL, NULL );
if ( FILE_ACTION_MODIFIED == pNotify->Action ) {
FileInfo fifile( std::string( pWatch->DirName ) + nfile );
if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime &&
pWatch->LastModifiedEvent.file.Size == fifile.Size &&
pWatch->LastModifiedEvent.fileName == nfile ) {
skip = true;
}
pWatch->LastModifiedEvent.fileName = nfile;
pWatch->LastModifiedEvent.file = fifile;
} else if ( FILE_ACTION_RENAMED_OLD_NAME == pNotify->Action ) {
pWatch->OldFiles.emplace_back( nfile, pNotify->FileId );
skip = true;
} else if ( FILE_ACTION_RENAMED_NEW_NAME == pNotify->Action ) {
std::string oldFile;
LARGE_INTEGER oldFileId{};
for ( auto it = pWatch->OldFiles.begin(); it != pWatch->OldFiles.end(); ++it ) {
if ( it->second.QuadPart == pNotify->FileId.QuadPart ) {
oldFile = it->first;
oldFileId = it->second;
it = pWatch->OldFiles.erase( it );
break;
}
}
if ( oldFile.empty() ) {
pWatch->Watch->handleAction( pWatch, nfile, FILE_ACTION_ADDED );
skip = true;
} else {
pWatch->Watch->handleAction( pWatch, oldFile, FILE_ACTION_RENAMED_OLD_NAME );
}
}
if ( !skip ) {
pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action );
}
} while ( pNotify->NextEntryOffset != 0 );
}
/// Unpacks events and passes them to a user defined callback.
void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) {
if ( NULL == lpOverlapped ) {
return;
}
WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped;
WatcherWin32* pWatch = tWatch->Watch;
if ( dwNumberOfBytesTransfered == 0 ) {
if ( nullptr != pWatch && !pWatch->StopNow ) {
/// Missed file actions due to buffer overflowed
std::string dir = pWatch->DirName;
FileSystem::dirRemoveSlashAtEnd( dir );
pWatch->Listener->handleMissedFileActions( pWatch->ID, dir );
RefreshWatch( tWatch );
} else {
return;
}
}
// Fork watch depending on the Windows API supported
if ( pWatch->Extended ) {
WatchCallbackEx( pWatch );
} else {
WatchCallbackOld( pWatch );
}
if ( !pWatch->StopNow ) {
RefreshWatch( tWatch );
}
}
/// Refreshes the directory monitoring.
RefreshResult RefreshWatch( WatcherStructWin32* pWatch ) {
initReadDirectoryChangesEx();
bool bRet = false;
RefreshResult ret = RefreshResult::Failed;
pWatch->Watch->Extended = false;
if ( pReadDirectoryChangesExW ) {
bRet = pReadDirectoryChangesExW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(),
(DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive,
pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped,
NULL, EFSW_ReadDirectoryNotifyExtendedInformation ) != 0;
if ( bRet ) {
ret = RefreshResult::SucessEx;
pWatch->Watch->Extended = true;
}
}
if ( !bRet ) {
bRet = ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(),
(DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive,
pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped,
NULL ) != 0;
if ( bRet )
ret = RefreshResult::Success;
}
if ( !bRet ) {
std::string error = std::to_string( GetLastError() );
Errors::Log::createLastError( Errors::WatcherFailed, error );
}
return ret;
}
/// Stops monitoring a directory.
void DestroyWatch( WatcherStructWin32* pWatch ) {
if ( pWatch ) {
WatcherWin32* tWatch = pWatch->Watch;
tWatch->StopNow = true;
CancelIoEx( pWatch->Watch->DirHandle, &pWatch->Overlapped );
CloseHandle( pWatch->Watch->DirHandle );
efSAFE_DELETE_ARRAY( pWatch->Watch->DirName );
efSAFE_DELETE( pWatch->Watch );
efSAFE_DELETE( pWatch );
}
}
/// Starts monitoring a directory.
WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive,
DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ) {
WatcherStructWin32* tWatch = new WatcherStructWin32();
WatcherWin32* pWatch = new WatcherWin32(bufferSize);
if (tWatch)
tWatch->Watch = pWatch;
pWatch->DirHandle = CreateFileW(
szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL );
if ( pWatch->DirHandle != INVALID_HANDLE_VALUE &&
CreateIoCompletionPort( pWatch->DirHandle, iocp, 0, 1 ) ) {
pWatch->NotifyFilter = notifyFilter;
pWatch->Recursive = recursive;
if ( RefreshResult::Failed != RefreshWatch( tWatch ) ) {
return tWatch;
}
}
CloseHandle( pWatch->DirHandle );
efSAFE_DELETE( pWatch->Watch );
efSAFE_DELETE( tWatch );
return NULL;
}
} // namespace efsw
#endif

79
src/efsw/WatcherWin32.hpp Normal file
View File

@ -0,0 +1,79 @@
#ifndef EFSW_WATCHERWIN32_HPP
#define EFSW_WATCHERWIN32_HPP
#include <efsw/FileInfo.hpp>
#include <efsw/FileWatcherImpl.hpp>
#include <vector>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <windows.h>
#ifdef EFSW_COMPILER_MSVC
#pragma comment( lib, "comctl32.lib" )
#pragma comment( lib, "user32.lib" )
#pragma comment( lib, "ole32.lib" )
// disable secure warnings
#pragma warning( disable : 4996 )
#endif
namespace efsw {
class WatcherWin32;
enum RefreshResult { Failed, Success, SucessEx };
/// Internal watch data
struct WatcherStructWin32 {
OVERLAPPED Overlapped;
WatcherWin32* Watch;
};
struct sLastModifiedEvent {
FileInfo file;
std::string fileName;
};
RefreshResult RefreshWatch( WatcherStructWin32* pWatch );
void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped );
void DestroyWatch( WatcherStructWin32* pWatch );
WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive,
DWORD bufferSize, DWORD notifyFilter, HANDLE iocp );
class WatcherWin32 : public Watcher {
public:
WatcherWin32(DWORD dwBufferSize) :
Struct( NULL ),
DirHandle( NULL ),
Buffer(),
lParam( 0 ),
NotifyFilter( 0 ),
StopNow( false ),
Extended( false ),
Watch( NULL ),
DirName( NULL ) {
Buffer.resize(dwBufferSize);
}
WatcherStructWin32* Struct;
HANDLE DirHandle;
std::vector<BYTE> Buffer;
LPARAM lParam;
DWORD NotifyFilter;
bool StopNow;
bool Extended;
FileWatcherImpl* Watch;
char* DirName;
sLastModifiedEvent LastModifiedEvent;
std::vector<std::pair<std::string, LARGE_INTEGER>> OldFiles;
};
} // namespace efsw
#endif
#endif

129
src/efsw/base.hpp Normal file
View File

@ -0,0 +1,129 @@
#ifndef EFSW_BASE
#define EFSW_BASE
#include <efsw/efsw.hpp>
#include <efsw/sophist.h>
namespace efsw {
typedef SOPHIST_int8 Int8;
typedef SOPHIST_uint8 Uint8;
typedef SOPHIST_int16 Int16;
typedef SOPHIST_uint16 Uint16;
typedef SOPHIST_int32 Int32;
typedef SOPHIST_uint32 Uint32;
typedef SOPHIST_int64 Int64;
typedef SOPHIST_uint64 Uint64;
#define EFSW_OS_WIN 1
#define EFSW_OS_LINUX 2
#define EFSW_OS_MACOSX 3
#define EFSW_OS_BSD 4
#define EFSW_OS_SOLARIS 5
#define EFSW_OS_HAIKU 6
#define EFSW_OS_ANDROID 7
#define EFSW_OS_IOS 8
#define EFSW_PLATFORM_WIN32 1
#define EFSW_PLATFORM_INOTIFY 2
#define EFSW_PLATFORM_KQUEUE 3
#define EFSW_PLATFORM_FSEVENTS 4
#define EFSW_PLATFORM_GENERIC 5
#if defined( _WIN32 )
/// Any Windows platform
#define EFSW_OS EFSW_OS_WIN
#define EFSW_PLATFORM EFSW_PLATFORM_WIN32
#if ( defined( _MSCVER ) || defined( _MSC_VER ) )
#define EFSW_COMPILER_MSVC
#endif
/// Force windows target version above or equal to Windows Server 2008 or Windows Vista
#if _WIN32_WINNT < 0x600
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x600
#endif
#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ ) || \
defined( __DragonFly__ )
#define EFSW_OS EFSW_OS_BSD
#define EFSW_PLATFORM EFSW_PLATFORM_KQUEUE
#elif defined( __APPLE_CC__ ) || defined( __APPLE__ )
#include <TargetConditionals.h>
#if defined( __IPHONE__ ) || ( defined( TARGET_OS_IPHONE ) && TARGET_OS_IPHONE ) || \
( defined( TARGET_IPHONE_SIMULATOR ) && TARGET_IPHONE_SIMULATOR )
#define EFSW_OS EFSW_OS_IOS
#define EFSW_PLATFORM EFSW_PLATFORM_KQUEUE
#else
#define EFSW_OS EFSW_OS_MACOSX
#if defined( EFSW_FSEVENTS_NOT_SUPPORTED )
#define EFSW_PLATFORM EFSW_PLATFORM_KQUEUE
#else
#define EFSW_PLATFORM EFSW_PLATFORM_FSEVENTS
#endif
#endif
#elif defined( __linux__ )
/// This includes Linux and Android
#ifndef EFSW_KQUEUE
#define EFSW_PLATFORM EFSW_PLATFORM_INOTIFY
#else
/// This is for testing libkqueue, sadly it doesnt work
#define EFSW_PLATFORM EFSW_PLATFORM_KQUEUE
#endif
#if defined( __ANDROID__ ) || defined( ANDROID )
#define EFSW_OS EFSW_OS_ANDROID
#else
#define EFSW_OS EFSW_OS_LINUX
#endif
#else
#if defined( __SVR4 )
#define EFSW_OS EFSW_OS_SOLARIS
#elif defined( __HAIKU__ ) || defined( __BEOS__ )
#define EFSW_OS EFSW_OS_HAIKU
#endif
/// Everything else
#define EFSW_PLATFORM EFSW_PLATFORM_GENERIC
#endif
#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
#define EFSW_PLATFORM_POSIX
#endif
#if 1 == SOPHIST_pointer64
#define EFSW_64BIT
#else
#define EFSW_32BIT
#endif
#if defined( arm ) || defined( __arm__ )
#define EFSW_ARM
#endif
#define efCOMMA ,
#define efSAFE_DELETE( p ) \
{ \
if ( p ) { \
delete ( p ); \
( p ) = NULL; \
} \
}
#define efSAFE_DELETE_ARRAY( p ) \
{ \
if ( p ) { \
delete[] ( p ); \
( p ) = NULL; \
} \
}
#define efARRAY_SIZE( __array ) ( sizeof( __array ) / sizeof( __array[0] ) )
} // namespace efsw
#endif

164
src/efsw/inotify-nosys.h Normal file
View File

@ -0,0 +1,164 @@
#ifndef _LINUX_INOTIFY_H
#define _LINUX_INOTIFY_H
#include <stdint.h>
#include <sys/syscall.h>
#include <unistd.h>
/*
* struct inotify_event - structure read from the inotify device for each event
*
* When you are watching a directory, you will receive the filename for events
* such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd.
*/
struct inotify_event {
int wd; /* watch descriptor */
uint32_t mask; /* watch mask */
uint32_t cookie; /* cookie to synchronize two events */
uint32_t len; /* length (including nulls) of name */
char name __flexarr; /* stub for possible name */
};
/* the following are legal, implemented events that user-space can watch for */
#define IN_ACCESS 0x00000001 /* File was accessed */
#define IN_MODIFY 0x00000002 /* File was modified */
#define IN_ATTRIB 0x00000004 /* Metadata changed */
#define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */
#define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
#define IN_OPEN 0x00000020 /* File was opened */
#define IN_MOVED_FROM 0x00000040 /* File was moved from X */
#define IN_MOVED_TO 0x00000080 /* File was moved to Y */
#define IN_CREATE 0x00000100 /* Subfile was created */
#define IN_DELETE 0x00000200 /* Subfile was deleted */
#define IN_DELETE_SELF 0x00000400 /* Self was deleted */
#define IN_MOVE_SELF 0x00000800 /* Self was moved */
/* the following are legal events. they are sent as needed to any watch */
#define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted */
#define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
#define IN_IGNORED 0x00008000 /* File was ignored */
/* helper events */
#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */
#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */
/* special flags */
#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */
#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */
#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */
#define IN_ISDIR 0x40000000 /* event occurred against dir */
#define IN_ONESHOT 0x80000000 /* only send event once */
/*
* All of the events - we build the list by hand so that we can add flags in
* the future and not break backward compatibility. Apps will get only the
* events that they originally wanted. Be sure to add new events here!
*/
#define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \
IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \
IN_MOVE_SELF)
#if defined (__alpha__)
# define __NR_inotify_init 444
# define __NR_inotify_add_watch 445
# define __NR_inotify_rm_watch 446
#elif defined (__arm__)
# define __NR_inotify_init (__NR_SYSCALL_BASE+316)
# define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317)
# define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318)
#elif defined (__aarch64__)
# define __NR_inotify_init 1043
# define __NR_inotify_add_watch 27
# define __NR_inotify_rm_watch 28
#elif defined (__frv__)
# define __NR_inotify_init 291
# define __NR_inotify_add_watch 292
# define __NR_inotify_rm_watch 293
#elif defined(__i386__)
# define __NR_inotify_init 291
# define __NR_inotify_add_watch 292
# define __NR_inotify_rm_watch 293
#elif defined (__ia64__)
# define __NR_inotify_init 1277
# define __NR_inotify_add_watch 1278
# define __NR_inotify_rm_watch 1279
#elif defined (__mips__)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define __NR_inotify_init (__NR_Linux + 284)
# define __NR_inotify_add_watch (__NR_Linux + 285)
# define __NR_inotify_rm_watch (__NR_Linux + 286)
# endif
# if _MIPS_SIM == _MIPS_SIM_ABI64
# define __NR_inotify_init (__NR_Linux + 243)
# define __NR_inotify_add_watch (__NR_Linux + 243)
# define __NR_inotify_rm_watch (__NR_Linux + 243)
# endif
# if _MIPS_SIM == _MIPS_SIM_NABI32
# define __NR_inotify_init (__NR_Linux + 247)
# define __NR_inotify_add_watch (__NR_Linux + 248)
# define __NR_inotify_rm_watch (__NR_Linux + 249)
# endif
#elif defined(__parisc__)
# define __NR_inotify_init (__NR_Linux + 269)
# define __NR_inotify_add_watch (__NR_Linux + 270)
# define __NR_inotify_rm_watch (__NR_Linux + 271)
#elif defined(__powerpc__) || defined(__powerpc64__)
# define __NR_inotify_init 275
# define __NR_inotify_add_watch 276
# define __NR_inotify_rm_watch 277
#elif defined (__s390__)
# define __NR_inotify_init 284
# define __NR_inotify_add_watch 285
# define __NR_inotify_rm_watch 286
#elif defined (__sh__)
# define __NR_inotify_init 290
# define __NR_inotify_add_watch 291
# define __NR_inotify_rm_watch 292
#elif defined (__sh64__)
# define __NR_inotify_init 318
# define __NR_inotify_add_watch 319
# define __NR_inotify_rm_watch 320
#elif defined (__sparc__) || defined (__sparc64__)
# define __NR_inotify_init 151
# define __NR_inotify_add_watch 152
# define __NR_inotify_rm_watch 156
#elif defined(__x86_64__)
# define __NR_inotify_init 253
# define __NR_inotify_add_watch 254
# define __NR_inotify_rm_watch 255
#else
# error "Unsupported architecture!"
#endif
static inline int inotify_init (void)
{
return syscall (__NR_inotify_init);
}
static inline int inotify_add_watch (int fd, const char *name, uint32_t mask)
{
return syscall (__NR_inotify_add_watch, fd, name, mask);
}
static inline int inotify_rm_watch (int fd, uint32_t wd)
{
return syscall (__NR_inotify_rm_watch, fd, wd);
}
#endif /* _LINUX_INOTIFY_H */

View File

@ -0,0 +1,16 @@
#ifndef EFSW_PLATFORMIMPL_HPP
#define EFSW_PLATFORMIMPL_HPP
#include <efsw/base.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <efsw/platform/posix/SystemImpl.hpp>
#include <efsw/platform/posix/FileSystemImpl.hpp>
#elif EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <efsw/platform/win/SystemImpl.hpp>
#include <efsw/platform/win/FileSystemImpl.hpp>
#else
#error Thread, Mutex, and System not implemented for this platform.
#endif
#endif

View File

@ -0,0 +1,251 @@
#include <efsw/platform/posix/FileSystemImpl.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <cstring>
#include <dirent.h>
#include <efsw/FileInfo.hpp>
#include <efsw/FileSystem.hpp>
#include <unistd.h>
#ifndef _DARWIN_FEATURE_64_BIT_INODE
#define _DARWIN_FEATURE_64_BIT_INODE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include <climits>
#include <cstdlib>
#include <sys/stat.h>
#if EFSW_OS == EFSW_OS_LINUX || EFSW_OS == EFSW_OS_SOLARIS || EFSW_OS == EFSW_OS_ANDROID
#include <sys/vfs.h>
#elif EFSW_OS == EFSW_OS_MACOSX || EFSW_OS == EFSW_OS_BSD || EFSW_OS == EFSW_OS_IOS
#include <sys/mount.h>
#include <sys/param.h>
#endif
/** Remote file systems codes */
#define S_MAGIC_AFS 0x5346414F
#define S_MAGIC_AUFS 0x61756673
#define S_MAGIC_CEPH 0x00C36400
#define S_MAGIC_CIFS 0xFF534D42
#define S_MAGIC_CODA 0x73757245
#define S_MAGIC_FHGFS 0x19830326
#define S_MAGIC_FUSEBLK 0x65735546
#define S_MAGIC_FUSECTL 0x65735543
#define S_MAGIC_GFS 0x01161970
#define S_MAGIC_GPFS 0x47504653
#define S_MAGIC_KAFS 0x6B414653
#define S_MAGIC_LUSTRE 0x0BD00BD0
#define S_MAGIC_NCP 0x564C
#define S_MAGIC_NFS 0x6969
#define S_MAGIC_NFSD 0x6E667364
#define S_MAGIC_OCFS2 0x7461636F
#define S_MAGIC_PANFS 0xAAD7AAEA
#define S_MAGIC_PIPEFS 0x50495045
#define S_MAGIC_SMB 0x517B
#define S_MAGIC_SNFS 0xBEEFDEAD
#define S_MAGIC_VMHGFS 0xBACBACBC
#define S_MAGIC_VXFS 0xA501FCF5
#if EFSW_OS == EFSW_OS_LINUX
#include <cstdio>
#include <mntent.h>
#endif
namespace efsw { namespace Platform {
#if EFSW_OS == EFSW_OS_LINUX
std::string findMountPoint( std::string file ) {
std::string cwd = FileSystem::getCurrentWorkingDirectory();
struct stat last_stat;
struct stat file_stat;
stat( file.c_str(), &file_stat );
std::string mp;
if ( efsw::FileSystem::isDirectory( file ) ) {
last_stat = file_stat;
if ( !FileSystem::changeWorkingDirectory( file ) )
return "";
} else {
std::string dir = efsw::FileSystem::pathRemoveFileName( file );
if ( !FileSystem::changeWorkingDirectory( dir ) )
return "";
if ( stat( ".", &last_stat ) < 0 )
return "";
}
while ( true ) {
struct stat st;
if ( stat( "..", &st ) < 0 )
goto done;
if ( st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino )
break;
if ( !FileSystem::changeWorkingDirectory( ".." ) ) {
goto done;
}
last_stat = st;
}
/* Finally reached a mount point, see what it's called. */
mp = FileSystem::getCurrentWorkingDirectory();
done:
FileSystem::changeWorkingDirectory( cwd );
return mp;
}
std::string findDevicePath( const std::string& directory ) {
struct mntent* ent;
FILE* aFile;
aFile = setmntent( "/proc/mounts", "r" );
if ( aFile == NULL )
return "";
while ( NULL != ( ent = getmntent( aFile ) ) ) {
std::string dirName( ent->mnt_dir );
if ( dirName == directory ) {
std::string fsName( ent->mnt_fsname );
endmntent( aFile );
return fsName;
}
}
endmntent( aFile );
return "";
}
bool isLocalFUSEDirectory( std::string directory ) {
efsw::FileSystem::dirRemoveSlashAtEnd( directory );
directory = findMountPoint( directory );
if ( !directory.empty() ) {
std::string devicePath = findDevicePath( directory );
return !devicePath.empty();
}
return false;
}
#endif
bool FileSystem::changeWorkingDirectory( const std::string& path ) {
return -1 != chdir( path.c_str() );
}
std::string FileSystem::getCurrentWorkingDirectory() {
char dir[PATH_MAX + 1];
char* result = getcwd( dir, PATH_MAX + 1 );
return result != NULL ? std::string( result ) : std::string();
}
FileInfoMap FileSystem::filesInfoFromPath( const std::string& path ) {
FileInfoMap files;
DIR* dp;
struct dirent* dirp;
if ( ( dp = opendir( path.c_str() ) ) == NULL )
return files;
while ( ( dirp = readdir( dp ) ) != NULL ) {
if ( strcmp( dirp->d_name, ".." ) != 0 && strcmp( dirp->d_name, "." ) != 0 ) {
std::string name( dirp->d_name );
std::string fpath( path + name );
files[name] = FileInfo( fpath );
}
}
closedir( dp );
return files;
}
char FileSystem::getOSSlash() {
return '/';
}
bool FileSystem::isDirectory( const std::string& path ) {
struct stat st;
int res = stat( path.c_str(), &st );
if ( 0 == res ) {
return static_cast<bool>( S_ISDIR( st.st_mode ) );
}
return false;
}
bool FileSystem::isRemoteFS( const std::string& directory ) {
#if EFSW_OS == EFSW_OS_LINUX || EFSW_OS == EFSW_OS_MACOSX || EFSW_OS == EFSW_OS_BSD || \
EFSW_OS == EFSW_OS_SOLARIS || EFSW_OS == EFSW_OS_ANDROID || EFSW_OS == EFSW_OS_IOS
struct statfs statfsbuf;
statfs( directory.c_str(), &statfsbuf );
switch ( statfsbuf.f_type | 0UL ) {
case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
{
#if EFSW_OS == EFSW_OS_LINUX
return !isLocalFUSEDirectory( directory );
#endif
}
case S_MAGIC_AFS: /* 0x5346414F remote */
case S_MAGIC_AUFS: /* 0x61756673 remote */
case S_MAGIC_CEPH: /* 0x00C36400 remote */
case S_MAGIC_CIFS: /* 0xFF534D42 remote */
case S_MAGIC_CODA: /* 0x73757245 remote */
case S_MAGIC_FHGFS: /* 0x19830326 remote */
case S_MAGIC_FUSECTL: /* 0x65735543 remote */
case S_MAGIC_GFS: /* 0x01161970 remote */
case S_MAGIC_GPFS: /* 0x47504653 remote */
case S_MAGIC_KAFS: /* 0x6B414653 remote */
case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
case S_MAGIC_NCP: /* 0x564C remote */
case S_MAGIC_NFS: /* 0x6969 remote */
case S_MAGIC_NFSD: /* 0x6E667364 remote */
case S_MAGIC_OCFS2: /* 0x7461636F remote */
case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
case S_MAGIC_PIPEFS: /* 0x50495045 remote */
case S_MAGIC_SMB: /* 0x517B remote */
case S_MAGIC_SNFS: /* 0xBEEFDEAD remote */
case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
case S_MAGIC_VXFS: /* 0xA501FCF5 remote */
{
return true;
}
default: {
return false;
}
}
#endif
return false;
}
}} // namespace efsw::Platform
#endif

View File

@ -0,0 +1,30 @@
#ifndef EFSW_FILESYSTEMIMPLPOSIX_HPP
#define EFSW_FILESYSTEMIMPLPOSIX_HPP
#include <efsw/FileInfo.hpp>
#include <efsw/base.hpp>
#if defined( EFSW_PLATFORM_POSIX )
namespace efsw { namespace Platform {
class FileSystem {
public:
static FileInfoMap filesInfoFromPath( const std::string& path );
static char getOSSlash();
static bool isDirectory( const std::string& path );
static bool isRemoteFS( const std::string& directory );
static bool changeWorkingDirectory( const std::string& path );
static std::string getCurrentWorkingDirectory();
};
}} // namespace efsw::Platform
#endif
#endif

View File

@ -0,0 +1,168 @@
#include <efsw/platform/posix/SystemImpl.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <cstdio>
#include <limits.h>
#include <pthread.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#if EFSW_OS == EFSW_OS_MACOSX
#include <CoreFoundation/CoreFoundation.h>
#elif EFSW_OS == EFSW_OS_LINUX || EFSW_OS == EFSW_OS_ANDROID
#include <libgen.h>
#include <unistd.h>
#elif EFSW_OS == EFSW_OS_HAIKU
#include <kernel/OS.h>
#include <kernel/image.h>
#elif EFSW_OS == EFSW_OS_SOLARIS
#include <stdlib.h>
#elif EFSW_OS == EFSW_OS_BSD
#include <sys/sysctl.h>
#endif
namespace efsw { namespace Platform {
void System::sleep( const unsigned long& ms ) {
// usleep( static_cast<unsigned long>( ms * 1000 ) );
// usleep is not reliable enough (it might block the
// whole process instead of just the current thread)
// so we must use pthread_cond_timedwait instead
// this implementation is inspired from Qt
// and taken from SFML
unsigned long long usecs = ms * 1000;
// get the current time
timeval tv;
gettimeofday( &tv, NULL );
// construct the time limit (current time + time to wait)
timespec ti;
ti.tv_nsec = ( tv.tv_usec + ( usecs % 1000000 ) ) * 1000;
ti.tv_sec = tv.tv_sec + ( usecs / 1000000 ) + ( ti.tv_nsec / 1000000000 );
ti.tv_nsec %= 1000000000;
// create a mutex and thread condition
pthread_mutex_t mutex;
pthread_mutex_init( &mutex, 0 );
pthread_cond_t condition;
pthread_cond_init( &condition, 0 );
// wait...
pthread_mutex_lock( &mutex );
pthread_cond_timedwait( &condition, &mutex, &ti );
pthread_mutex_unlock( &mutex );
// destroy the mutex and condition
pthread_cond_destroy( &condition );
}
std::string System::getProcessPath() {
#if EFSW_OS == EFSW_OS_MACOSX
char exe_file[FILENAME_MAX + 1];
CFBundleRef mainBundle = CFBundleGetMainBundle();
if ( mainBundle ) {
CFURLRef mainURL = CFBundleCopyBundleURL( mainBundle );
if ( mainURL ) {
int ok = CFURLGetFileSystemRepresentation( mainURL, ( Boolean ) true, (UInt8*)exe_file,
FILENAME_MAX );
if ( ok ) {
return std::string( exe_file ) + "/";
}
}
}
return "./";
#elif EFSW_OS == EFSW_OS_LINUX
char exe_file[FILENAME_MAX + 1];
int size;
size = readlink( "/proc/self/exe", exe_file, FILENAME_MAX );
if ( size < 0 ) {
return std::string( "./" );
} else {
exe_file[size] = '\0';
return std::string( dirname( exe_file ) ) + "/";
}
#elif EFSW_OS == EFSW_OS_BSD
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
char buf[1024];
size_t cb = sizeof( buf );
sysctl( mib, 4, buf, &cb, NULL, 0 );
return FileSystem::pathRemoveFileName( std::string( buf ) );
#elif EFSW_OS == EFSW_OS_SOLARIS
return FileSystem::pathRemoveFileName( std::string( getexecname() ) );
#elif EFSW_OS == EFSW_OS_HAIKU
image_info info;
int32 cookie = 0;
while ( B_OK == get_next_image_info( 0, &cookie, &info ) ) {
if ( info.type == B_APP_IMAGE )
break;
}
return FileSystem::pathRemoveFileName( std::string( info.name ) );
#elif EFSW_OS == EFSW_OS_ANDROID
return "/sdcard/";
#else
#warning getProcessPath() not implemented on this platform. ( will return "./" )
return "./";
#endif
}
void System::maxFD() {
static bool maxed = false;
if ( !maxed ) {
struct rlimit limit;
getrlimit( RLIMIT_NOFILE, &limit );
limit.rlim_cur = limit.rlim_max;
setrlimit( RLIMIT_NOFILE, &limit );
getrlimit( RLIMIT_NOFILE, &limit );
efDEBUG( "File descriptor limit %ld\n", limit.rlim_cur );
maxed = true;
}
}
Uint64 System::getMaxFD() {
static rlim_t max_fd = 0;
if ( max_fd == 0 ) {
struct rlimit limit;
getrlimit( RLIMIT_NOFILE, &limit );
max_fd = limit.rlim_cur;
}
return max_fd;
}
}} // namespace efsw::Platform
#endif

View File

@ -0,0 +1,25 @@
#ifndef EFSW_SYSTEMIMPLPOSIX_HPP
#define EFSW_SYSTEMIMPLPOSIX_HPP
#include <efsw/base.hpp>
#if defined( EFSW_PLATFORM_POSIX )
namespace efsw { namespace Platform {
class System {
public:
static void sleep( const unsigned long& ms );
static std::string getProcessPath();
static void maxFD();
static Uint64 getMaxFD();
};
}} // namespace efsw::Platform
#endif
#endif

View File

@ -0,0 +1,111 @@
#include <efsw/platform/win/FileSystemImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <climits>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifndef EFSW_COMPILER_MSVC
#include <dirent.h>
#else
#include <direct.h>
#endif
namespace efsw { namespace Platform {
bool FileSystem::changeWorkingDirectory( const std::string& path ) {
int res;
#ifdef EFSW_COMPILER_MSVC
#ifdef UNICODE
res = _wchdir( String::fromUtf8( path.c_str() ).toWideString().c_str() );
#else
res = _chdir( String::fromUtf8( path.c_str() ).toAnsiString().c_str() );
#endif
#else
res = chdir( path.c_str() );
#endif
return -1 != res;
}
std::string FileSystem::getCurrentWorkingDirectory() {
#ifdef EFSW_COMPILER_MSVC
#if defined( UNICODE ) && !defined( EFSW_NO_WIDECHAR )
wchar_t dir[_MAX_PATH];
return ( 0 != GetCurrentDirectoryW( _MAX_PATH, dir ) ) ? String( dir ).toUtf8() : std::string();
#else
char dir[_MAX_PATH];
return ( 0 != GetCurrentDirectory( _MAX_PATH, dir ) ) ? String( dir, std::locale() ).toUtf8()
: std::string();
#endif
#else
char dir[PATH_MAX + 1];
getcwd( dir, PATH_MAX + 1 );
return std::string( dir );
#endif
}
FileInfoMap FileSystem::filesInfoFromPath( const std::string& path ) {
FileInfoMap files;
String tpath( path );
if ( tpath[tpath.size() - 1] == '/' || tpath[tpath.size() - 1] == '\\' ) {
tpath += "*";
} else {
tpath += "\\*";
}
WIN32_FIND_DATAW findFileData;
HANDLE hFind = FindFirstFileW( (LPCWSTR)tpath.toWideString().c_str(), &findFileData );
if ( hFind != INVALID_HANDLE_VALUE ) {
std::string name( String( findFileData.cFileName ).toUtf8() );
std::string fpath( path + name );
if ( name != "." && name != ".." ) {
files[name] = FileInfo( fpath );
}
while ( FindNextFileW( hFind, &findFileData ) ) {
name = String( findFileData.cFileName ).toUtf8();
fpath = path + name;
if ( name != "." && name != ".." ) {
files[name] = FileInfo( fpath );
}
}
FindClose( hFind );
}
return files;
}
char FileSystem::getOSSlash() {
return '\\';
}
bool FileSystem::isDirectory( const std::string& path ) {
DWORD attrs = GetFileAttributesW( String( path ).toWideString().c_str() );
return attrs != INVALID_FILE_ATTRIBUTES && ( attrs & FILE_ATTRIBUTE_DIRECTORY ) != 0;
}
bool FileSystem::isRemoteFS( const std::string& directory ) {
if ( ( directory[0] == '\\' || directory[0] == '/' ) &&
( directory[1] == '\\' || directory[1] == '/' ) ) {
return true;
}
if ( directory.size() >= 3 ) {
return 4 == GetDriveTypeA( directory.substr( 0, 3 ).c_str() );
}
return false;
}
}} // namespace efsw::Platform
#endif

View File

@ -0,0 +1,31 @@
#ifndef EFSW_FILESYSTEMIMPLWIN_HPP
#define EFSW_FILESYSTEMIMPLWIN_HPP
#include <efsw/FileInfo.hpp>
#include <efsw/String.hpp>
#include <efsw/base.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
namespace efsw { namespace Platform {
class FileSystem {
public:
static FileInfoMap filesInfoFromPath( const std::string& path );
static char getOSSlash();
static bool isDirectory( const std::string& path );
static bool isRemoteFS( const std::string& directory );
static bool changeWorkingDirectory( const std::string& path );
static std::string getCurrentWorkingDirectory();
};
}} // namespace efsw::Platform
#endif
#endif

View File

@ -0,0 +1,46 @@
#include <efsw/String.hpp>
#include <efsw/platform/win/SystemImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <cstdlib>
#include <windows.h>
namespace efsw { namespace Platform {
void System::sleep( const unsigned long& ms ) {
::Sleep( ms );
}
std::string System::getProcessPath() {
// Get path to executable:
WCHAR szDrive[_MAX_DRIVE];
WCHAR szDir[_MAX_DIR];
WCHAR szFilename[_MAX_DIR];
WCHAR szExt[_MAX_DIR];
std::wstring dllName( _MAX_DIR, 0 );
GetModuleFileNameW( 0, &dllName[0], _MAX_PATH );
#ifdef EFSW_COMPILER_MSVC
_wsplitpath_s( dllName.c_str(), szDrive, _MAX_DRIVE, szDir, _MAX_DIR, szFilename, _MAX_DIR,
szExt, _MAX_DIR );
#else
_wsplitpath( dllName.c_str(), szDrive, szDir, szFilename, szExt );
#endif
return String( szDrive ).toUtf8() + String( szDir ).toUtf8();
}
void System::maxFD() {}
Uint64 System::getMaxFD() { // Number of ReadDirectory per thread
return 60;
}
}} // namespace efsw::Platform
#endif

View File

@ -0,0 +1,25 @@
#ifndef EFSW_SYSTEMIMPLWIN_HPP
#define EFSW_SYSTEMIMPLWIN_HPP
#include <efsw/base.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
namespace efsw { namespace Platform {
class System {
public:
static void sleep( const unsigned long& ms );
static std::string getProcessPath();
static void maxFD();
static Uint64 getMaxFD();
};
}} // namespace efsw::Platform
#endif
#endif

147
src/efsw/sophist.h Normal file
View File

@ -0,0 +1,147 @@
/* sophist.h - 0.3 - public domain - Sean Barrett 2010
** Knowledge drawn from Brian Hook's posh.h and http://predef.sourceforge.net
** Sophist provides portable types; you typedef/#define them to your own names
**
** defines:
** - SOPHIST_endian - either SOPHIST_little_endian or SOPHIST_big_endian
** - SOPHIST_has_64 - either 0 or 1; if 0, int64 types aren't defined
** - SOPHIST_pointer64 - either 0 or 1; if 1, pointer is 64-bit
**
** - SOPHIST_intptr, SOPHIST_uintptr - integer same size as pointer
** - SOPHIST_int8, SOPHIST_uint8, SOPHIST_int16, SOPHIST_uint16
** - SOPHIST_int32, SOPHIST_uint32, SOPHIST_int64, SOPHIST_uint64
** - SOPHIST_int64_constant(number) - macros for creating 64-bit
** - SOPHIST_uint64_constant(number) integer constants
** - SOPHIST_printf_format64 - string for printf format for int64
*/
#ifndef __INCLUDE_SOPHIST_H__
#define __INCLUDE_SOPHIST_H__
#define SOPHIST_compiletime_assert(name,val) \
typedef int SOPHIST__assert##name[(val) ? 1 : -1]
/* define a couple synthetic rules to make code more readable */
#if (defined(__sparc__) || defined(__sparc)) && \
(defined(__arch64__) || defined(__sparcv9) || defined(__sparc_v9__))
#define SOPHIST_sparc64
#endif
#if (defined(linux) || defined(__linux__)) && \
(defined(__alpha)||defined(__alpha__)||defined(__x86_64__)||defined(_M_X64))
#define SOPHIST_linux64
#endif
/* basic types */
typedef signed char SOPHIST_int8;
typedef unsigned char SOPHIST_uint8;
typedef signed short SOPHIST_int16;
typedef unsigned short SOPHIST_uint16;
#ifdef __palmos__
typedef signed long SOPHIST_int32;
typedef unsigned long SOPHIST_uint32;
#else
typedef signed int SOPHIST_int32;
typedef unsigned int SOPHIST_uint32;
#endif
#ifndef SOPHIST_NO_64
#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) \
|| (defined(__alpha) && defined(__DECC))
typedef signed __int64 SOPHIST_int64;
typedef unsigned __int64 SOPHIST_uint64;
#define SOPHIST_has_64 1
#define SOPHIST_int64_constant(x) (x##i64)
#define SOPHIST_uint64_constant(x) (x##ui64)
#define SOPHIST_printf_format64 "I64"
#elif defined(__LP64__) || defined(__powerpc64__) || defined(SOPHIST_sparc64)
typedef signed long SOPHIST_int64;
typedef unsigned long SOPHIST_uint64;
#define SOPHIST_has_64 1
#define SOPHIST_int64_constant(x) ((SOPHIST_int64) x)
#define SOPHIST_uint64_constant(x) ((SOPHIST_uint64) x)
#define SOPHIST_printf_format64 "l"
#elif defined(_LONG_LONG) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) \
|| defined(__GNUC__) || defined(__MWERKS__) || defined(__APPLE_CC__) \
|| defined(sgi) || defined (__sgi) || defined(__sgi__) \
|| defined(_CRAYC)
typedef signed long long SOPHIST_int64;
typedef unsigned long long SOPHIST_uint64;
#define SOPHIST_has_64 1
#define SOPHIST_int64_constant(x) (x##LL)
#define SOPHIST_uint64_constant(x) (x##ULL)
#define SOPHIST_printf_format64 "ll"
#endif
#endif
#ifndef SOPHIST_has_64
#define SOPHIST_has_64 0
#endif
SOPHIST_compiletime_assert( int8 , sizeof(SOPHIST_int8 ) == 1);
SOPHIST_compiletime_assert(uint16, sizeof(SOPHIST_int16) == 2);
SOPHIST_compiletime_assert( int32, sizeof(SOPHIST_int32 ) == 4);
SOPHIST_compiletime_assert(uint32, sizeof(SOPHIST_uint32) == 4);
#if SOPHIST_has_64
SOPHIST_compiletime_assert( int64, sizeof(SOPHIST_int64 ) == 8);
SOPHIST_compiletime_assert(uint64, sizeof(SOPHIST_uint64) == 8);
#endif
/* determine whether pointers are 64-bit */
#if defined(SOPHIST_linux64) || defined(SOPHIST_sparc64) \
|| defined(__osf__) || (defined(_WIN64) && !defined(_XBOX)) \
|| defined(__64BIT__) \
|| defined(__LP64) || defined(__LP64__) || defined(_LP64) \
|| defined(_ADDR64) || defined(_CRAYC) \
#define SOPHIST_pointer64 1
SOPHIST_compiletime_assert(pointer64, sizeof(void*) == 8);
typedef SOPHIST_int64 SOPHIST_intptr;
typedef SOPHIST_uint64 SOPHIST_uintptr;
#else
#define SOPHIST_pointer64 0
SOPHIST_compiletime_assert(pointer64, sizeof(void*) <= 4);
/* do we care about pointers that are only 16-bit? */
typedef SOPHIST_int32 SOPHIST_intptr;
typedef SOPHIST_uint32 SOPHIST_uintptr;
#endif
SOPHIST_compiletime_assert(intptr, sizeof(SOPHIST_intptr) == sizeof(char *));
/* enumerate known little endian cases; fallback to big-endian */
#define SOPHIST_little_endian 1
#define SOPHIST_big_endian 2
#if defined(__386__) || defined(i386) || defined(__i386__) \
|| defined(__X86) || defined(_M_IX86) \
|| defined(_M_X64) || defined(__x86_64__) \
|| defined(alpha) || defined(__alpha) || defined(__alpha__) \
|| defined(_M_ALPHA) \
|| defined(ARM) || defined(_ARM) || defined(__arm__) \
|| defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
|| defined(_WIN32_WCE) || defined(__NT__) \
|| defined(__MIPSEL__)
#define SOPHIST_endian SOPHIST_little_endian
#else
#define SOPHIST_endian SOPHIST_big_endian
#endif
#endif /* __INCLUDE_SOPHIST_H__ */

7988
src/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

1724
src/stb_image_write.h Normal file

File diff suppressed because it is too large Load Diff

164
src/test/efsw-test.c Normal file
View File

@ -0,0 +1,164 @@
#include <efsw/efsw.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
const char PATH_SEPARATOR =
#ifdef _WIN32
'\\';
#else
'/';
#endif
bool STOP = false;
void sigend( int sig ) {
printf( "Bye bye" );
STOP = true;
}
void sleepMsecs( int msecs ) {
#ifdef _WIN32
Sleep( msecs );
#else
sleep( msecs );
#endif
}
const char* getActionName( enum efsw_action action ) {
switch ( action ) {
case EFSW_ADD:
return "Add";
case EFSW_MODIFIED:
return "Modified";
case EFSW_DELETE:
return "Delete";
case EFSW_MOVED:
return "Moved";
default:
return "Bad Action";
}
}
void handleFileAction( efsw_watcher watcher, efsw_watchid watchid, const char* dir,
const char* filename, enum efsw_action action, const char* oldFilename,
void* param ) {
if ( strlen( oldFilename ) == 0 ) {
printf( "Watch ID %ld DIR (%s) FILE (%s) has event %s\n", watchid, dir, filename,
getActionName( action ) );
} else {
printf( "Watch ID %ld DIR (%s) FILE (from file %s to %s) has event %s\n", watchid, dir,
oldFilename, filename, getActionName( action ) );
}
}
efsw_watchid handleWatchID( efsw_watchid watchid ) {
switch ( watchid ) {
case EFSW_NOTFOUND:
case EFSW_REPEATED:
case EFSW_OUTOFSCOPE:
case EFSW_REMOTE:
case EFSW_WATCHER_FAILED:
case EFSW_UNSPECIFIED: {
printf( "%s\n", efsw_getlasterror() );
break;
}
default: {
printf( "Added WatchID: %ld\n", watchid );
}
}
return watchid;
}
int main( int argc, char** argv ) {
signal( SIGABRT, sigend );
signal( SIGINT, sigend );
signal( SIGTERM, sigend );
printf( "Press ^C to exit demo\n" );
bool commonTest = true;
bool useGeneric = false;
char* path = 0;
if ( argc >= 2 ) {
path = argv[1];
struct stat s;
if ( stat( path, &s ) == 0 && ( s.st_mode & S_IFDIR ) == S_IFDIR ) {
commonTest = false;
}
if ( argc >= 3 ) {
if ( strcmp( argv[2], "true" ) == 0 ) {
useGeneric = true;
}
}
}
/// create the file watcher object
efsw_watcher fileWatcher = efsw_create( useGeneric );
efsw_follow_symlinks( fileWatcher, false );
efsw_allow_outofscopelinks( fileWatcher, false );
if ( commonTest ) {
char cwd[256];
getcwd( cwd, sizeof( cwd ) );
printf( "CurPath: %s\n", cwd );
/// starts watching
efsw_watch( fileWatcher );
/// add a watch to the system
char path1[512];
snprintf( path1, sizeof( path1 ), "%s%ctest", cwd, PATH_SEPARATOR );
handleWatchID( efsw_addwatch_withoptions( fileWatcher, path1, handleFileAction, true, 0, 0,
0, NULL ) );
/// adds another watch after started watching...
sleepMsecs( 100 );
char path2[512];
snprintf( path2, sizeof( path2 ), "%s%ctest2", cwd, PATH_SEPARATOR );
efsw_watchid watchID = handleWatchID( efsw_addwatch_withoptions(
fileWatcher, path2, handleFileAction, true, 0, 0, 0, NULL ) );
/// delete the watch
if ( watchID > 0 ) {
sleepMsecs( 1000 );
efsw_removewatch_byid( fileWatcher, watchID );
}
} else {
if ( efsw_addwatch( fileWatcher, path, handleFileAction, true, 0 ) > 0 ) {
efsw_watch( fileWatcher );
printf( "Watching directory: %s\n", path );
if ( useGeneric ) {
printf( "Using generic backend watcher\n" );
}
} else {
printf( "Error trying to watch directory: %s\n", path );
printf( "%s\n", efsw_getlasterror() );
}
}
while ( !STOP ) {
sleepMsecs( 100 );
}
efsw_release( fileWatcher );
return 0;
}

Some files were not shown because too many files have changed in this diff Show More