Dep: Add efsw (entropia file system watcher)

* Uses system API's which reduces the overhead
  instead of checking periodically for changes.
* Will be used in the hotswap system to reload
  shared libraries on changes.
This commit is contained in:
Naios
2015-10-08 03:22:47 +02:00
parent d024314b13
commit 46daaf7689
79 changed files with 10579 additions and 1 deletions

View File

@@ -35,6 +35,7 @@ if(SERVERS)
add_subdirectory(readline)
add_subdirectory(gsoap)
add_subdirectory(rapidjson)
add_subdirectory(efsw)
endif()
if(TOOLS)

View File

@@ -16,6 +16,10 @@ cppformat (type safe format library)
https://github.com/cppformat/cppformat
Version: 5174b8ca281426af604b85fdf53be8a748b33f56
efws (Entropia File System Watcher - crossplatform file system watcher)
https://bitbucket.org/SpartanJ/efsw
ff0b69daeca1edf7785a8a580518e462be5a6f3d
G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License)
http://g3d.sourceforge.net/
Version: 9.0-Release r4036

View File

@@ -0,0 +1,5 @@
repo: 78c2ea8c48b213ee0078d6326a1dd719d0844764
node: ff0b69daeca1edf7785a8a580518e462be5a6f3d
branch: default
latesttag: null
latesttagdistance: 144

85
dep/efsw/CMakeLists.txt Normal file
View File

@@ -0,0 +1,85 @@
if (WITH_DYNAMIC_LINKING)
set(SRCS
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/Mutex.cpp
src/efsw/String.cpp
src/efsw/System.cpp
src/efsw/Thread.cpp
src/efsw/Watcher.cpp
src/efsw/WatcherGeneric.cpp)
if(WIN32)
list(APPEND SRCS
src/efsw/platform/win/FileSystemImpl.cpp
src/efsw/platform/win/MutexImpl.cpp
src/efsw/platform/win/SystemImpl.cpp
src/efsw/platform/win/ThreadImpl.cpp)
else()
list(APPEND SRCS
src/efsw/platform/posix/FileSystemImpl.cpp
src/efsw/platform/posix/MutexImpl.cpp
src/efsw/platform/posix/SystemImpl.cpp
src/efsw/platform/posix/ThreadImpl.cpp)
endif()
if (WIN32)
list(APPEND SRCS
src/efsw/WatcherWin32.cpp
src/efsw/FileWatcherWin32.cpp)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
list(APPEND SRCS
src/efsw/FileWatcherInotify.cpp
src/efsw/WatcherInotify.cpp)
if (NOT EXISTS "/usr/include/sys/inotify.h" AND NOT EXISTS "/usr/local/include/sys/inotify.h")
add_definitions(-DEFSW_INOTIFY_NOSYS)
endif()
elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR APPLE)
list(APPEND SRCS
src/efsw/FileWatcherKqueue.cpp
src/efsw/WatcherKqueue.cpp)
if (APPLE)
list(APPEND SRCS
src/efsw/FileWatcherFSEvents.cpp
src/efsw/WatcherFSEvents.cpp)
exec_program(uname ARGS -v OUTPUT_VARIABLE OSX_VERSION)
string(REGEX MATCH "[0-9]+" OSX_VERSION ${OSX_VERSION})
if (NOT OSX_VERSION GREATER 9)
add_definitions(-DEFSW_FSEVENTS_NOT_SUPPORTED)
endif()
set(OPTIONAL_MAC_LINK_LIBRARIES "-framework CoreFoundation" "-framework CoreServices")
endif()
endif()
add_library(efsw STATIC ${SRCS})
target_include_directories(efsw
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(efsw
PUBLIC
threads
${OPTIONAL_MAC_LINK_LIBRARIES})
set_target_properties(efsw
PROPERTIES
FOLDER
"dep")
else()
add_library(efsw INTERFACE)
endif()

22
dep/efsw/LICENSE Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2012 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.

138
dep/efsw/README.md Normal file
View File

@@ -0,0 +1,138 @@
Entropia File System Watcher
============================
**efsw** is a C++ cross-platform file system watcher and notifier.
**efsw** monitors the file system asynchronously for changes to files and directories by watching a list of specified paths, and raises events when a directory or file change.
**efsw** supports recursive directories watch, tracking the entire sub directory tree.
**efsw** currently supports the following platforms:
* Linux via [inotify](http://en.wikipedia.org/wiki/Inotify)
* Windows via [I/O Completion Ports](http://en.wikipedia.org/wiki/IOCP)
* Mac OS X via [FSEvents](http://en.wikipedia.org/wiki/FSEvents) or [kqueue](http://en.wikipedia.org/wiki/Kqueue)
* FreeBSD/BSD via [kqueue](http://en.wikipedia.org/wiki/Kqueue)
* OS-independent generic watcher
(polling the disk for directory snapshots and comparing them periodically)
If any of the backend fails to start by any reason, it will fallback to the OS-independent implementation.
This should never happen, except for the Kqueue implementation, see `Platform limitations and clarifications`.
**Code License**
--------------
[MIT License](http://www.opensource.org/licenses/mit-license.php)
**Some example code:**
--------------------
:::c++
// Inherits from the abstract listener class, and implements the the file action handler
class UpdateListener : public efsw::FileWatchListener
{
public:
UpdateListener() {}
void handleFileAction( efsw::WatchID watchid, const std::string& dir, const std::string& filename, efsw::Action action, std::string oldFilename = "" )
{
switch( action )
{
case efsw::Actions::Add:
std::cout << "DIR (" << dir << ") FILE (" << filename << ") has event Added" << std::endl;
break;
case efsw::Actions::Delete:
std::cout << "DIR (" << dir << ") FILE (" << filename << ") has event Delete" << std::endl;
break;
case efsw::Actions::Modified:
std::cout << "DIR (" << dir << ") FILE (" << filename << ") has event Modified" << std::endl;
break;
case efsw::Actions::Moved:
std::cout << "DIR (" << dir << ") FILE (" << filename << ") has event Moved from (" << oldFilename << ")" << std::endl;
break;
default:
std::cout << "Should never happen!" << std::endl;
}
}
};
// Create the file system watcher instance
// efsw::FileWatcher allow a first boolean parameter that indicates if it should start with the generic file watcher instead of the platform specific backend
efsw::FileWatcher * fileWatcher = new efsw::FileWatcher();
// Create the instance of your efsw::FileWatcherListener implementation
UpdateListener * listener = new UpdateListener();
// Add a folder to watch, and get the efsw::WatchID
// It will watch the /tmp folder recursively ( the third parameter indicates that is recursive )
// Reporting the files and directories changes to the instance of the listener
efsw::WatchID watchID = fileWatcher->addWatch( "/tmp", listener, true );
// Adds another directory to watch. This time as non-recursive.
efsw::WatchID watchID2 = fileWatcher->addWatch( "/usr", listener, false );
// Start watching asynchronously the directories
fileWatcher.watch();
// Remove the second watcher added
// You can also call removeWatch by passing the watch path ( it must end with an slash or backslash in windows, since that's how internally it's saved )
fileWatcher->removeWatch( watchID2 );
**Dependencies**
--------------
None :)
**Compiling**
------------
To generate project files you will need to [download and install](http://industriousone.com/premake/download) [Premake](http://industriousone.com/what-premake)
Then you can generate the project for your platform just going to the project directory where the premake4.lua file is located and then execute:
`premake4 gmake` to generate project Makefiles, then `cd make/*YOURPLATFORM*/`, and finally `make` or `make config=release` ( it will generate the static lib, the shared lib and the test application ).
or
`premake4 vs2010` to generate Visual Studio 2010 project.
or
`premake4 xcode4` to generate Xcode 4 project.
There is also a cmake file that i don't oficially support but it works just fine, provided by [Mohammed Nafees](https://bitbucket.org/binaryking).
**Platform limitations and clarifications**
-------------------------------------------
Directory paths are expected to be encoded as UTF-8 strings in all platforms.
handleFileAction returns UTF-8 strings in all platforms.
Windows and FSEvents Mac OS X implementation can't follow symlinks ( it will ignore followSymlinks() and allowOutOfScopeLinks() ).
Kqueue implementation is limited by the maximun number of file descriptors allowed per process by the OS, in the case of reaching the file descriptors limit ( in BSD around 18000 and in OS X around 10240 ) it will fallback to the generic file watcher.
OS X will only use Kqueue if OS X version is below to 10.5, and this implementation needs to be compiled separately from the OS X >= 10.5 implementation. Since there's no way to compile FSEvents backend in OS X below 10.5.
FSEvents for OS X Lion and beyond in some cases will generate more actions that in reality ocurred, since fine-grained implementation of FSEvents doesn't give the order of the actions retrieved, in some cases i need to guess/aproximate the order of them.
Generic watcher relies on the inode information to detect file and directories renames/move. Since Windows has no concept of inodes as Unix platforms do, there is no current reliable way of determining file/directory movement on Windows without help from the Windows API ( this is replaced with Add/Delete events ).
Linux versions below 2.6.13 are not supported, since inotify wasn't implemented yet. I'm not interested in support older kernels, since i don't see the point. If someone needs this open an issue in the issue tracker and i may consider implenent a dnotify backend.
OS-independent watcher, Kqueue and FSEvents for OS X below 10.5 keep cache of the directories structures, to be able to detect changes in the directories. This means that there's a memory overhead for this backends.
**Useful information**
--------------------
The project also comes with a C API wrapper, contributed by [Sepul Sepehr Taghdisian](https://bitbucket.org/sepul).
There's a string manipulation class not exposed in the efsw header ( efsw::String ) that can be used to make string encoding conversion.
**Clarifications**
----------------
This software started as a fork of the [simplefilewatcher](http://code.google.com/p/simplefilewatcher/) by James Wynn (james[at]jameswynn.com), [MIT licensed](http://www.opensource.org/licenses/mit-license.html).
The icon used for the project is part of the [Haiku®'s Icons](http://www.haiku-inc.org/haiku-icons.html), [MIT licensed](http://www.opensource.org/licenses/mit-license.html).

View File

@@ -0,0 +1,151 @@
/**
@author Sepul Sepehr Taghdisian
Copyright (c) 2013 Martin 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_UNSPECIFIED = -6
};
/// 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
);
/**
* 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);
/// Retreive last error occured by file-watcher
EFSW_API const char* efsw_getlasterror();
/// Add a directory watch. Same as the other addWatch, but doesn't have recursive option.
/// For backwards compatibility.
/// 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);
/// 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

View File

@@ -0,0 +1,197 @@
/**
@author Martín Lucas Golini
Copyright (c) 2013 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 <list>
#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
namespace efsw {
/// Type for a watch id
typedef long WatchID;
// forward declarations
class FileWatcherImpl;
class FileWatchListener;
/// 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
{
FileNotFound = -1,
FileRepeated = -2,
FileOutOfScope = -3,
FileNotReadable = -4,
FileRemote = -5, /** Directory in remote file system ( create a generic FileWatcher instance to watch this directory ). */
Unspecified = -6
};
class EFSW_API Log
{
public:
/// @return The last error logged
static std::string getLastErrorLog();
/// Creates an error of the type specified
static Error createLastError( Error err, std::string log );
};
}
typedef Errors::Error Error;
/// 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
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);
/// 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::list<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:
FileWatchListener() {}
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;
};
}
#endif

View File

@@ -0,0 +1,85 @@
#include <efsw/Debug.hpp>
#include <iostream>
#ifdef EFSW_COMPILER_MSVC
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <crtdbg.h>
#endif
#include <cassert>
#include <cstdio>
#include <cstdarg>
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
}

View File

@@ -0,0 +1,50 @@
#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
}
#endif

View File

@@ -0,0 +1,451 @@
#include <efsw/DirWatcherGeneric.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/Debug.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 )
{
FileSystem::dirAddSlashAtEnd(directory);
dir = Parent->DirSnap.DirectoryInfo.Filepath + 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 dir( DirSnap.DirectoryInfo.Filepath + 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;
}
}

View File

@@ -0,0 +1,55 @@
#ifndef EFSW_DIRWATCHERGENERIC_HPP
#define EFSW_DIRWATCHERGENERIC_HPP
#include <efsw/WatcherGeneric.hpp>
#include <efsw/FileInfo.hpp>
#include <efsw/DirectorySnapshot.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 = "");
};
}
#endif

View File

@@ -0,0 +1,261 @@
#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 );
}
}
}
void DirectorySnapshot::setDirectoryInfo( std::string directory )
{
DirectoryInfo = FileInfo( directory );
}
void DirectorySnapshot::initFiles()
{
Files = FileSystem::filesInfoFromPath( DirectoryInfo.Filepath );
FileInfoMap::iterator it = Files.begin();
std::list<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::list<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 );
}
}

View File

@@ -0,0 +1,46 @@
#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 );
};
}
#endif

View File

@@ -0,0 +1,29 @@
#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();
}
}

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++ )
}
#endif

View File

@@ -0,0 +1,274 @@
#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()
{
return 0 != S_ISDIR(Permissions);
}
bool FileInfo::isRegularFile()
{
return 0 != S_ISREG(Permissions);
}
bool FileInfo::isReadable()
{
return 0 != S_ISRDBL(Permissions);
}
bool FileInfo::isLink()
{
#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);
}
}

View File

@@ -0,0 +1,66 @@
#ifndef EFSW_FILEINFO_HPP
#define EFSW_FILEINFO_HPP
#include <efsw/base.hpp>
#include <string>
#include <map>
#include <list>
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();
bool isRegularFile();
bool isReadable();
bool sameInode( const FileInfo& Other ) const;
bool isLink();
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::list<FileInfo> FileInfoList;
typedef std::list< std::pair< std::string, FileInfo> > MovedList;
}
#endif

View File

@@ -0,0 +1,124 @@
#include <efsw/FileSystem.hpp>
#include <efsw/platform/platformimpl.hpp>
#if EFSW_OS == EFSW_OS_MACOSX
#include <CoreFoundation/CoreFoundation.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);
char c_str[255 + 1];
CFStringGetCString(cfMutable, c_str, sizeof(c_str)-1, kCFStringEncodingUTF8);
CFRelease(cfStringRef);
CFRelease(cfMutable);
return std::string(c_str);
#else
return name;
#endif
}
bool FileSystem::isRemoteFS( const std::string& directory )
{
return Platform::FileSystem::isRemoteFS( directory );
}
}

View File

@@ -0,0 +1,40 @@
#ifndef EFSW_FILESYSTEM_HPP
#define EFSW_FILESYSTEM_HPP
#include <efsw/base.hpp>
#include <efsw/FileInfo.hpp>
#include <map>
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 void realPath( std::string curdir, std::string& path );
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 );
};
}
#endif

View File

@@ -0,0 +1,145 @@
#include <efsw/efsw.hpp>
#include <efsw/FileWatcherImpl.hpp>
#include <efsw/FileWatcherGeneric.hpp>
#include <efsw/FileSystem.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)
{
if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) )
{
return mImpl->addWatch(directory, watcher, false);
}
else
{
return Errors::Log::createLastError( Errors::FileRemote, directory );
}
}
WatchID FileWatcher::addWatch(const std::string& directory, FileWatchListener* watcher, bool recursive)
{
if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) )
{
return mImpl->addWatch(directory, watcher, recursive);
}
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::list<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;
}
}

View File

@@ -0,0 +1,132 @@
#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;
public:
Watcher_CAPI(efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param)
{
mWatcher = watcher;
mFn = fn;
mParam = param;
}
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 );
}
};
/*************************************************************************************************
* globals
*/
static std::vector<Watcher_CAPI*> g_callbacks;
Watcher_CAPI* find_callback(efsw_watcher watcher, efsw_pfn_fileaction_callback fn)
{
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)
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_watchid efsw_addwatch(efsw_watcher watcher, const char* directory,
efsw_pfn_fileaction_callback callback_fn, int recursive, void * param)
{
Watcher_CAPI* callback = find_callback(watcher, callback_fn);
if (callback == NULL) {
callback = new Watcher_CAPI(watcher, callback_fn, param);
g_callbacks.push_back(callback);
}
return ((efsw::FileWatcher*)watcher)->addWatch(std::string(directory), callback,
TOBOOL(recursive));
}
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,278 @@
#include <efsw/FileWatcherFSEvents.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/System.hpp>
#include <efsw/Debug.hpp>
#include <efsw/String.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;
}
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++ )
{
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 ),
mRunLoopRef( NULL ),
mLastWatchID(0),
mThread( NULL )
{
mInitOK = true;
watch();
}
FileWatcherFSEvents::~FileWatcherFSEvents()
{
WatchMap::iterator iter = mWatches.begin();
for( ; iter != mWatches.end(); ++iter )
{
WatcherFSEvents * watch = iter->second;
efSAFE_DELETE( watch );
}
mWatches.clear();
mInitOK = false;
if ( NULL != mRunLoopRef )
{
CFRunLoopStop( mRunLoopRef );
}
efSAFE_DELETE( mThread );
}
WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive )
{
/// Wait to the RunLoopRef to be ready
while ( NULL == mRunLoopRef )
{
System::sleep( 1 );
}
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 );
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->init();
mWatchesLock.lock();
mWatches.insert(std::make_pair(mLastWatchID, pWatch));
mWatchesLock.unlock();
return pWatch->ID;
}
void FileWatcherFSEvents::removeWatch(const std::string& directory)
{
mWatchesLock.lock();
WatchMap::iterator iter = mWatches.begin();
for(; iter != mWatches.end(); ++iter)
{
if( directory == iter->second->Directory )
{
removeWatch( iter->second->ID );
return;
}
}
mWatchesLock.unlock();
}
void FileWatcherFSEvents::removeWatch(WatchID watchid)
{
mWatchesLock.lock();
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 );
mWatchesLock.unlock();
}
void FileWatcherFSEvents::watch()
{
if ( NULL == mThread )
{
mThread = new Thread( &FileWatcherFSEvents::run, this );
mThread->launch();
}
}
void FileWatcherFSEvents::run()
{
mRunLoopRef = CFRunLoopGetCurrent();
while ( mInitOK )
{
if ( !mNeedInit.empty() )
{
for ( std::list<WatcherFSEvents*>::iterator it = mNeedInit.begin(); it != mNeedInit.end(); it++ )
{
(*it)->initAsync();
}
mNeedInit.clear();
}
CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0.5, kCFRunLoopRunTimedOut );
}
}
void FileWatcherFSEvents::handleAction(Watcher* watch, const std::string& filename, unsigned long action, std::string oldFilename)
{
/// Not used
}
std::list<std::string> FileWatcherFSEvents::directories()
{
std::list<std::string> dirs;
mWatchesLock.lock();
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
dirs.push_back( std::string( it->second->Directory ) );
}
mWatchesLock.unlock();
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;
}
}
#endif

View File

@@ -0,0 +1,107 @@
#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 <efsw/WatcherFSEvents.hpp>
#include <map>
#include <list>
#include <vector>
namespace efsw
{
/* OSX < 10.7 has no file events */
/* So i declare the events constants */
enum FSEventEvents
{
efswFSEventStreamCreateFlagFileEvents = 0x00000010,
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 |
efswFSEventStreamEventFlagItemChangeOwner |
efswFSEventStreamEventFlagItemXattrMod
};
/// 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);
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch(const std::string& directory);
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch(WatchID watchid);
/// Updates the watcher. Must be called often.
void watch();
/// Handles the action
void handleAction(Watcher* watch, const std::string& filename, unsigned long action, std::string oldFilename = "");
/// @return Returns a list of the directories that are being watched
std::list<std::string> directories();
protected:
static void FSEventCallback( ConstFSEventStreamRef streamRef,
void *userData,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]
);
CFRunLoopRef mRunLoopRef;
/// Vector of WatcherWin32 pointers
WatchMap mWatches;
/// The last watchid
WatchID mLastWatchID;
Thread * mThread;
Mutex mWatchesLock;
bool pathInWatches( const std::string& path );
std::list<WatcherFSEvents*> mNeedInit;
private:
void run();
};
}
#endif
#endif

View File

@@ -0,0 +1,197 @@
#include <efsw/FileWatcherGeneric.hpp>
#include <efsw/FileSystem.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;
mThread->wait();
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)
{
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 );
mWatchesLock.lock();
mWatches.push_back(pWatch);
mWatchesLock.unlock();
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);
mWatchesLock.lock();
mWatches.erase( it );
efSAFE_DELETE( watch ) ;
mWatchesLock.unlock();
return;
}
}
}
void FileWatcherGeneric::removeWatch(WatchID watchid)
{
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); it++ )
{
if ( (*it)->ID == watchid )
{
WatcherGeneric * watch = (*it);
mWatchesLock.lock();
mWatches.erase( it );
efSAFE_DELETE( watch ) ;
mWatchesLock.unlock();
return;
}
}
}
void FileWatcherGeneric::watch()
{
if ( NULL == mThread )
{
mThread = new Thread( &FileWatcherGeneric::run, this );
mThread->launch();
}
}
void FileWatcherGeneric::run()
{
do
{
mWatchesLock.lock();
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); it++ )
{
(*it)->watch();
}
mWatchesLock.unlock();
if ( mInitOK ) System::sleep( 1000 );
} while ( mInitOK );
}
void FileWatcherGeneric::handleAction(Watcher * watch, const std::string& filename, unsigned long action, std::string oldFilename)
{
/// Not used
}
std::list<std::string> FileWatcherGeneric::directories()
{
std::list<std::string> dirs;
mWatchesLock.lock();
WatchList::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); it++ )
{
dirs.push_back( (*it)->Directory );
}
mWatchesLock.unlock();
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;
}
}

View File

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

View File

@@ -0,0 +1,29 @@
#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 mInitOK;
}
bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string& link )
{
return ( mFileWatcher->followSymlinks() && mFileWatcher->allowOutOfScopeLinks() ) || -1 != String::strStartsWith( curPath, link );
}
}

View File

@@ -0,0 +1,54 @@
#ifndef EFSW_FILEWATCHERIMPL_HPP
#define EFSW_FILEWATCHERIMPL_HPP
#include <efsw/base.hpp>
#include <efsw/efsw.hpp>
#include <efsw/Watcher.hpp>
#include <efsw/Thread.hpp>
#include <efsw/Mutex.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) = 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::list<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;
FileWatcher * mFileWatcher;
bool mInitOK;
bool mIsGeneric;
};
}
#endif

View File

@@ -0,0 +1,531 @@
#include <efsw/FileWatcherInotify.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#ifdef EFSW_INOTIFY_NOSYS
#include <efsw/inotify-nosys.h>
#else
#include <sys/inotify.h>
#endif
#include <efsw/FileSystem.hpp>
#include <efsw/System.hpp>
#include <efsw/Debug.hpp>
#define BUFF_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*1024)
namespace efsw
{
FileWatcherInotify::FileWatcherInotify( FileWatcher * parent ) :
FileWatcherImpl( parent ),
mFD(-1),
mThread(NULL)
{
mFD = inotify_init();
if (mFD < 0)
{
efDEBUG( "Error: %s\n", strerror(errno) );
}
else
{
mInitOK = true;
}
}
FileWatcherInotify::~FileWatcherInotify()
{
mInitOK = false;
efSAFE_DELETE( mThread );
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 )
{
return addWatch( directory, watcher, recursive, NULL );
}
WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, WatcherInotify * parent )
{
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 = wd;
pWatch->Directory = dir;
pWatch->Recursive = recursive;
pWatch->Parent = parent;
mWatchesLock.lock();
mWatches.insert(std::make_pair(wd, pWatch));
mWatchesLock.unlock();
if ( NULL == pWatch->Parent )
{
mRealWatches[ pWatch->ID ] = pWatch;
}
if ( pWatch->Recursive )
{
std::map<std::string, FileInfo> files = FileSystem::filesInfoFromPath( pWatch->Directory );
std::map<std::string, FileInfo>::iterator it = files.begin();
for ( ; it != files.end(); it++ )
{
FileInfo fi = it->second;
if ( fi.isDirectory() && fi.isReadable() )
{
addWatch( fi.Filepath, watcher, recursive, pWatch );
}
}
}
return wd;
}
void FileWatcherInotify::removeWatchLocked(WatchID watchid)
{
WatchMap::iterator iter = mWatches.find( watchid );
WatcherInotify * watch = iter->second;
if ( watch->Recursive )
{
WatchMap::iterator it = mWatches.begin();
std::list<WatchID> eraseWatches;
for(; it != mWatches.end(); ++it)
{
if ( it->second != watch &&
it->second->inParentTree( watch )
)
{
eraseWatches.push_back( it->second->ID );
}
}
for ( std::list<WatchID>::iterator eit = eraseWatches.begin(); eit != eraseWatches.end(); eit++ )
{
removeWatch( *eit );
}
}
mWatches.erase( iter );
if ( NULL == watch->Parent )
{
WatchMap::iterator eraseit = mRealWatches.find( watch->ID );
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)
{
mWatchesLock.lock();
WatchMap::iterator iter = mWatches.begin();
for(; iter != mWatches.end(); ++iter)
{
if( directory == iter->second->Directory )
{
WatcherInotify * watch = iter->second;
if ( watch->Recursive )
{
WatchMap::iterator it = mWatches.begin();
std::list<WatchID> eraseWatches;
for(; it != mWatches.end(); ++it)
{
if ( it->second->inParentTree( watch ) )
{
eraseWatches.push_back( it->second->ID );
}
}
for ( std::list<WatchID>::iterator eit = eraseWatches.begin(); eit != eraseWatches.end(); eit++ )
{
removeWatchLocked( *eit );
}
}
mWatches.erase( iter );
if ( NULL == watch->Parent )
{
WatchMap::iterator eraseit = mRealWatches.find( watch->ID );
if ( eraseit != mRealWatches.end() )
{
mRealWatches.erase( eraseit );
}
}
int err = inotify_rm_watch(mFD, watch->ID);
if ( err < 0 )
{
efDEBUG( "Error removing watch %d: %s\n", watch->ID, strerror(errno) );
}
else
{
efDEBUG( "Removed watch %s with id: %d\n", watch->Directory.c_str(), watch->ID );
}
efSAFE_DELETE( watch );
break;
}
}
mWatchesLock.unlock();
}
void FileWatcherInotify::removeWatch( WatchID watchid )
{
mWatchesLock.lock();
WatchMap::iterator iter = mWatches.find( watchid );
if( iter == mWatches.end() )
{
mWatchesLock.unlock();
return;
}
removeWatchLocked( watchid );
mWatchesLock.unlock();
}
void FileWatcherInotify::watch()
{
if ( NULL == mThread )
{
mThread = new Thread( &FileWatcherInotify::run, this );
mThread->launch();
}
}
void FileWatcherInotify::run()
{
static char buff[BUFF_SIZE] = {0};
WatchMap::iterator wit;
std::list<WatcherInotify*> movedOutsideWatches;
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, i = 0;
len = read (mFD, buff, BUFF_SIZE);
if (len != -1)
{
while (i < len)
{
struct inotify_event *pevent = (struct inotify_event *)&buff[i];
mWatchesLock.lock();
wit = mWatches.find( pevent->wd );
if ( wit != mWatches.end() )
{
handleAction(wit->second, pevent->name, pevent->mask);
/// Keep track of the IN_MOVED_FROM events to known if the IN_MOVED_TO event is also fired
if ( !wit->second->OldFileName.empty() )
{
movedOutsideWatches.push_back( wit->second );
}
}
mWatchesLock.unlock();
i += sizeof(struct inotify_event) + pevent->len;
}
if ( !movedOutsideWatches.empty() )
{
/// In case that the IN_MOVED_TO is never fired means that the file was moved to other folder
for ( std::list<WatcherInotify*>::iterator it = movedOutsideWatches.begin(); it != movedOutsideWatches.end(); it++ )
{
if ( !(*it)->OldFileName.empty() )
{
/// So we send a IN_DELETE event for files that where moved outside of our scope
handleAction( *it, (*it)->OldFileName, IN_DELETE );
/// Remove the OldFileName
(*it)->OldFileName = "";
}
}
movedOutsideWatches.clear();
}
}
}
} while( mInitOK );
}
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;
/// First check if exists
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
if ( it->second->Directory == fpath )
{
found = true;
break;
}
}
if ( !found )
{
addWatch( fpath, watch->Listener, watch->Recursive, static_cast<WatcherInotify*>( watch ) );
}
}
}
void FileWatcherInotify::handleAction( Watcher* watch, const std::string& filename, unsigned long action, std::string oldFilename )
{
if ( !watch || !watch->Listener )
{
return;
}
std::string fpath( watch->Directory + filename );
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 );
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
if ( it->second->Directory == opath && it->second->DirInfo.Inode == FileInfo( opath ).Inode )
{
it->second->Directory = fpath;
it->second->DirInfo = FileInfo( fpath );
break;
}
}
}
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 )
{
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
if ( it->second->Directory == fpath )
{
removeWatch( it->second->ID );
break;
}
}
}
}
}
std::list<std::string> FileWatcherInotify::directories()
{
std::list<std::string> dirs;
mWatchesLock.lock();
WatchMap::iterator it = mRealWatches.begin();
for ( ; it != mRealWatches.end(); it++ )
{
dirs.push_back( it->second->Directory );
}
mWatchesLock.unlock();
return dirs;
}
bool FileWatcherInotify::pathInWatches( const std::string& path )
{
/// 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;
}
}
#endif

View File

@@ -0,0 +1,73 @@
#ifndef EFSW_FILEWATCHERLINUX_HPP
#define EFSW_FILEWATCHERLINUX_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY
#include <efsw/WatcherInotify.hpp>
#include <map>
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);
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch(const std::string& directory);
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch(WatchID watchid);
/// Updates the watcher. Must be called often.
void watch();
/// Handles the action
void handleAction(Watcher * watch, const std::string& filename, unsigned long action, std::string oldFilename = "");
/// @return Returns a list of the directories that are being watched
std::list<std::string> directories();
protected:
/// Map of WatchID to WatchStruct pointers
WatchMap mWatches;
/// User added watches
WatchMap mRealWatches;
/// inotify file descriptor
int mFD;
Thread * mThread;
Mutex mWatchesLock;
WatchID addWatch(const std::string& directory, FileWatchListener* watcher, bool recursive, WatcherInotify * parent = NULL );
bool pathInWatches( const std::string& path );
private:
void run();
void removeWatchLocked(WatchID watchid);
void checkForNewWatcher( Watcher* watch, std::string fpath );
};
}
#endif
#endif

View File

@@ -0,0 +1,274 @@
#include <efsw/FileWatcherKqueue.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <efsw/FileSystem.hpp>
#include <efsw/System.hpp>
#include <efsw/Debug.hpp>
#include <efsw/WatcherGeneric.hpp>
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;
mThread->wait();
efSAFE_DELETE( mThread );
}
WatchID FileWatcherKqueue::addWatch(const std::string& directory, FileWatchListener* watcher, bool recursive)
{
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 );
mWatchesLock.lock();
mWatches.insert(std::make_pair(mLastWatchID, watch));
mWatchesLock.unlock();
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, this, recursive );
mWatchesLock.lock();
mWatches.insert(std::make_pair(mLastWatchID, watch));
mWatchesLock.unlock();
}
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 );
mWatchesLock.lock();
mWatches.insert(std::make_pair(mLastWatchID, watch));
mWatchesLock.unlock();
}
return mLastWatchID;
}
void FileWatcherKqueue::removeWatch(const std::string& directory)
{
mWatchesLock.lock();
WatchMap::iterator iter = mWatches.begin();
for(; iter != mWatches.end(); ++iter)
{
if(directory == iter->second->Directory)
{
removeWatch(iter->first);
return;
}
}
mWatchesLock.unlock();
}
void FileWatcherKqueue::removeWatch(WatchID watchid)
{
mWatchesLock.lock();
WatchMap::iterator iter = mWatches.find(watchid);
if(iter == mWatches.end())
return;
Watcher* watch = iter->second;
mWatches.erase(iter);
efSAFE_DELETE( watch );
mWatchesLock.unlock();
}
bool FileWatcherKqueue::isAddingWatcher() const
{
return mAddingWatcher;
}
void FileWatcherKqueue::watch()
{
if ( NULL == mThread )
{
mThread = new Thread( &FileWatcherKqueue::run, this );
mThread->launch();
}
}
void FileWatcherKqueue::run()
{
do
{
mWatchesLock.lock();
for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it )
{
it->second->watch();
}
mWatchesLock.unlock();
System::sleep( 500 );
} while( mInitOK );
}
void FileWatcherKqueue::handleAction(Watcher* watch, const std::string& filename, unsigned long action, std::string oldFilename)
{
}
std::list<std::string> FileWatcherKqueue::directories()
{
std::list<std::string> dirs;
mWatchesLock.lock();
WatchMap::iterator it = mWatches.begin();
for ( ; it != mWatches.end(); it++ )
{
dirs.push_back( it->second->Directory );
}
mWatchesLock.unlock();
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;
}
}
#endif

View File

@@ -0,0 +1,78 @@
#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);
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch(const std::string& directory);
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch(WatchID watchid);
/// Updates the watcher. Must be called often.
void watch();
/// Handles the action
void handleAction(Watcher* watch, const std::string& filename, unsigned long action, std::string oldFilename = "");
/// @return Returns a list of the directories that are being watched
std::list<std::string> directories();
protected:
/// Map of WatchID to WatchStruct pointers
WatchMap mWatches;
/// time out data
struct timespec mTimeOut;
/// WatchID allocator
int mLastWatchID;
Thread * mThread;
Mutex mWatchesLock;
std::list<WatchID> mRemoveList;
long mFileDescriptorCount;
bool mAddingWatcher;
bool isAddingWatcher() const;
bool pathInWatches( const std::string& path );
void addFD();
void removeFD();
bool availablesFD();
private:
void run();
};
}
#endif
#endif

View File

@@ -0,0 +1,291 @@
#include <efsw/FileWatcherWin32.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/System.hpp>
#include <efsw/String.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
namespace efsw
{
FileWatcherWin32::FileWatcherWin32( FileWatcher * parent ) :
FileWatcherImpl( parent ),
mLastWatchID(0),
mThread( NULL )
{
mInitOK = true;
}
FileWatcherWin32::~FileWatcherWin32()
{
WatchVector::iterator iter = mWatches.begin();
mWatchesLock.lock();
for(; iter != mWatches.end(); ++iter)
{
DestroyWatch((*iter));
}
mHandles.clear();
mWatches.clear();
mInitOK = false;
mWatchesLock.unlock();
efSAFE_DELETE( mThread );
}
WatchID FileWatcherWin32::addWatch(const std::string& directory, FileWatchListener* watcher, bool recursive)
{
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 );
WatchID watchid = ++mLastWatchID;
mWatchesLock.lock();
WatcherStructWin32 * watch = CreateWatch( String::fromUtf8( dir ).toWideString().c_str(), recursive, FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE
);
if( NULL == watch )
{
return Errors::Log::createLastError( Errors::FileNotFound, dir );
}
if ( pathInWatches( dir ) )
{
return Errors::Log::createLastError( Errors::FileRepeated, 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());
mHandles.push_back( watch->Watch->DirHandle );
mWatches.push_back( watch );
mWatchesLock.unlock();
return watchid;
}
void FileWatcherWin32::removeWatch(const std::string& directory)
{
mWatchesLock.lock();
WatchVector::iterator iter = mWatches.begin();
for(; iter != mWatches.end(); ++iter)
{
if(directory == (*iter)->Watch->DirName)
{
removeWatch((*iter)->Watch->ID);
return;
}
}
mWatchesLock.unlock();
}
void FileWatcherWin32::removeWatch(WatchID watchid)
{
mWatchesLock.lock();
WatchVector::iterator iter = mWatches.begin();
WatcherStructWin32* watch = NULL;
for(; iter != mWatches.end(); ++iter)
{
// Find the watch ID
if ( (*iter)->Watch->ID == watchid )
{
watch = (*iter);
mWatches.erase( iter );
// Remove handle from the handle vector
HandleVector::iterator it = mHandles.begin();
for ( ; it != mHandles.end(); it++ )
{
if ( watch->Watch->DirHandle == (*it) )
{
mHandles.erase( it );
break;
}
}
DestroyWatch(watch);
break;
}
}
mWatchesLock.unlock();
}
void FileWatcherWin32::watch()
{
if ( NULL == mThread )
{
mThread = new Thread( &FileWatcherWin32::run, this );
mThread->launch();
}
}
void FileWatcherWin32::run()
{
if ( mHandles.empty() )
{
return;
}
do
{
if ( !mHandles.empty() )
{
mWatchesLock.lock();
for ( std::size_t i = 0; i < mWatches.size(); i++ )
{
WatcherStructWin32 * watch = mWatches[ i ];
// If the overlapped struct was cancelled ( because the creator thread doesn't exists anymore ),
// we recreate the overlapped in the current thread and refresh the watch
if ( /*STATUS_CANCELED*/0xC0000120 == watch->Overlapped.Internal )
{
watch->Overlapped = OVERLAPPED();
RefreshWatch(watch);
}
// First ensure that the handle is the same, this means that the watch was not removed.
if ( HasOverlappedIoCompleted( &watch->Overlapped ) && mHandles[ i ] == watch->Watch->DirHandle )
{
DWORD bytes;
if ( GetOverlappedResult( watch->Watch->DirHandle, &watch->Overlapped, &bytes, FALSE ) )
{
WatchCallback( ERROR_SUCCESS, bytes, &watch->Overlapped );
}
}
}
mWatchesLock.unlock();
if ( mInitOK )
{
System::sleep( 10 );
}
}
else
{
// Wait for a new handle to be added
System::sleep( 10 );
}
} while ( mInitOK );
}
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 ( WatchVector::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
if ( (*it)->Watch->Directory == opath )
{
(*it)->Watch->Directory = fpath;
break;
}
}
}
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;
};
watch->Listener->handleFileAction(watch->ID, static_cast<WatcherWin32*>( watch )->DirName, filename, fwAction);
}
std::list<std::string> FileWatcherWin32::directories()
{
std::list<std::string> dirs;
mWatchesLock.lock();
for ( WatchVector::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
dirs.push_back( std::string( (*it)->Watch->DirName ) );
}
mWatchesLock.unlock();
return dirs;
}
bool FileWatcherWin32::pathInWatches( const std::string& path )
{
for ( WatchVector::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
if ( (*it)->Watch->DirName == path )
{
return true;
}
}
return false;
}
}
#endif

View File

@@ -0,0 +1,69 @@
#ifndef EFSW_FILEWATCHERWIN32_HPP
#define EFSW_FILEWATCHERWIN32_HPP
#include <efsw/base.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <efsw/WatcherWin32.hpp>
#include <vector>
#include <map>
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::vector<WatcherStructWin32*> WatchVector;
typedef std::vector<HANDLE> HandleVector;
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);
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch(const std::string& directory);
/// Remove a directory watch. This is a map lookup O(logn).
void removeWatch(WatchID watchid);
/// Updates the watcher. Must be called often.
void watch();
/// Handles the action
void handleAction(Watcher* watch, const std::string& filename, unsigned long action, std::string oldFilename = "");
/// @return Returns a list of the directories that are being watched
std::list<std::string> directories();
protected:
/// Vector of WatcherWin32 pointers
WatchVector mWatches;
/// Keeps an updated handles vector
HandleVector mHandles;
/// The last watchid
WatchID mLastWatchID;
Thread * mThread;
Mutex mWatchesLock;
bool pathInWatches( const std::string& path );
private:
void run();
};
}
#endif
#endif

27
dep/efsw/src/efsw/Log.cpp Normal file
View File

@@ -0,0 +1,27 @@
#include <efsw/efsw.hpp>
namespace efsw { namespace Errors {
static std::string LastError;
std::string Log::getLastErrorLog()
{
return LastError;
}
Error Log::createLastError( Error err, std::string log )
{
switch ( err )
{
case FileNotFound: LastError = "File not found ( " + log + " )"; break;
case FileRepeated: LastError = "File reapeated 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 Unspecified:
default: LastError = log;
}
return err;
}
}}

View File

@@ -0,0 +1,26 @@
#include <efsw/Mutex.hpp>
#include <efsw/platform/platformimpl.hpp>
namespace efsw {
Mutex::Mutex() :
mMutexImpl( new Platform::MutexImpl() )
{
}
Mutex::~Mutex()
{
efSAFE_DELETE( mMutexImpl );
}
void Mutex::lock()
{
mMutexImpl->lock();
}
void Mutex::unlock()
{
mMutexImpl->unlock();
}
}

View File

@@ -0,0 +1,28 @@
#ifndef EFSW_MUTEX_HPP
#define EFSW_MUTEX_HPP
#include <efsw/base.hpp>
namespace efsw {
namespace Platform { class MutexImpl; }
/** Simple mutex class */
class Mutex {
public:
Mutex();
~Mutex();
/** Lock the mutex */
void lock();
/** Unlock the mutex */
void unlock();
private:
Platform::MutexImpl * mMutexImpl;
};
}
#endif

View File

@@ -0,0 +1,813 @@
#include <iterator>
#include <efsw/String.hpp>
#include <efsw/Utf.hpp>
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;
}
}

View File

@@ -0,0 +1,629 @@
/** 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 <efsw/base.hpp>
#include <locale>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
namespace efsw {
/** @brief Utility string class that automatically handles conversions between types and encodings **/
class String
{
public :
typedef Uint32 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 );
}
#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.
**/

View File

@@ -0,0 +1,26 @@
#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();
}
}

View File

@@ -0,0 +1,26 @@
#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();
};
}
#endif

View File

@@ -0,0 +1,51 @@
#include <efsw/Thread.hpp>
#include <efsw/platform/platformimpl.hpp>
namespace efsw {
Thread::Thread() :
mThreadImpl(NULL),
mEntryPoint(NULL)
{
}
Thread::~Thread()
{
wait();
efSAFE_DELETE( mEntryPoint );
}
void Thread::launch()
{
wait();
mThreadImpl = new Platform::ThreadImpl( this );
}
void Thread::wait()
{
if ( mThreadImpl )
{
mThreadImpl->wait();
efSAFE_DELETE( mThreadImpl );
}
}
void Thread::terminate()
{
if ( mThreadImpl )
{
mThreadImpl->terminate();
efSAFE_DELETE( mThreadImpl );
}
}
void Thread::run()
{
mEntryPoint->run();
}
}

View File

@@ -0,0 +1,111 @@
#ifndef EFSW_THREAD_HPP
#define EFSW_THREAD_HPP
#include <efsw/base.hpp>
namespace efsw {
namespace Platform { class ThreadImpl; }
namespace Private { struct ThreadFunc; }
/** @brief Thread manager class */
class Thread {
public:
typedef void (*FuncType)(void*);
template <typename F>
Thread( F function );
template <typename F, typename A>
Thread( F function, A argument );
template <typename C>
Thread( void(C::*function)(), C* object );
virtual ~Thread();
/** Launch the thread */
virtual void launch();
/** Wait the thread until end */
void wait();
/** Terminate the thread */
void terminate();
protected:
Thread();
private:
friend class Platform::ThreadImpl;
/** The virtual function to run in the thread */
virtual void run();
Platform::ThreadImpl * mThreadImpl; ///< OS-specific implementation of the thread
Private::ThreadFunc * mEntryPoint; ///< Abstraction of the function to run
};
//! NOTE: Taken from SFML2 threads
namespace Private {
// Base class for abstract thread functions
struct ThreadFunc
{
virtual ~ThreadFunc() {}
virtual void run() = 0;
};
// Specialization using a functor (including free functions) with no argument
template <typename T>
struct ThreadFunctor : ThreadFunc
{
ThreadFunctor(T functor) : m_functor(functor) {}
virtual void run() {m_functor();}
T m_functor;
};
// Specialization using a functor (including free functions) with one argument
template <typename F, typename A>
struct ThreadFunctorWithArg : ThreadFunc
{
ThreadFunctorWithArg(F function, A arg) : m_function(function), m_arg(arg) {}
virtual void run() {m_function(m_arg);}
F m_function;
A m_arg;
};
// Specialization using a member function
template <typename C>
struct ThreadMemberFunc : ThreadFunc
{
ThreadMemberFunc(void(C::*function)(), C* object) : m_function(function), m_object(object) {}
virtual void run() {(m_object->*m_function)();}
void(C::*m_function)();
C* m_object;
};
}
template <typename F>
Thread::Thread(F functor) :
mThreadImpl (NULL),
mEntryPoint( new Private::ThreadFunctor<F>(functor) )
{
}
template <typename F, typename A>
Thread::Thread(F function, A argument) :
mThreadImpl(NULL),
mEntryPoint( new Private::ThreadFunctorWithArg<F efCOMMA A>(function, argument) )
{
}
template <typename C>
Thread::Thread(void(C::*function)(), C* object) :
mThreadImpl(NULL),
mEntryPoint( new Private::ThreadMemberFunc<C>(function, object) )
{
}
}
#endif

748
dep/efsw/src/efsw/Utf.hpp Normal file
View File

@@ -0,0 +1,748 @@
/** 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 <locale>
#include <string>
#include <cstdlib>
#include <efsw/base.hpp>
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;
}
#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)
///
////////////////////////////////////////////////////////////

671
dep/efsw/src/efsw/Utf.inl Normal file
View File

@@ -0,0 +1,671 @@
// 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

View File

@@ -0,0 +1,21 @@
#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 )
{
}
}

View File

@@ -0,0 +1,30 @@
#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;
};
}
#endif

View File

@@ -0,0 +1,264 @@
#include <efsw/WatcherFSEvents.hpp>
#include <efsw/FileWatcherFSEvents.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/Debug.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
namespace efsw {
WatcherFSEvents::WatcherFSEvents() :
Watcher(),
FWatcher( NULL ),
FSStream( NULL ),
WatcherGen( NULL ),
initializedAsync( false )
{
}
WatcherFSEvents::WatcherFSEvents( WatchID id, std::string directory, FileWatchListener * listener, bool recursive, WatcherFSEvents * parent ) :
Watcher( id, directory, listener, recursive ),
FWatcher( NULL ),
FSStream( NULL ),
WatcherGen( NULL ),
initializedAsync( false )
{
}
WatcherFSEvents::~WatcherFSEvents()
{
if ( initializedAsync )
{
FSEventStreamStop( FSStream );
FSEventStreamInvalidate( FSStream );
}
if ( NULL != 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;
}
else
{
WatcherGen = new WatcherGeneric( ID, Directory, Listener, FWatcher, Recursive );
}
FSEventStreamContext ctx;
/* Initialize context */
ctx.version = 0;
ctx.info = this;
ctx.retain = NULL;
ctx.release = NULL;
ctx.copyDescription = NULL;
FSStream = FSEventStreamCreate( kCFAllocatorDefault, &FileWatcherFSEvents::FSEventCallback, &ctx, CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0.25, streamFlags );
FWatcher->mNeedInit.push_back( this );
CFRelease( CFDirectoryArray );
CFRelease( CFDirectory );
}
void WatcherFSEvents::initAsync()
{
FSEventStreamScheduleWithRunLoop( FSStream, FWatcher->mRunLoopRef, kCFRunLoopDefaultMode );
FSEventStreamStart( FSStream );
initializedAsync = true;
}
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, oldFilename );
}
void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path, std::string& dirPath, std::string& filePath )
{
if ( flags & efswFSEventStreamEventFlagItemCreated )
{
if ( FileInfo::exists( path ) )
{
sendFileAction( ID, dirPath, filePath, Actions::Add );
}
}
if ( flags & efswFSEventsModified )
{
sendFileAction( ID, dirPath, filePath, Actions::Modified );
}
if ( flags & efswFSEventStreamEventFlagItemRemoved )
{
// Since i don't know the order, at least i try to keep the data consistent with the real state
if ( !FileInfo::exists( path ) )
{
sendFileAction( ID, dirPath, filePath, Actions::Delete );
}
}
}
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 |
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: %ld\n", path.c_str(), event.Flags );
if ( event.Flags & efswFSEventStreamEventFlagItemRenamed )
{
if ( ( i + 1 < esize ) &&
( events[ i + 1 ].Flags & efswFSEventStreamEventFlagItemRenamed ) &&
( events[ i + 1 ].Id == event.Id + 1 )
)
{
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 ) )
{
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 & efswFSEventsModified )
{
sendFileAction( ID, newDir, newFilepath, Actions::Modified );
}
}
}
else
{
handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath );
}
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 & efswFSEventsModified )
{
sendFileAction( ID, dirPath, filePath, Actions::Modified );
}
}
else
{
sendFileAction( ID, dirPath, filePath, Actions::Delete );
}
}
else
{
handleAddModDel( event.Flags, event.Path, dirPath, filePath );
}
}
else
{
efDEBUG( "Directory: %s changed\n", event.Path.c_str() );
DirsChanged.insert( event.Path );
}
}
}
void WatcherFSEvents::process()
{
std::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();
}
}
#endif

View File

@@ -0,0 +1,70 @@
#ifndef EFSW_WATCHERINOTIFY_HPP
#define EFSW_WATCHERINOTIFY_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <efsw/WatcherGeneric.hpp>
#include <efsw/FileInfo.hpp>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <set>
#include <vector>
namespace efsw {
class FileWatcherFSEvents;
class FSEvent
{
public:
FSEvent( std::string path, long flags, Uint64 id ) :
Path( path ),
Flags( flags ),
Id ( id )
{
}
std::string Path;
long Flags;
Uint64 Id;
};
class WatcherFSEvents : public Watcher
{
public:
WatcherFSEvents();
WatcherFSEvents( WatchID id, std::string directory, FileWatchListener * listener, bool recursive, WatcherFSEvents * parent = NULL );
~WatcherFSEvents();
void init();
void initAsync();
void handleActions( std::vector<FSEvent> & events );
void process();
FileWatcherFSEvents * FWatcher;
FSEventStreamRef FSStream;
protected:
void handleAddModDel( const Uint32 &flags, const std::string &path, std::string &dirPath, std::string &filePath );
WatcherGeneric * WatcherGen;
bool initializedAsync;
std::set<std::string> DirsChanged;
void sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename, Action action, std::string oldFilename = "" );
};
}
#endif
#endif

View File

@@ -0,0 +1,40 @@
#include <efsw/WatcherGeneric.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/DirWatcherGeneric.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 );
}
}

View File

@@ -0,0 +1,30 @@
#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();
void watchDir( std::string dir );
bool pathInWatches( std::string path );
};
}
#endif

View File

@@ -0,0 +1,35 @@
#include <efsw/WatcherInotify.hpp>
namespace efsw {
WatcherInotify::WatcherInotify() :
Watcher(),
Parent( NULL )
{
}
WatcherInotify::WatcherInotify( WatchID id, std::string directory, FileWatchListener * listener, bool recursive, WatcherInotify * parent ) :
Watcher( id, directory, listener, recursive ),
Parent( parent ),
DirInfo( directory )
{
}
bool WatcherInotify::inParentTree( WatcherInotify * parent )
{
WatcherInotify * tNext = Parent;
while ( NULL != tNext )
{
if ( tNext == parent )
{
return true;
}
tNext = tNext->Parent;
}
return false;
}
}

View File

@@ -0,0 +1,25 @@
#ifndef EFSW_WATCHERINOTIFY_HPP
#define EFSW_WATCHERINOTIFY_HPP
#include <efsw/FileWatcherImpl.hpp>
#include <efsw/FileInfo.hpp>
namespace efsw {
class WatcherInotify : public Watcher
{
public:
WatcherInotify();
WatcherInotify( WatchID id, std::string directory, FileWatchListener * listener, bool recursive, WatcherInotify * parent = NULL );
bool inParentTree( WatcherInotify * parent );
WatcherInotify * Parent;
FileInfo DirInfo;
};
}
#endif

View File

@@ -0,0 +1,667 @@
#include <efsw/WatcherKqueue.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <efsw/Debug.hpp>
#include <efsw/String.hpp>
#include <efsw/System.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/WatcherGeneric.hpp>
#include <efsw/FileWatcherKqueue.hpp>
#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::list<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::list<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( ( nev = kevent( mKqueue, &mChangeList[0], 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 long s_fc = 0;
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();
s_fc++;
// 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. Folders added: %ld\n", mWatcher->mFileDescriptorCount, s_fc );
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;
}
}
#endif

View File

@@ -0,0 +1,94 @@
#ifndef EFSW_WATCHEROSX_HPP
#define EFSW_WATCHEROSX_HPP
#include <efsw/FileWatcherImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
#include <map>
#include <vector>
#include <sys/types.h>
#include <sys/event.h>
#include <efsw/DirectorySnapshot.hpp>
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();
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();
};
}
#endif
#endif

View File

@@ -0,0 +1,150 @@
#include <efsw/WatcherWin32.hpp>
#include <efsw/String.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
namespace efsw
{
/// Unpacks events and passes them to a user defined callback.
void CALLBACK WatchCallback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
char szFile[MAX_PATH];
PFILE_NOTIFY_INFORMATION pNotify;
WatcherStructWin32 * tWatch = (WatcherStructWin32*) lpOverlapped;
WatcherWin32 * pWatch = tWatch->Watch;
size_t offset = 0;
if (dwNumberOfBytesTransfered == 0)
{
RefreshWatch(tWatch); // If dwNumberOfBytesTransfered == 0, it means the buffer overflowed (too many changes between GetOverlappedResults calls). Those events are lost, but at least we can refresh so subsequent changes are seen again.
return;
}
if (dwErrorCode == ERROR_SUCCESS)
{
do
{
bool skip = false;
pNotify = (PFILE_NOTIFY_INFORMATION) &pWatch->mBuffer[offset];
offset += pNotify->NextEntryOffset;
int count = WideCharToMultiByte(CP_UTF8, 0, pNotify->FileName,
pNotify->FileNameLength / sizeof(WCHAR),
szFile, MAX_PATH - 1, NULL, NULL);
szFile[count] = TEXT('\0');
std::string nfile( szFile );
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);
}
if (!pWatch->StopNow)
{
RefreshWatch(tWatch);
}
}
/// Refreshes the directory monitoring.
bool RefreshWatch(WatcherStructWin32* pWatch)
{
return ReadDirectoryChangesW(
pWatch->Watch->DirHandle,
pWatch->Watch->mBuffer,
sizeof(pWatch->Watch->mBuffer),
pWatch->Watch->Recursive,
pWatch->Watch->NotifyFilter,
NULL,
&pWatch->Overlapped,
NULL
) != 0;
}
/// Stops monitoring a directory.
void DestroyWatch(WatcherStructWin32* pWatch)
{
if (pWatch)
{
WatcherWin32 * tWatch = pWatch->Watch;
tWatch->StopNow = true;
CancelIo(tWatch->DirHandle);
RefreshWatch(pWatch);
if (!HasOverlappedIoCompleted(&pWatch->Overlapped))
{
SleepEx(5, TRUE);
}
CloseHandle(pWatch->Overlapped.hEvent);
CloseHandle(pWatch->Watch->DirHandle);
efSAFE_DELETE_ARRAY( pWatch->Watch->DirName );
efSAFE_DELETE( pWatch->Watch );
HeapFree(GetProcessHeap(), 0, pWatch);
}
}
/// Starts monitoring a directory.
WatcherStructWin32* CreateWatch(LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter)
{
WatcherStructWin32 * tWatch;
size_t ptrsize = sizeof(*tWatch);
tWatch = static_cast<WatcherStructWin32*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize));
WatcherWin32 * pWatch = new WatcherWin32();
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)
{
tWatch->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
pWatch->NotifyFilter = NotifyFilter;
pWatch->Recursive = recursive;
if (RefreshWatch(tWatch))
{
return tWatch;
}
else
{
CloseHandle(tWatch->Overlapped.hEvent);
CloseHandle(pWatch->DirHandle);
}
}
HeapFree(GetProcessHeap(), 0, tWatch);
return NULL;
}
}
#endif

View File

@@ -0,0 +1,77 @@
#ifndef EFSW_WATCHERWIN32_HPP
#define EFSW_WATCHERWIN32_HPP
#include <efsw/FileWatcherImpl.hpp>
#include <efsw/FileInfo.hpp>
#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;
/// Internal watch data
struct WatcherStructWin32
{
OVERLAPPED Overlapped;
WatcherWin32 * Watch;
};
class cLastModifiedEvent
{
public:
cLastModifiedEvent() {}
FileInfo file;
std::string fileName;
};
bool RefreshWatch(WatcherStructWin32* pWatch);
void CALLBACK WatchCallback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped);
void DestroyWatch(WatcherStructWin32* pWatch);
WatcherStructWin32* CreateWatch(LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter);
class WatcherWin32 : public Watcher
{
public:
WatcherWin32() :
Struct( NULL ),
DirHandle( NULL ),
lParam( 0 ),
NotifyFilter( 0 ),
StopNow( false ),
Watch( NULL ),
DirName( NULL )
{
}
WatcherStructWin32 * Struct;
HANDLE DirHandle;
BYTE mBuffer[63 * 1024]; // do NOT make this bigger than 64K because it will fail if the folder being watched is on the network! (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
LPARAM lParam;
DWORD NotifyFilter;
bool StopNow;
FileWatcherImpl* Watch;
char* DirName;
cLastModifiedEvent LastModifiedEvent;
};
}
#endif
#endif

110
dep/efsw/src/efsw/base.hpp Normal file
View File

@@ -0,0 +1,110 @@
#ifndef EFSW_BASE
#define EFSW_BASE
#include <efsw/sophist.h>
#include <efsw/efsw.hpp>
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
#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]) )
}
#endif

View File

@@ -0,0 +1,159 @@
#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 (__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,20 @@
#ifndef EFSW_PLATFORMIMPL_HPP
#define EFSW_PLATFORMIMPL_HPP
#include <efsw/base.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <efsw/platform/posix/ThreadImpl.hpp>
#include <efsw/platform/posix/MutexImpl.hpp>
#include <efsw/platform/posix/SystemImpl.hpp>
#include <efsw/platform/posix/FileSystemImpl.hpp>
#elif EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <efsw/platform/win/ThreadImpl.hpp>
#include <efsw/platform/win/MutexImpl.hpp>
#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,144 @@
#include <efsw/platform/posix/FileSystemImpl.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <efsw/FileInfo.hpp>
#include <efsw/FileSystem.hpp>
#include <dirent.h>
#include <cstring>
#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 <cstdlib>
#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/param.h>
#include <sys/mount.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
namespace efsw { namespace Platform {
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_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_FUSEBLK: /* 0x65735546 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;
}
}}
#endif

View File

@@ -0,0 +1,27 @@
#ifndef EFSW_FILESYSTEMIMPLPOSIX_HPP
#define EFSW_FILESYSTEMIMPLPOSIX_HPP
#include <efsw/base.hpp>
#include <efsw/FileInfo.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 );
};
}}
#endif
#endif

View File

@@ -0,0 +1,32 @@
#include <efsw/platform/posix/MutexImpl.hpp>
#if defined( EFSW_PLATFORM_POSIX )
namespace efsw { namespace Platform {
MutexImpl::MutexImpl()
{
pthread_mutexattr_t attributes;
pthread_mutexattr_init(&attributes);
pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mMutex, &attributes);
}
MutexImpl::~MutexImpl()
{
pthread_mutex_destroy(&mMutex);
}
void MutexImpl::lock()
{
pthread_mutex_lock(&mMutex);
}
void MutexImpl::unlock()
{
pthread_mutex_unlock(&mMutex);
}
}}
#endif

View File

@@ -0,0 +1,31 @@
#ifndef EFSW_MUTEXIMPLPOSIX_HPP
#define EFSW_MUTEXIMPLPOSIX_HPP
#include <efsw/base.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <pthread.h>
namespace efsw { namespace Platform {
class MutexImpl
{
public:
MutexImpl();
~MutexImpl();
void lock();
void unlock();
private:
pthread_mutex_t mMutex;
};
}}
#endif
#endif

View File

@@ -0,0 +1,180 @@
#include <efsw/platform/posix/SystemImpl.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <cstdio>
#include <pthread.h>
#include <sys/time.h>
#include <limits.h>
#include <sys/resource.h>
#include <efsw/FileSystem.hpp>
#include <efsw/Debug.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;
}
}}
#endif

View File

@@ -0,0 +1,26 @@
#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();
};
}}
#endif
#endif

View File

@@ -0,0 +1,68 @@
#include <efsw/platform/posix/ThreadImpl.hpp>
#include <efsw/Thread.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <cassert>
#include <iostream>
#include <efsw/Debug.hpp>
namespace efsw { namespace Platform {
ThreadImpl::ThreadImpl( Thread * owner ) :
mIsActive(false)
{
mIsActive = pthread_create( &mThread, NULL, &ThreadImpl::entryPoint, owner ) == 0;
if ( !mIsActive )
{
efDEBUG( "Failed to create thread\n" );
}
}
void ThreadImpl::wait()
{
// Wait for the thread to finish, no timeout
if ( mIsActive )
{
assert( pthread_equal( pthread_self(), mThread ) == 0 );
pthread_join( mThread, NULL );
mIsActive = false; // Reset the thread state
}
}
void ThreadImpl::terminate()
{
if ( mIsActive )
{
#if !defined( __ANDROID__ ) && !defined( ANDROID )
pthread_cancel( mThread );
#else
pthread_kill( mThread , SIGUSR1 );
#endif
mIsActive = false;
}
}
void * ThreadImpl::entryPoint( void * userData )
{
// The Thread instance is stored in the user data
Thread * owner = static_cast<Thread*>( userData );
// Tell the thread to handle cancel requests immediatly
#ifdef PTHREAD_CANCEL_ASYNCHRONOUS
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
#endif
// Forward to the owner
owner->run();
return NULL;
}
}}
#endif

View File

@@ -0,0 +1,35 @@
#ifndef EFSW_THREADIMPLPOSIX_HPP
#define EFSW_THREADIMPLPOSIX_HPP
#include <efsw/base.hpp>
#if defined( EFSW_PLATFORM_POSIX )
#include <pthread.h>
namespace efsw {
class Thread;
namespace Platform {
class ThreadImpl
{
public:
ThreadImpl( Thread * owner );
void wait();
void terminate();
protected:
static void * entryPoint( void* userData );
pthread_t mThread;
bool mIsActive;
};
}}
#endif
#endif

View File

@@ -0,0 +1,89 @@
#include <efsw/platform/win/FileSystemImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifndef EFSW_COMPILER_MSVC
#include <dirent.h>
#endif
namespace efsw { namespace Platform {
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 )
{
return 0 != ( GetFileAttributesW( String( path ).toWideString().c_str() ) & FILE_ATTRIBUTE_DIRECTORY );
}
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;
}
}}
#endif

View File

@@ -0,0 +1,28 @@
#ifndef EFSW_FILESYSTEMIMPLWIN_HPP
#define EFSW_FILESYSTEMIMPLWIN_HPP
#include <efsw/base.hpp>
#include <efsw/String.hpp>
#include <efsw/FileInfo.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 );
};
}}
#endif
#endif

View File

@@ -0,0 +1,29 @@
#include <efsw/platform/win/MutexImpl.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
namespace efsw { namespace Platform {
MutexImpl::MutexImpl()
{
InitializeCriticalSection(&mMutex);
}
MutexImpl::~MutexImpl()
{
DeleteCriticalSection(&mMutex);
}
void MutexImpl::lock()
{
EnterCriticalSection(&mMutex);
}
void MutexImpl::unlock()
{
LeaveCriticalSection(&mMutex);
}
}}
#endif

View File

@@ -0,0 +1,34 @@
#ifndef EFSW_MUTEXIMPLWIN_HPP
#define EFSW_MUTEXIMPLWIN_HPP
#include <efsw/base.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
namespace efsw { namespace Platform {
class MutexImpl
{
public:
MutexImpl();
~MutexImpl();
void lock();
void unlock();
private:
CRITICAL_SECTION mMutex;
};
}}
#endif
#endif

View File

@@ -0,0 +1,50 @@
#include <efsw/platform/win/SystemImpl.hpp>
#include <efsw/String.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <cstdlib>
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;
}
}}
#endif

View File

@@ -0,0 +1,26 @@
#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();
};
}}
#endif
#endif

View File

@@ -0,0 +1,56 @@
#include <efsw/platform/win/ThreadImpl.hpp>
#include <efsw/Thread.hpp>
#include <assert.h>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#include <efsw/Debug.hpp>
namespace efsw { namespace Platform {
ThreadImpl::ThreadImpl( Thread *owner )
{
mThread = reinterpret_cast<HANDLE>( _beginthreadex( NULL, 0, &ThreadImpl::entryPoint, owner, 0, &mThreadId ) );
if ( !mThread )
{
efDEBUG( "Failed to create thread\n" );
}
}
void ThreadImpl::wait()
{
// Wait for the thread to finish, no timeout
if ( mThread )
{
assert( mThreadId != GetCurrentThreadId() ); // A thread cannot wait for itself!
WaitForSingleObject( mThread, INFINITE );
}
}
void ThreadImpl::terminate()
{
if ( mThread )
{
TerminateThread( mThread, 0 );
}
}
unsigned int __stdcall ThreadImpl::entryPoint( void * userData )
{
// The Thread instance is stored in the user data
Thread * owner = static_cast<Thread*>( userData );
// Forward to the owner
owner->run();
// Optional, but it is cleaner
_endthreadex(0);
return 0;
}
}}
#endif

View File

@@ -0,0 +1,39 @@
#ifndef EFSW_THREADIMPLWIN_HPP
#define EFSW_THREADIMPLWIN_HPP
#include <efsw/base.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <process.h>
namespace efsw {
class Thread;
namespace Platform {
class ThreadImpl
{
public:
ThreadImpl( Thread * owner );
void wait();
void terminate();
protected:
static unsigned int __stdcall entryPoint(void* userData);
HANDLE mThread;
unsigned int mThreadId;
};
}}
#endif
#endif

147
dep/efsw/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__ */

View File

@@ -0,0 +1,151 @@
#include <efsw/efsw.hpp>
#include <efsw/System.hpp>
#include <efsw/FileSystem.hpp>
#include <signal.h>
#include <iostream>
bool STOP = false;
void sigend(int signal)
{
std::cout << std::endl << "Bye bye" << std::endl;
STOP = true;
}
/// Processes a file action
class UpdateListener : public efsw::FileWatchListener
{
public:
UpdateListener() {}
std::string getActionName( efsw::Action action )
{
switch ( action )
{
case efsw::Actions::Add: return "Add";
case efsw::Actions::Modified: return "Modified";
case efsw::Actions::Delete: return "Delete";
case efsw::Actions::Moved: return "Moved";
default: return "Bad Action";
}
}
void handleFileAction( efsw::WatchID watchid, const std::string& dir, const std::string& filename, efsw::Action action, std::string oldFilename = "" )
{
std::cout << "DIR (" << dir + ") FILE (" + ( oldFilename.empty() ? "" : "from file " + oldFilename + " to " ) + filename + ") has event " << getActionName( action ) << std::endl;
}
};
efsw::WatchID handleWatchID( efsw::WatchID watchid )
{
switch ( watchid )
{
case efsw::Errors::FileNotFound:
case efsw::Errors::FileRepeated:
case efsw::Errors::FileOutOfScope:
case efsw::Errors::FileRemote:
case efsw::Errors::Unspecified:
{
std::cout << efsw::Errors::Log::getLastErrorLog().c_str() << std::endl;
break;
}
default:
{
std::cout << "Added WatchID: " << watchid << std::endl;
}
}
return watchid;
}
int main(int argc, char **argv)
{
signal( SIGABRT , sigend );
signal( SIGINT , sigend );
signal( SIGTERM , sigend );
std::cout << "Press ^C to exit demo" << std::endl;
bool commonTest = true;
bool useGeneric = false;
std::string path;
if ( argc >= 2 )
{
path = std::string( argv[1] );
if ( efsw::FileSystem::isDirectory( path ) )
{
commonTest = false;
}
if ( argc >= 3 )
{
if ( std::string( argv[2] ) == "true" )
{
useGeneric = true;
}
}
}
UpdateListener * ul = new UpdateListener();
/// create the file watcher object
efsw::FileWatcher fileWatcher( useGeneric );
fileWatcher.followSymlinks( false );
fileWatcher.allowOutOfScopeLinks( false );
if ( commonTest )
{
std::string CurPath( efsw::System::getProcessPath() );
std::cout << "CurPath: " << CurPath.c_str() << std::endl;
/// add a watch to the system
handleWatchID( fileWatcher.addWatch( CurPath + "test", ul, true ) );
/// starts watching
fileWatcher.watch();
/// adds another watch after started watching...
efsw::System::sleep( 100 );
efsw::WatchID watchID = handleWatchID( fileWatcher.addWatch( CurPath + "test2", ul, true ) );
/// delete the watch
if ( watchID > 0 )
{
efsw::System::sleep( 1000 );
fileWatcher.removeWatch( watchID );
}
}
else
{
efsw::WatchID err;
if ( ( err = fileWatcher.addWatch( path, ul, true ) ) > 0 )
{
fileWatcher.watch();
std::cout << "Watching directory: " << path.c_str() << std::endl;
if ( useGeneric )
{
std::cout << "Using generic backend watcher" << std::endl;
}
}
else
{
std::cout << "Error trying to watch directory: " << path.c_str() << std::endl;
std::cout << efsw::Errors::Log::getLastErrorLog().c_str() << std::endl;
}
}
while( !STOP )
{
efsw::System::sleep( 100 );
}
return 0;
}

View File

@@ -43,7 +43,9 @@ target_include_directories(game
target_link_libraries(game
PUBLIC
shared
Detour)
Detour
PRIVATE
efsw)
set_target_properties(game
PROPERTIES