diff options
Diffstat (limited to 'dep')
101 files changed, 11526 insertions, 465 deletions
| diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt index 7f4f1cd884c..48be56bc9ef 100644 --- a/dep/CMakeLists.txt +++ b/dep/CMakeLists.txt @@ -8,37 +8,38 @@  # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -if( MSVC ) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +  string(REGEX REPLACE "/W[0-4] " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +  string(REGEX REPLACE "/W[0-4] " "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")    add_definitions(/W0)  else()    add_definitions(-w)  endif() -if(CMAKE_SYSTEM_NAME MATCHES "Linux") -  if(SERVERS AND NOT NOJEM) -    add_subdirectory(jemalloc)   -  endif() -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Windows") -  if(TOOLS) -    add_subdirectory(bzip2) -  endif() -  if(SERVERS OR TOOLS) -    add_subdirectory(zlib) -  endif() -endif() +add_subdirectory(threads)  if(SERVERS OR TOOLS) +  add_subdirectory(boost) +  add_subdirectory(process) +  add_subdirectory(zlib)    add_subdirectory(g3dlite)    add_subdirectory(recastnavigation)    add_subdirectory(cppformat) +  add_subdirectory(SFMT) +  add_subdirectory(utf8cpp) +  add_subdirectory(valgrind) +  add_subdirectory(openssl) +  add_subdirectory(jemalloc)  endif()  if(SERVERS) +  add_subdirectory(mysql) +  add_subdirectory(readline)    add_subdirectory(gsoap) +  add_subdirectory(efsw)  endif()  if(TOOLS) +  add_subdirectory(bzip2)    add_subdirectory(libmpq)  endif() diff --git a/dep/PackageList.txt b/dep/PackageList.txt index e84fef8d3b2..66421127162 100644 --- a/dep/PackageList.txt +++ b/dep/PackageList.txt @@ -1,6 +1,6 @@  TrinityCore uses (parts of or in whole) the following opensource software : -Boost +Boost (external)    http://www.boost.org    Version: 1.55 @@ -14,7 +14,11 @@ bzip2 (a freely available, patent free, high-quality data compressor)  cppformat (type safe format library)    https://github.com/cppformat/cppformat -  Version: 5c76d107cbaf5e851bd66b6c563e4fc7c90be7ad +  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/ @@ -23,11 +27,19 @@ G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License)  jemalloc (a general-purpose scalable concurrent malloc-implementation)    http://www.canonware.com/jemalloc/    Version: 3.6.0 -   +  libMPQ (a library for reading MPQ files)    https://github.com/mbroemme/libmpq/    Version: d59b4cf1d107b5f6a0f67d6bc545c6c6ebef3d74 +libreadline (command line editing library) +  https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html +  Version: external + +OpenSSL (general-purpose cryptography library) +  https://www.openssl.org/ +  Version: external +  SFMT (SIMD-oriented Fast Mersenne Twister)    Based on http://agner.org/random/    Version: 2010-Aug-03 diff --git a/dep/SFMT/CMakeLists.txt b/dep/SFMT/CMakeLists.txt new file mode 100644 index 00000000000..5cf1b9bf972 --- /dev/null +++ b/dep/SFMT/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +add_library(sfmt INTERFACE) + +target_include_directories(sfmt +  INTERFACE +    ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/dep/boost/CMakeLists.txt b/dep/boost/CMakeLists.txt new file mode 100644 index 00000000000..6cda5fbec4e --- /dev/null +++ b/dep/boost/CMakeLists.txt @@ -0,0 +1,70 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +if(WIN32) + set(BOOST_DEBUG ON) +  if(DEFINED ENV{BOOST_ROOT}) +    set(BOOST_ROOT $ENV{BOOST_ROOT}) +    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0) +      set(BOOST_LIBRARYDIR ${BOOST_ROOT}/lib${PLATFORM}-msvc-12.0) +    else() +      set(BOOST_LIBRARYDIR ${BOOST_ROOT}/lib${PLATFORM}-msvc-14.0) +    endif() +  else() +    message(FATAL_ERROR "No BOOST_ROOT environment variable could be found! Please make sure it is set and the points to your Boost installation.") +  endif() + +  set(Boost_USE_STATIC_LIBS        ON) +  set(Boost_USE_MULTITHREADED      ON) +  set(Boost_USE_STATIC_RUNTIME     OFF) +endif() + +find_package(Boost 1.51 REQUIRED system filesystem thread program_options iostreams regex) + +# Find if Boost was compiled in C++03 mode because it requires -DBOOST_NO_CXX11_SCOPED_ENUMS + +include (CheckCXXSourceCompiles) + +set(CMAKE_REQUIRED_INCLUDES ${Boost_INCLUDE_DIR}) +set(CMAKE_REQUIRED_LIBRARIES ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_IOSTREAMS_LIBRARY}) +set(CMAKE_REQUIRED_FLAGS "-std=c++11") +check_cxx_source_compiles(" +  #include <boost/filesystem/path.hpp> +  #include <boost/filesystem/operations.hpp> +  int main() { boost::filesystem::copy_file(boost::filesystem::path(), boost::filesystem::path()); }" +boost_filesystem_copy_links_without_NO_SCOPED_ENUM) +unset(CMAKE_REQUIRED_INCLUDES) +unset(CMAKE_REQUIRED_LIBRARIES) +unset(CMAKE_REQUIRED_FLAGS) + +add_library(boost INTERFACE) + +target_link_libraries(boost +  INTERFACE +    ${Boost_LIBRARIES}) + +target_include_directories(boost +  INTERFACE +    ${Boost_INCLUDE_DIRS}) + +if (boost_filesystem_copy_links_without_NO_SCOPED_ENUM) +  target_compile_definitions(boost +    INTERFACE +      -DBOOST_DATE_TIME_NO_LIB +      -DBOOST_REGEX_NO_LIB +      -DBOOST_CHRONO_NO_LIB) +else() +  target_compile_definitions(boost +    INTERFACE +      -DBOOST_DATE_TIME_NO_LIB +      -DBOOST_REGEX_NO_LIB +      -DBOOST_CHRONO_NO_LIB +      -DBOOST_NO_CXX11_SCOPED_ENUMS) +endif() diff --git a/dep/bzip2/CMakeLists.txt b/dep/bzip2/CMakeLists.txt index d3aadbe002e..d5a7414f383 100644 --- a/dep/bzip2/CMakeLists.txt +++ b/dep/bzip2/CMakeLists.txt @@ -8,15 +8,30 @@  # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -file(GLOB sources *.c) +if(UNIX) +  # Look for an installed bzip2 on unix +  find_package(BZip2 REQUIRED) -set(bzip2_STAT_SRCS -  ${sources} -) +  add_library(bzip2 SHARED IMPORTED GLOBAL) -include_directories( -  ${CMAKE_SOURCE_DIR}/dep/zlib -  ${CMAKE_CURRENT_SOURCE_DIR} -) +  set_target_properties(bzip2 +    PROPERTIES +      IMPORTED_LOCATION +        "${BZIP2_LIBRARIES}" +      INTERFACE_INCLUDE_DIRECTORIES +        "${BZIP2_INCLUDE_DIRS}") +else() +  # Use the bundled source on windows +  file(GLOB sources *.c) +  add_library(bzip2 STATIC +    ${sources}) -add_library(bzip2 STATIC ${bzip2_STAT_SRCS}) +  target_include_directories(bzip2 +    PUBLIC +      ${CMAKE_CURRENT_SOURCE_DIR}) + +  set_target_properties(bzip2 +    PROPERTIES +      FOLDER +        "dep") +endif() diff --git a/dep/cppformat/CMakeLists.txt b/dep/cppformat/CMakeLists.txt index 3be3e5f6dbb..1cbff49b871 100644 --- a/dep/cppformat/CMakeLists.txt +++ b/dep/cppformat/CMakeLists.txt @@ -1,31 +1,37 @@ -include(CheckCXXCompilerFlag) -include(CheckSymbolExists) - -set(FMT_SOURCES format.cc format.h) - -# Use variadic templates -add_definitions(-DFMT_VARIADIC_TEMPLATES=1) - -# Use deleted functions -add_definitions(-DFMT_USE_DELETED_FUNCTIONS=1) - -# Use static assert -add_definitions(-DFMT_USE_STATIC_ASSERT=1) +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +include(CheckSymbolExists)  if (WIN32)    check_symbol_exists(open io.h HAVE_OPEN)  else ()    check_symbol_exists(open fcntl.h HAVE_OPEN)  endif () +set(FMT_SOURCES +  ${CMAKE_CURRENT_SOURCE_DIR}/cppformat/format.h +  ${CMAKE_CURRENT_SOURCE_DIR}/cppformat/format.cc) +  if (HAVE_OPEN) -  add_definitions(-DFMT_USE_FILE_DESCRIPTORS=1) -  set(FMT_SOURCES ${FMT_SOURCES} posix.cc posix.h) -endif () +  set(FMT_SOURCES ${FMT_SOURCES} +    ${CMAKE_CURRENT_SOURCE_DIR}/cppformat/posix.h +    ${CMAKE_CURRENT_SOURCE_DIR}/cppformat/posix.cc) +endif() -add_library(format STATIC ${FMT_SOURCES}) +add_library(cppformat STATIC ${FMT_SOURCES}) -if (CMAKE_COMPILER_IS_GNUCXX) -  set_target_properties(format PROPERTIES COMPILE_FLAGS -    "-Wall -Wextra -Wshadow -pedantic") -endif () +target_include_directories(cppformat +  PUBLIC +    ${CMAKE_CURRENT_SOURCE_DIR}) + +set_target_properties(cppformat +    PROPERTIES +      FOLDER +        "dep") diff --git a/dep/cppformat/README.rst b/dep/cppformat/README.rst index fb4399f0af4..e859f909466 100644 --- a/dep/cppformat/README.rst +++ b/dep/cppformat/README.rst @@ -28,9 +28,9 @@ Features  * Format API with `format string syntax    <http://cppformat.github.io/latest/syntax.html>`_    similar to the one used by `str.format -  <http://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python. +  <https://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python.  * Safe `printf implementation -  <http://cppformat.github.io/latest/reference.html#printf-formatting-functions>`_ +  <http://cppformat.github.io/latest/api.html#printf-formatting-functions>`_    including the POSIX extension for positional arguments.  * Support for user-defined types.  * High speed: performance of the format API is close to that of @@ -103,10 +103,10 @@ An object of any user-defined type for which there is an overloaded      // s == "The date is 2012-12-9"  You can use the `FMT_VARIADIC -<http://cppformat.github.io/latest/reference.html#utilities>`_ +<http://cppformat.github.io/latest/api.html#utilities>`_  macro to create your own functions similar to `format -<http://cppformat.github.io/latest/reference.html#format>`_ and -`print <http://cppformat.github.io/latest/reference.html#print>`_ +<http://cppformat.github.io/latest/api.html#format>`_ and +`print <http://cppformat.github.io/latest/api.html#print>`_  which take arbitrary arguments:  .. code:: c++ @@ -132,13 +132,17 @@ Projects using this library  * `AMPL/MP <https://github.com/ampl/mp>`_:    An open-source library for mathematical programming -* `HarpyWar/pvpgn <https://github.com/HarpyWar/pvpgn>`_: +* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:    Player vs Player Gaming Network with tweaks -* `KBEngine <http://www.kbengine.org/>`_: An open-source MMOG server engine +* `KBEngine <http://kbengine.org/>`_: An open-source MMOG server engine + +* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows  * `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game +* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to generate randomized datasets +  * `PenUltima Online (POL) <http://www.polserver.com/>`_:    An MMO server, compatible with most Ultima Online clients @@ -148,7 +152,7 @@ Projects using this library  * `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster proxy -* `Saddy <https://code.google.com/p/saddy/>`_: +* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:    Small crossplatform 2D graphic engine  * `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_: @@ -188,7 +192,7 @@ doesn't support user-defined types. Printf also has safety issues although  they are mostly solved with `__attribute__ ((format (printf, ...))  <http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.  There is a POSIX extension that adds positional arguments required for -`i18n <http://en.wikipedia.org/wiki/Internationalization_and_localization>`_ +`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_  to printf but it is not a part of C99 and may not be available on some  platforms. @@ -376,18 +380,13 @@ C++ Format is distributed under the BSD `license  The `Format String Syntax  <http://cppformat.github.io/latest/syntax.html>`_  section in the documentation is based on the one from Python `string module -documentation <http://docs.python.org/3/library/string.html#module-string>`_ +documentation <https://docs.python.org/3/library/string.html#module-string>`_  adapted for the current library. For this reason the documentation is  distributed under the Python Software Foundation license available in  `doc/python-license.txt  <https://raw.github.com/cppformat/cppformat/master/doc/python-license.txt>`_.  It only applies if you distribute the documentation of C++ Format. -Links ------ - -`API changes/compatibility report <http://upstream-tracker.org/versions/cppformat.html>`_ -  Acknowledgments  --------------- diff --git a/dep/cppformat/format.cc b/dep/cppformat/cppformat/format.cc index 1970d53c500..daccd68f1da 100644 --- a/dep/cppformat/format.cc +++ b/dep/cppformat/cppformat/format.cc @@ -207,10 +207,15 @@ void format_error_code(fmt::Writer &out, int error_code,    out.clear();    static const char SEP[] = ": ";    static const char ERROR_STR[] = "error "; -  fmt::internal::IntTraits<int>::MainType ec_value = error_code;    // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.    std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; -  error_code_size += fmt::internal::count_digits(ec_value); +  typedef fmt::internal::IntTraits<int>::MainType MainType; +  MainType abs_value = static_cast<MainType>(error_code); +  if (internal::is_negative(error_code)) { +    abs_value = 0 - abs_value; +    ++error_code_size; +  } +  error_code_size += fmt::internal::count_digits(abs_value);    if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size)      out << message << SEP;    out << ERROR_STR << error_code; @@ -252,7 +257,7 @@ class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {    template <typename T>    unsigned visit_any_int(T value) {      typedef typename fmt::internal::IntTraits<T>::MainType UnsignedType; -    UnsignedType width = value; +    UnsignedType width = static_cast<UnsignedType>(value);      if (fmt::internal::is_negative(value)) {        spec_.align_ = fmt::ALIGN_LEFT;        width = 0 - width; @@ -278,8 +283,21 @@ class PrecisionHandler :    }  }; -// Converts an integer argument to an integral type T for printf. +template <typename T, typename U> +struct is_same { +  enum { value = 0 }; +}; +  template <typename T> +struct is_same<T, T> { +  enum { value = 1 }; +}; + +// An argument visitor that converts an integer argument to T for printf, +// if T is an integral type. If T is void, the argument is converted to +// corresponding signed or unsigned type depending on the type specifier: +// 'd' and 'i' - signed, other - unsigned) +template <typename T = void>  class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {   private:    fmt::internal::Arg &arg_; @@ -300,21 +318,25 @@ class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {    void visit_any_int(U value) {      bool is_signed = type_ == 'd' || type_ == 'i';      using fmt::internal::Arg; -    if (sizeof(T) <= sizeof(int)) { +    typedef typename fmt::internal::Conditional< +        is_same<T, void>::value, U, T>::type TargetType; +    if (sizeof(TargetType) <= sizeof(int)) {        // Extra casts are used to silence warnings.        if (is_signed) {          arg_.type = Arg::INT; -        arg_.int_value = static_cast<int>(static_cast<T>(value)); +        arg_.int_value = static_cast<int>(static_cast<TargetType>(value));        } else {          arg_.type = Arg::UINT; -        arg_.uint_value = static_cast<unsigned>( -            static_cast<typename fmt::internal::MakeUnsigned<T>::Type>(value)); +        typedef typename fmt::internal::MakeUnsigned<TargetType>::Type Unsigned; +        arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));        }      } else {        if (is_signed) {          arg_.type = Arg::LONG_LONG; -        arg_.long_long_value = -            static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value); +        // glibc's printf doesn't sign extend arguments of smaller types: +        //   std::printf("%lld", -42);  // prints "4294967254" +        // but we don't have to do the same because it's a UB. +        arg_.long_long_value = static_cast<fmt::LongLong>(value);        } else {          arg_.type = Arg::ULONG_LONG;          arg_.ulong_long_value = @@ -340,6 +362,21 @@ class CharConverter : public fmt::internal::ArgVisitor<CharConverter, void> {      arg_.int_value = static_cast<char>(value);    }  }; + +// Write the content of w to os. +void write(std::ostream &os, fmt::Writer &w) { +  const char *data = w.data(); +  typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize; +  UnsignedStreamSize size = w.size(); +  UnsignedStreamSize max_size = +      internal::to_unsigned((std::numeric_limits<std::streamsize>::max)()); +  do { +    UnsignedStreamSize n = size <= max_size ? size : max_size; +    os.write(data, static_cast<std::streamsize>(n)); +    data += n; +    size -= n; +  } while (size != 0); +}  }  // namespace  namespace internal { @@ -551,27 +588,25 @@ FMT_FUNC void fmt::WindowsError::init(  FMT_FUNC void fmt::internal::format_windows_error(      fmt::Writer &out, int error_code,      fmt::StringRef message) FMT_NOEXCEPT { -  class String { -   private: -    LPWSTR str_; - -   public: -    String() : str_() {} -    ~String() { LocalFree(str_); } -    LPWSTR *ptr() { return &str_; } -    LPCWSTR c_str() const { return str_; } -  };    FMT_TRY { -    String system_message; -    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | -        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, -        error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), -        reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) { -      UTF16ToUTF8 utf8_message; -      if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { -        out << message << ": " << utf8_message; -        return; +    MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer; +    buffer.resize(INLINE_BUFFER_SIZE); +    for (;;) { +      wchar_t *system_message = &buffer[0]; +      int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, +                                  0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), +                                  system_message, static_cast<uint32_t>(buffer.size()), 0); +      if (result != 0) { +        UTF16ToUTF8 utf8_message; +        if (utf8_message.convert(system_message) == ERROR_SUCCESS) { +          out << message << ": " << utf8_message; +          return; +        } +        break;        } +      if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) +        break;  // Can't get error message, report error code instead. +      buffer.resize(buffer.size() * 2);      }    } FMT_CATCH(...) {}    fmt::format_error_code(out, error_code, message);  // 'fmt::' is for bcc32. @@ -616,7 +651,7 @@ void fmt::internal::ArgMap<Char>::init(const ArgList &args) {          return;        case internal::Arg::NAMED_ARG:          named_arg = static_cast<const NamedArg*>(args.values_[i].pointer); -        map_.insert(Pair(named_arg->name, *named_arg)); +        map_.push_back(Pair(named_arg->name, *named_arg));          break;        default:          /*nothing*/; @@ -628,7 +663,7 @@ void fmt::internal::ArgMap<Char>::init(const ArgList &args) {      internal::Arg::Type arg_type = args.type(i);      if (arg_type == internal::Arg::NAMED_ARG) {        named_arg = static_cast<const NamedArg*>(args.args_[i].pointer); -      map_.insert(Pair(named_arg->name, *named_arg)); +      map_.push_back(Pair(named_arg->name, *named_arg));      }    }    for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { @@ -637,7 +672,7 @@ void fmt::internal::ArgMap<Char>::init(const ArgList &args) {        return;      case internal::Arg::NAMED_ARG:        named_arg = static_cast<const NamedArg*>(args.args_[i].pointer); -      map_.insert(Pair(named_arg->name, *named_arg)); +      map_.push_back(Pair(named_arg->name, *named_arg));        break;      default:        /*nothing*/; @@ -659,6 +694,7 @@ FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(      break;    case Arg::NAMED_ARG:      arg = *static_cast<const internal::Arg*>(arg.pointer); +    break;    default:      /*nothing*/;    } @@ -763,7 +799,7 @@ void fmt::internal::PrintfFormatter<Char>::format(      if (*s == '.') {        ++s;        if ('0' <= *s && *s <= '9') { -        spec.precision_ = parse_nonnegative_int(s); +        spec.precision_ = static_cast<int>(parse_nonnegative_int(s));        } else if (*s == '*') {          ++s;          spec.precision_ = PrecisionHandler().visit(get_arg(s)); @@ -772,7 +808,7 @@ void fmt::internal::PrintfFormatter<Char>::format(      Arg arg = get_arg(s, arg_index);      if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) -      spec.flags_ &= ~HASH_FLAG; +      spec.flags_ &= ~to_unsigned<int>(HASH_FLAG);      if (spec.fill_ == '0') {        if (arg.type <= Arg::LAST_NUMERIC_TYPE)          spec.align_ = ALIGN_NUMERIC; @@ -809,7 +845,7 @@ void fmt::internal::PrintfFormatter<Char>::format(        break;      default:        --s; -      ArgConverter<int>(arg, *s).visit(arg); +      ArgConverter<void>(arg, *s).visit(arg);      }      // Parse type. @@ -861,10 +897,11 @@ FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {    print(stdout, format_str, args);  } -FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, ArgList args) { +FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, +                         ArgList args) {    MemoryWriter w;    w.write(format_str, args); -  os.write(w.data(), w.size()); +  write(os, w);  }  FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) { @@ -882,6 +919,13 @@ FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {    return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);  } +FMT_FUNC int fmt::fprintf(std::ostream &os, CStringRef format, ArgList args) { +  MemoryWriter w; +  printf(w, format, args); +  write(os, w); +  return static_cast<int>(w.size()); +} +  #ifndef FMT_HEADER_ONLY  template struct fmt::internal::BasicData<void>; diff --git a/dep/cppformat/format.h b/dep/cppformat/cppformat/format.h index a98a166091b..08bb9b5d9e8 100644 --- a/dep/cppformat/format.h +++ b/dep/cppformat/cppformat/format.h @@ -28,14 +28,6 @@  #ifndef FMT_FORMAT_H_  #define FMT_FORMAT_H_ -#if defined _MSC_VER && _MSC_VER <= 1500 -typedef unsigned int       uint32_t; -typedef unsigned long long uint64_t; -typedef long long          intmax_t; -#else -#include <stdint.h> -#endif -  #include <cassert>  #include <cmath>  #include <cstdio> @@ -44,7 +36,8 @@ typedef long long          intmax_t;  #include <memory>  #include <stdexcept>  #include <string> -#include <map> +#include <vector> +#include <utility>  #ifndef FMT_USE_IOSTREAMS  # define FMT_USE_IOSTREAMS 1 @@ -64,40 +57,23 @@ typedef long long          intmax_t;  # include <iterator>  #endif -#ifdef _MSC_VER -# include <intrin.h>  // _BitScanReverse, _BitScanReverse64 - -namespace fmt { -namespace internal { -# pragma intrinsic(_BitScanReverse) -inline uint32_t clz(uint32_t x) { -  unsigned long r = 0; -  _BitScanReverse(&r, x); -  return 31 - r; -} -# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) - -# ifdef _WIN64 -#  pragma intrinsic(_BitScanReverse64) -# endif - -inline uint32_t clzll(uint64_t x) { -  unsigned long r = 0; -# ifdef _WIN64 -  _BitScanReverse64(&r, x); -# else -  // Scan the high 32 bits. -  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) -    return 63 - (r + 32); +#if defined(_MSC_VER) && _MSC_VER <= 1500 +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int64          intmax_t; +#else +#include <stdint.h> +#endif -  // Scan the low 32 bits. -  _BitScanReverse(&r, static_cast<uint32_t>(x)); +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# ifdef FMT_EXPORT +#  define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +#  define FMT_API __declspec(dllimport)  # endif -  return 63 - r; -} -# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) -} -} +#endif +#ifndef FMT_API +# define FMT_API  #endif  #ifdef __GNUC__ @@ -174,21 +150,6 @@ inline uint32_t clzll(uint64_t x) {  # include <utility>  // for std::move  #endif -// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ -   (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ -   _MSC_VER >= 1900 -#  define FMT_NOEXCEPT noexcept -# else -#  define FMT_NOEXCEPT throw() -# endif -#endif -  // Check if exceptions are disabled.  #if defined(__GNUC__) && !defined(__EXCEPTIONS)  # define FMT_EXCEPTIONS 0 @@ -208,6 +169,25 @@ inline uint32_t clzll(uint64_t x) {  # endif  #endif +// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS +#  if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ +    (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ +    _MSC_VER >= 1900 +#   define FMT_NOEXCEPT noexcept +#  else +#   define FMT_NOEXCEPT throw() +#  endif +# else +#  define FMT_NOEXCEPT +# endif +#endif +  // A macro to disallow the copy constructor and operator= functions  // This should be used in the private: declarations for a class  #ifndef FMT_USE_DELETED_FUNCTIONS @@ -241,6 +221,67 @@ inline uint32_t clzll(uint64_t x) {  # define FMT_ASSERT(condition, message) assert((condition) && message)  #endif + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or  +// otherwise support __builtin_clz and __builtin_clzll, so +// only define FMT_BUILTIN_CLZ using the MSVC intrinsics +// if the clz and clzll builtins are not available. +#if defined(_MSC_VER) && !defined(FMT_BUILTIN_CLZLL) +# include <intrin.h>  // _BitScanReverse, _BitScanReverse64 + +namespace fmt { +namespace internal { +# pragma intrinsic(_BitScanReverse) +inline uint32_t clz(uint32_t x) { +  unsigned long r = 0; +  _BitScanReverse(&r, x); + +  assert(x != 0); +  // Static analysis complains about using uninitialized data +  // "r", but the only way that can happen is if "x" is 0,  +  // which the callers guarantee to not happen. +# pragma warning(suppress: 6102) +  return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) + +# ifdef _WIN64 +#  pragma intrinsic(_BitScanReverse64) +# endif + +inline uint32_t clzll(uint64_t x) { +  unsigned long r = 0; +# ifdef _WIN64 +  _BitScanReverse64(&r, x); +# else +  // Scan the high 32 bits. +  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) +    return 63 - (r + 32); + +  // Scan the low 32 bits. +  _BitScanReverse(&r, static_cast<uint32_t>(x)); +# endif + +  assert(x != 0); +  // Static analysis complains about using uninitialized data +  // "r", but the only way that can happen is if "x" is 0,  +  // which the callers guarantee to not happen. +# pragma warning(suppress: 6102) +  return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +} +} +#endif +  namespace fmt {  namespace internal {  struct DummyInt { @@ -396,7 +437,7 @@ class BasicStringRef {      return std::basic_string<Char>(data_, size_);    } -  /** Returns the pointer to a C string. */ +  /** Returns a pointer to the string data. */    const Char *data() const { return data_; }    /** Returns the string size. */ @@ -492,6 +533,29 @@ class FormatError : public std::runtime_error {  };  namespace internal { + +// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T. +template <typename T> +struct MakeUnsigned { typedef T Type; }; + +#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ +  template <> \ +  struct MakeUnsigned<T> { typedef U Type; } + +FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); +FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); +FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); +FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); + +// Casts nonnegative integer to unsigned. +template <typename Int> +inline typename MakeUnsigned<Int>::Type to_unsigned(Int value) { +  FMT_ASSERT(value >= 0, "negative value"); +  return static_cast<typename MakeUnsigned<Int>::Type>(value); +} +  // The number of characters to store in the MemoryBuffer object itself  // to avoid dynamic memory allocation.  enum { INLINE_BUFFER_SIZE = 500 }; @@ -581,8 +645,7 @@ class Buffer {  template <typename T>  template <typename U>  void Buffer<T>::append(const U *begin, const U *end) { -  assert(begin <= end); -  std::size_t new_size = size_ + (end - begin); +  std::size_t new_size = size_ + internal::to_unsigned(end - begin);    if (new_size > capacity_)      grow(new_size);    std::uninitialized_copy(begin, end, @@ -592,8 +655,8 @@ void Buffer<T>::append(const U *begin, const U *end) {  namespace internal { -// A memory buffer for POD types with the first SIZE elements stored in -// the object itself. +// A memory buffer for trivially copyable/constructible types with the first SIZE +// elements stored in the object itself.  template <typename T, std::size_t SIZE, typename Allocator = std::allocator<T> >  class MemoryBuffer : private Allocator, public Buffer<T> {   private: @@ -676,7 +739,7 @@ class FixedBuffer : public fmt::Buffer<Char> {    FixedBuffer(Char *array, std::size_t size) : fmt::Buffer<Char>(array, size) {}   protected: -  void grow(std::size_t size); +  FMT_API void grow(std::size_t size);  };  template <typename Char> @@ -704,7 +767,7 @@ class CharTraits<char> : public BasicCharTraits<char> {    // Formats a floating-point number.    template <typename T> -  static int format_float(char *buffer, std::size_t size, +  FMT_API static int format_float(char *buffer, std::size_t size,        const char *format, unsigned width, int precision, T value);  }; @@ -715,7 +778,7 @@ class CharTraits<wchar_t> : public BasicCharTraits<wchar_t> {    static wchar_t convert(wchar_t value) { return value; }    template <typename T> -  static int format_float(wchar_t *buffer, std::size_t size, +  FMT_API static int format_float(wchar_t *buffer, std::size_t size,        const wchar_t *format, unsigned width, int precision, T value);  }; @@ -754,27 +817,12 @@ struct IntTraits {      TypeSelector<std::numeric_limits<T>::digits <= 32>::Type MainType;  }; -// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T. -template <typename T> -struct MakeUnsigned { typedef T Type; }; - -#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ -  template <> \ -  struct MakeUnsigned<T> { typedef U Type; } - -FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); -FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); -FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); -FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); - -void report_unknown_type(char code, const char *type); +FMT_API void report_unknown_type(char code, const char *type);  // Static data is placed in this class template to allow header-only  // configuration.  template <typename T = void> -struct BasicData { +struct FMT_API BasicData {    static const uint32_t POWERS_OF_10_32[];    static const uint64_t POWERS_OF_10_64[];    static const char DIGITS[]; @@ -782,22 +830,14 @@ struct BasicData {  typedef BasicData<> Data; -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif -  #ifdef FMT_BUILTIN_CLZLL  // Returns the number of decimal digits in n. Leading zeros are not counted  // except for n == 0 in which case count_digits returns 1.  inline unsigned count_digits(uint64_t n) {    // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10    // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. -  unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; -  return t - (n < Data::POWERS_OF_10_64[t]) + 1; +  int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; +  return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1;  }  #else  // Fallback version of count_digits used when __builtin_clz is not available. @@ -820,8 +860,8 @@ inline unsigned count_digits(uint64_t n) {  #ifdef FMT_BUILTIN_CLZ  // Optional version of count_digits for better performance on 32-bit platforms.  inline unsigned count_digits(uint32_t n) { -  uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; -  return t - (n < Data::POWERS_OF_10_32[t]) + 1; +  int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; +  return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1;  }  #endif @@ -863,7 +903,7 @@ class UTF8ToUTF16 {    MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer_;   public: -  explicit UTF8ToUTF16(StringRef s); +  FMT_API explicit UTF8ToUTF16(StringRef s);    operator WStringRef() const { return WStringRef(&buffer_[0], size()); }    size_t size() const { return buffer_.size() - 1; }    const wchar_t *c_str() const { return &buffer_[0]; } @@ -878,7 +918,7 @@ class UTF16ToUTF8 {   public:    UTF16ToUTF8() {} -  explicit UTF16ToUTF8(WStringRef s); +  FMT_API explicit UTF16ToUTF8(WStringRef s);    operator StringRef() const { return StringRef(&buffer_[0], size()); }    size_t size() const { return buffer_.size() - 1; }    const char *c_str() const { return &buffer_[0]; } @@ -887,15 +927,15 @@ class UTF16ToUTF8 {    // Performs conversion returning a system error code instead of    // throwing exception on conversion error. This method may still throw    // in case of memory allocation error. -  int convert(WStringRef s); +  FMT_API int convert(WStringRef s);  }; -void format_windows_error(fmt::Writer &out, int error_code, -                          fmt::StringRef message) FMT_NOEXCEPT; +FMT_API void format_windows_error(fmt::Writer &out, int error_code, +                                  fmt::StringRef message) FMT_NOEXCEPT;  #endif -void format_system_error(fmt::Writer &out, int error_code, -                         fmt::StringRef message) FMT_NOEXCEPT; +FMT_API void format_system_error(fmt::Writer &out, int error_code, +                                 fmt::StringRef message) FMT_NOEXCEPT;  // A formatting argument value.  struct Value { @@ -938,8 +978,8 @@ struct Value {    };  }; -// A formatting argument. It is a POD type to allow storage in -// internal::MemoryBuffer. +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in internal::MemoryBuffer.  struct Arg : Value {    Type type;  }; @@ -976,6 +1016,7 @@ template <typename T>  T &get();  struct DummyStream : std::ostream { +  DummyStream();  // Suppress a bogus warning in MSVC.    // Hide all operator<< overloads from std::ostream.    void operator<<(Null<>);  }; @@ -1199,17 +1240,27 @@ class MakeValue : public Arg {    static uint64_t type(const NamedArg<Char_> &) { return Arg::NAMED_ARG; }  }; +template <typename Formatter> +class MakeArg : public Arg { +public: +  MakeArg() { +    type = Arg::NONE; +  } +   +  template <typename T> +  MakeArg(const T &value) +  : Arg(MakeValue<Formatter>(value)) { +    type = static_cast<Arg::Type>(MakeValue<Formatter>::type(value)); +  } +}; +  template <typename Char>  struct NamedArg : Arg {    BasicStringRef<Char> name; -  typedef internal::MakeValue< BasicFormatter<Char> > MakeValue; -    template <typename T>    NamedArg(BasicStringRef<Char> argname, const T &value) -  : Arg(MakeValue(value)), name(argname) { -    type = static_cast<Arg::Type>(MakeValue::type(value)); -  } +  : Arg(MakeArg< BasicFormatter<Char> >(value)), name(argname) {}  };  #define FMT_DISPATCH(call) static_cast<Impl*>(this)->call @@ -1631,17 +1682,22 @@ namespace internal {  template <typename Char>  class ArgMap {   private: -  typedef std::map<fmt::BasicStringRef<Char>, internal::Arg> MapType; +  typedef std::vector<std::pair<fmt::BasicStringRef<Char>, internal::Arg> > MapType;    typedef typename MapType::value_type Pair;    MapType map_;   public: -  void init(const ArgList &args); +  FMT_API void init(const ArgList &args);    const internal::Arg* find(const fmt::BasicStringRef<Char> &name) const { -    typename MapType::const_iterator it = map_.find(name); -    return it != map_.end() ? &it->second : 0; +    // The list is unsorted, so just return the first matching name. +    for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); +         it != end; ++it) { +      if (it->first == name) +        return &it->second; +    } +    return 0;    }  }; @@ -1767,7 +1823,7 @@ class FormatterBase {    int next_arg_index_;    // Returns the argument with specified index. -  Arg do_get_arg(unsigned arg_index, const char *&error); +  FMT_API Arg do_get_arg(unsigned arg_index, const char *&error);   protected:    const ArgList &args() const { return args_; } @@ -1780,7 +1836,7 @@ class FormatterBase {    // Returns the next argument.    Arg next_arg(const char *&error) {      if (next_arg_index_ >= 0) -      return do_get_arg(next_arg_index_++, error); +      return do_get_arg(internal::to_unsigned(next_arg_index_++), error);      error = "cannot switch from manual to automatic argument indexing";      return Arg();    } @@ -1803,7 +1859,7 @@ class FormatterBase {    template <typename Char>    void write(BasicWriter<Char> &w, const Char *start, const Char *end) {      if (start != end) -      w << BasicStringRef<Char>(start, end - start); +      w << BasicStringRef<Char>(start, internal::to_unsigned(end - start));    }  }; @@ -1823,14 +1879,16 @@ class PrintfFormatter : private FormatterBase {   public:    explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {} -  void format(BasicWriter<Char> &writer, BasicCStringRef<Char> format_str); +  FMT_API void format(BasicWriter<Char> &writer, +                      BasicCStringRef<Char> format_str);  };  }  // namespace internal -// A formatter. +/** This template formats data and writes the output to a writer. */  template <typename CharType>  class BasicFormatter : private internal::FormatterBase {   public: +  /** The character type for the output. */    typedef CharType Char;   private: @@ -1852,13 +1910,23 @@ class BasicFormatter : private internal::FormatterBase {    internal::Arg parse_arg_name(const Char *&s);   public: +  /** +   \rst +   Constructs a ``BasicFormatter`` object. References to the arguments and +   the writer are stored in the formatter object so make sure they have +   appropriate lifetimes. +   \endrst +   */    BasicFormatter(const ArgList &args, BasicWriter<Char> &w)      : internal::FormatterBase(args), writer_(w) {} +  /** Returns a reference to the writer associated with this formatter. */    BasicWriter<Char> &writer() { return writer_; } +  /** Formats stored arguments and writes the output to the writer. */    void format(BasicCStringRef<Char> format_str); +  // Formats a single argument and advances format_str, a format string pointer.    const Char *format(const Char *&format_str, const internal::Arg &arg);  }; @@ -1889,16 +1957,29 @@ inline uint64_t make_type(const T &arg) {    return MakeValue< BasicFormatter<char> >::type(arg);  } +template <unsigned N, bool/*IsPacked*/= (N < ArgList::MAX_PACKED_ARGS)> +struct ArgArray; + +template <unsigned N> +struct ArgArray<N, true/*IsPacked*/> { +  typedef Value Type[N > 0 ? N : 1]; +   +  template <typename Formatter, typename T> +  static Value make(const T &value) { +    Value result = MakeValue<Formatter>(value); +    // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: +    // https://github.com/cppformat/cppformat/issues/276 +    (void)result.custom.format; +    return result; +  } +}; +  template <unsigned N> -struct ArgArray { -  // Computes the argument array size by adding 1 to N, which is the number of -  // arguments, if N is zero, because array of zero size is invalid, or if N -  // is greater than ArgList::MAX_PACKED_ARGS to accommodate for an extra -  // argument that marks the end of the list. -  enum { SIZE = N + (N == 0 || N >= ArgList::MAX_PACKED_ARGS ? 1 : 0) }; - -  typedef typename Conditional< -    (N < ArgList::MAX_PACKED_ARGS), Value, Arg>::type Type[SIZE]; +struct ArgArray<N, false/*IsPacked*/> { +  typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE + +  template <typename Formatter, typename T> +  static Arg make(const T &value) { return MakeArg<Formatter>(value); }  };  #if FMT_USE_VARIADIC_TEMPLATES @@ -1907,47 +1988,6 @@ inline uint64_t make_type(const Arg &first, const Args & ... tail) {    return make_type(first) | (make_type(tail...) << 4);  } -inline void do_set_types(Arg *) {} - -template <typename T, typename... Args> -inline void do_set_types(Arg *args, const T &arg, const Args & ... tail) { -  args->type = static_cast<Arg::Type>( -        MakeValue< BasicFormatter<char> >::type(arg)); -  do_set_types(args + 1, tail...); -} - -template <typename... Args> -inline void set_types(Arg *array, const Args & ... args) { -  if (check(sizeof...(Args) > ArgList::MAX_PACKED_ARGS)) -    do_set_types(array, args...); -  array[sizeof...(Args)].type = Arg::NONE; -} - -template <typename... Args> -inline void set_types(Value *, const Args & ...) { -  // Do nothing as types are passed separately from values. -} - -template <typename Formatter, typename Value> -inline void store_args(Value *) {} - -template <typename Formatter, typename Arg, typename T, typename... Args> -inline void store_args(Arg *args, const T &arg, const Args & ... tail) { -  // Assign only the Value subobject of Arg and don't overwrite type (if any) -  // that is assigned by set_types. -  Value &value = *args; -  value = MakeValue<Formatter>(arg); -  store_args<Formatter>(args + 1, tail...); -} - -template <typename Formatter, typename... Args> -ArgList make_arg_list(typename ArgArray<sizeof...(Args)>::Type array, -                      const Args & ... args) { -  if (check(sizeof...(Args) >= ArgList::MAX_PACKED_ARGS)) -    set_types(array, args...); -  store_args<Formatter>(array, args...); -  return ArgList(make_type(args...), array); -}  #else  struct ArgType { @@ -1985,7 +2025,7 @@ class FormatBuf : public std::basic_streambuf<Char> {    int_type overflow(int_type ch = traits_type::eof()) {      if (!traits_type::eq_int_type(ch, traits_type::eof())) { -      size_t size = this->pptr() - start_; +      size_t size = this->size();        buffer_.resize(size);        buffer_.reserve(size * 2); @@ -1997,7 +2037,7 @@ class FormatBuf : public std::basic_streambuf<Char> {    }    size_t size() const { -    return this->pptr() - start_; +    return to_unsigned(this->pptr() - start_);    }  };  }  // namespace internal @@ -2015,18 +2055,20 @@ class FormatBuf : public std::basic_streambuf<Char> {  # define FMT_VARIADIC_VOID(func, arg_type) \    template <typename... Args> \    void func(arg_type arg0, const Args & ... args) { \ -    typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \ -    func(arg0, fmt::internal::make_arg_list< \ -      fmt::BasicFormatter<Char> >(array, args...)); \ +    typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \ +    typename ArgArray::Type array{ \ +      ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \ +    func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \    }  // Defines a variadic constructor.  # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \    template <typename... Args> \    ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ -    typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \ -    func(arg0, arg1, fmt::internal::make_arg_list< \ -      fmt::BasicFormatter<Char> >(array, args...)); \ +    typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \ +    typename ArgArray::Type array{ \ +      ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \ +    func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \    }  #else @@ -2207,7 +2249,8 @@ class BasicWriter {    // Writes a decimal integer.    template <typename Int>    void write_decimal(Int value) { -    typename internal::IntTraits<Int>::MainType abs_value = value; +    typedef typename internal::IntTraits<Int>::MainType MainType; +    MainType abs_value = static_cast<MainType>(value);      if (internal::is_negative(value)) {        abs_value = 0 - abs_value;        *write_unsigned_decimal(abs_value, 1) = '-'; @@ -2474,9 +2517,9 @@ void BasicWriter<Char>::write_str(        return;      }    } -  std::size_t precision = spec.precision_; +  std::size_t precision = static_cast<std::size_t>(spec.precision_);    if (spec.precision_ >= 0 && precision < str_size) -    str_size = spec.precision_; +    str_size = precision;    write_str(str_value, str_size, spec);  } @@ -2510,7 +2553,8 @@ typename BasicWriter<Char>::CharPtr      // is specified.      if (prefix_size > 0 && prefix[prefix_size - 1] == '0')        --prefix_size; -    unsigned number_size = prefix_size + spec.precision(); +    unsigned number_size = +        prefix_size + internal::to_unsigned(spec.precision());      AlignSpec subspec(number_size, '0', ALIGN_NUMERIC);      if (number_size >= width)        return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); @@ -2564,7 +2608,7 @@ template <typename T, typename Spec>  void BasicWriter<Char>::write_int(T value, Spec spec) {    unsigned prefix_size = 0;    typedef typename internal::IntTraits<T>::MainType UnsignedType; -  UnsignedType abs_value = value; +  UnsignedType abs_value = static_cast<UnsignedType>(value);    char prefix[4] = "";    if (internal::is_negative(value)) {      prefix[0] = '-'; @@ -2643,8 +2687,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec) {  template <typename Char>  template <typename T> -void BasicWriter<Char>::write_double( -    T value, const FormatSpec &spec) { +void BasicWriter<Char>::write_double(T value, const FormatSpec &spec) {    // Check type.    char type = spec.type();    bool upper = false; @@ -2744,6 +2787,8 @@ void BasicWriter<Char>::write_double(    // Format using snprintf.    Char fill = internal::CharTraits<Char>::cast(spec.fill()); +  unsigned n = 0; +  Char *start = 0;    for (;;) {      std::size_t buffer_size = buffer_.capacity() - offset;  #ifdef _MSC_VER @@ -2755,41 +2800,44 @@ void BasicWriter<Char>::write_double(        buffer_size = buffer_.capacity() - offset;      }  #endif -    Char *start = &buffer_[offset]; -    int n = internal::CharTraits<Char>::format_float( +    start = &buffer_[offset]; +    int result = internal::CharTraits<Char>::format_float(          start, buffer_size, format, width_for_sprintf, spec.precision(), value); -    if (n >= 0 && offset + n < buffer_.capacity()) { -      if (sign) { -        if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || -            *start != ' ') { -          *(start - 1) = sign; -          sign = 0; -        } else { -          *(start - 1) = fill; -        } -        ++n; -      } -      if (spec.align() == ALIGN_CENTER && -          spec.width() > static_cast<unsigned>(n)) { -        width = spec.width(); -        CharPtr p = grow_buffer(width); -        std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); -        fill_padding(p, spec.width(), n, fill); -        return; -      } -      if (spec.fill() != ' ' || sign) { -        while (*start == ' ') -          *start++ = fill; -        if (sign) -          *(start - 1) = sign; -      } -      grow_buffer(n); -      return; +    if (result >= 0) { +      n = internal::to_unsigned(result); +      if (offset + n < buffer_.capacity()) +        break;  // The buffer is large enough - continue with formatting. +      buffer_.reserve(offset + n + 1); +    } else { +      // If result is negative we ask to increase the capacity by at least 1, +      // but as std::vector, the buffer grows exponentially. +      buffer_.reserve(buffer_.capacity() + 1);      } -    // If n is negative we ask to increase the capacity by at least 1, -    // but as std::vector, the buffer grows exponentially. -    buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1);    } +  if (sign) { +    if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || +        *start != ' ') { +      *(start - 1) = sign; +      sign = 0; +    } else { +      *(start - 1) = fill; +    } +    ++n; +  } +  if (spec.align() == ALIGN_CENTER && spec.width() > n) { +    width = spec.width(); +    CharPtr p = grow_buffer(width); +    std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); +    fill_padding(p, spec.width(), n, fill); +    return; +  } +  if (spec.fill() != ' ' || sign) { +    while (*start == ' ') +      *start++ = fill; +    if (sign) +      *(start - 1) = sign; +  } +  grow_buffer(n);  }  /** @@ -2920,22 +2968,21 @@ void format(BasicFormatter<Char> &f, const Char *&format_str, const T &value) {    output << value;    BasicStringRef<Char> str(&buffer[0], format_buf.size()); -  typedef internal::MakeValue< BasicFormatter<Char> > MakeValue; -  internal::Arg arg = MakeValue(str); -  arg.type = static_cast<internal::Arg::Type>(MakeValue::type(str)); -  format_str = f.format(format_str, arg); +  typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; +  format_str = f.format(format_str, MakeArg(str));  }  // Reports a system error without throwing an exception.  // Can be used to report errors from destructors. -void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; +FMT_API void report_system_error(int error_code, +                                 StringRef message) FMT_NOEXCEPT;  #if FMT_USE_WINDOWS_H  /** A Windows error. */  class WindowsError : public SystemError {   private: -  void init(int error_code, CStringRef format_str, ArgList args); +  FMT_API void init(int error_code, CStringRef format_str, ArgList args);   public:    /** @@ -2974,7 +3021,8 @@ class WindowsError : public SystemError {  // Reports a Windows error without throwing an exception.  // Can be used to report errors from destructors. -void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; +FMT_API void report_windows_error(int error_code, +                                  StringRef message) FMT_NOEXCEPT;  #endif @@ -2986,7 +3034,7 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };    Example:      print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);   */ -void print_colored(Color c, CStringRef format, ArgList args); +FMT_API void print_colored(Color c, CStringRef format, ArgList args);  /**    \rst @@ -3018,7 +3066,7 @@ inline std::wstring format(WCStringRef format_str, ArgList args) {      print(stderr, "Don't {}!", "panic");    \endrst   */ -void print(std::FILE *f, CStringRef format_str, ArgList args); +FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args);  /**    \rst @@ -3029,7 +3077,7 @@ void print(std::FILE *f, CStringRef format_str, ArgList args);      print("Elapsed time: {0:.2f} seconds", 1.23);    \endrst   */ -void print(CStringRef format_str, ArgList args); +FMT_API void print(CStringRef format_str, ArgList args);  template <typename Char>  void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args) { @@ -3066,7 +3114,7 @@ inline std::wstring sprintf(WCStringRef format, ArgList args) {      fmt::fprintf(stderr, "Don't %s!", "panic");    \endrst   */ -int fprintf(std::FILE *f, CStringRef format, ArgList args); +FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);  /**    \rst @@ -3132,10 +3180,10 @@ class FormatInt {    explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {}    explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} -  /** -    Returns the number of characters written to the output buffer. -   */ -  std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } +  /** Returns the number of characters written to the output buffer. */ +  std::size_t size() const { +    return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1); +  }    /**      Returns a pointer to the output buffer content. No terminating null @@ -3165,7 +3213,8 @@ class FormatInt {  // write a terminating null character.  template <typename T>  inline void format_decimal(char *&buffer, T value) { -  typename internal::IntTraits<T>::MainType abs_value = value; +  typedef typename internal::IntTraits<T>::MainType MainType; +  MainType abs_value = static_cast<MainType>(value);    if (internal::is_negative(value)) {      *buffer++ = '-';      abs_value = 0 - abs_value; @@ -3245,10 +3294,11 @@ void arg(WStringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;    template <typename... Args> \    ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \        const Args & ... args) { \ -    typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \ +    typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \ +    typename ArgArray::Type array{ \ +      ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \      call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ -      fmt::internal::make_arg_list< \ -        fmt::BasicFormatter<Char> >(array, args...)); \ +      fmt::ArgList(fmt::internal::make_type(args...), array)); \    }  #else  // Defines a wrapper for a function taking __VA_ARGS__ arguments @@ -3361,8 +3411,20 @@ FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)      print(cerr, "Don't {}!", "panic");    \endrst   */ -void print(std::ostream &os, CStringRef format_str, ArgList args); +FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);  FMT_VARIADIC(void, print, std::ostream &, CStringRef) + +/** +  \rst +  Prints formatted data to the stream *os*. + +  **Example**:: + +    fprintf(cerr, "Don't %s!", "panic"); +  \endrst + */ +FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args); +FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)  #endif  namespace internal { @@ -3374,7 +3436,7 @@ inline bool is_name_start(Char c) {  // Parses an unsigned integer advancing s to the end of the parsed input.  // This function assumes that the first character of s is a digit.  template <typename Char> -int parse_nonnegative_int(const Char *&s) { +unsigned parse_nonnegative_int(const Char *&s) {    assert('0' <= *s && *s <= '9');    unsigned value = 0;    do { @@ -3453,7 +3515,6 @@ inline internal::Arg BasicFormatter<Char>::parse_arg_name(const Char *&s) {    return arg;  } -// Should be after FormatSpec  template <typename Char>  const Char *BasicFormatter<Char>::format(      const Char *&format_str, const internal::Arg &arg) { diff --git a/dep/cppformat/posix.cc b/dep/cppformat/cppformat/posix.cc index 756281a0ebd..c6c2ae2c413 100644 --- a/dep/cppformat/posix.cc +++ b/dep/cppformat/cppformat/posix.cc @@ -173,7 +173,7 @@ std::size_t fmt::File::read(void *buffer, std::size_t count) {    FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));    if (result < 0)      throw SystemError(errno, "cannot read from file"); -  return result; +  return internal::to_unsigned(result);  }  std::size_t fmt::File::write(const void *buffer, std::size_t count) { @@ -181,7 +181,7 @@ std::size_t fmt::File::write(const void *buffer, std::size_t count) {    FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));    if (result < 0)      throw SystemError(errno, "cannot write to file"); -  return result; +  return internal::to_unsigned(result);  }  fmt::File fmt::File::dup(int fd) { diff --git a/dep/cppformat/posix.h b/dep/cppformat/cppformat/posix.h index 88bcb4f557b..bfbd3851838 100644 --- a/dep/cppformat/posix.h +++ b/dep/cppformat/cppformat/posix.h @@ -34,11 +34,17 @@  #endif  #include <errno.h> -#include <fcntl.h>  // for O_RDONLY +#include <fcntl.h>   // for O_RDONLY +#include <locale.h>  // for locale_t  #include <stdio.h> +#include <stdlib.h>  // for strtod_l  #include <cstddef> +#ifdef __APPLE__ +# include <xlocale.h>  // for LC_NUMERIC_MASK on OS X +#endif +  #include "format.h"  #ifndef FMT_POSIX @@ -299,7 +305,8 @@ class File {    // Closes the file.    void close(); -  // Returns the file size. +  // Returns the file size. The size has signed type for consistency with +  // stat::st_size.    LongLong size() const;    // Attempts to read count bytes from the file into the specified buffer. @@ -331,6 +338,58 @@ class File {  // Returns the memory page size.  long getpagesize(); + +#if defined(LC_NUMERIC_MASK) || defined(_MSC_VER) +# define FMT_LOCALE +#endif + +#ifdef FMT_LOCALE +// A "C" numeric locale. +class Locale { + private: +# ifdef _MSC_VER +  typedef _locale_t locale_t; + +  enum { LC_NUMERIC_MASK = LC_NUMERIC }; + +  static locale_t newlocale(int category_mask, const char *locale, locale_t) { +    return _create_locale(category_mask, locale); +  } + +  static void freelocale(locale_t locale) { +    _free_locale(locale); +  } + +  static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { +    return _strtod_l(nptr, endptr, locale); +  } +# endif + +  locale_t locale_; + +  FMT_DISALLOW_COPY_AND_ASSIGN(Locale); + + public: +  typedef locale_t Type; + +  Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL)) { +    if (!locale_) +      throw fmt::SystemError(errno, "cannot create locale"); +  } +  ~Locale() { freelocale(locale_); } + +  Type get() const { return locale_; } + +  // Converts string to floating-point number and advances str past the end +  // of the parsed input. +  double strtod(const char *&str) const { +    char *end = 0; +    double result = strtod_l(str, &end, locale_); +    str = end; +    return result; +  } +}; +#endif  // FMT_LOCALE  }  // namespace fmt  #if !FMT_USE_RVALUE_REFERENCES diff --git a/dep/efsw/.hg_archival.txt b/dep/efsw/.hg_archival.txt new file mode 100644 index 00000000000..19565afb668 --- /dev/null +++ b/dep/efsw/.hg_archival.txt @@ -0,0 +1,5 @@ +repo: 78c2ea8c48b213ee0078d6326a1dd719d0844764 +node: ff0b69daeca1edf7785a8a580518e462be5a6f3d +branch: default +latesttag: null +latesttagdistance: 144 diff --git a/dep/efsw/CMakeLists.txt b/dep/efsw/CMakeLists.txt new file mode 100644 index 00000000000..eecc5531944 --- /dev/null +++ b/dep/efsw/CMakeLists.txt @@ -0,0 +1,85 @@ +if (BUILD_SHARED_LIBS) +  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 IMPORTED GLOBAL) +endif() diff --git a/dep/efsw/LICENSE b/dep/efsw/LICENSE new file mode 100644 index 00000000000..ac8ac28988d --- /dev/null +++ b/dep/efsw/LICENSE @@ -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.
\ No newline at end of file diff --git a/dep/efsw/README.md b/dep/efsw/README.md new file mode 100644 index 00000000000..fda0bb599aa --- /dev/null +++ b/dep/efsw/README.md @@ -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). diff --git a/dep/efsw/include/efsw/efsw.h b/dep/efsw/include/efsw/efsw.h new file mode 100644 index 00000000000..28e63e2139e --- /dev/null +++ b/dep/efsw/include/efsw/efsw.h @@ -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 diff --git a/dep/efsw/include/efsw/efsw.hpp b/dep/efsw/include/efsw/efsw.hpp new file mode 100644 index 00000000000..0693bb296f0 --- /dev/null +++ b/dep/efsw/include/efsw/efsw.hpp @@ -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 diff --git a/dep/efsw/src/efsw/Debug.cpp b/dep/efsw/src/efsw/Debug.cpp new file mode 100644 index 00000000000..9c4ee02e5b6 --- /dev/null +++ b/dep/efsw/src/efsw/Debug.cpp @@ -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 + +} + diff --git a/dep/efsw/src/efsw/Debug.hpp b/dep/efsw/src/efsw/Debug.hpp new file mode 100644 index 00000000000..75d6dce0466 --- /dev/null +++ b/dep/efsw/src/efsw/Debug.hpp @@ -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 diff --git a/dep/efsw/src/efsw/DirWatcherGeneric.cpp b/dep/efsw/src/efsw/DirWatcherGeneric.cpp new file mode 100644 index 00000000000..b80c14d24ce --- /dev/null +++ b/dep/efsw/src/efsw/DirWatcherGeneric.cpp @@ -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; +} + +}  diff --git a/dep/efsw/src/efsw/DirWatcherGeneric.hpp b/dep/efsw/src/efsw/DirWatcherGeneric.hpp new file mode 100644 index 00000000000..a7581904422 --- /dev/null +++ b/dep/efsw/src/efsw/DirWatcherGeneric.hpp @@ -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 diff --git a/dep/efsw/src/efsw/DirectorySnapshot.cpp b/dep/efsw/src/efsw/DirectorySnapshot.cpp new file mode 100644 index 00000000000..c0ef747548a --- /dev/null +++ b/dep/efsw/src/efsw/DirectorySnapshot.cpp @@ -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 ); +} + +} diff --git a/dep/efsw/src/efsw/DirectorySnapshot.hpp b/dep/efsw/src/efsw/DirectorySnapshot.hpp new file mode 100644 index 00000000000..1ada66fe980 --- /dev/null +++ b/dep/efsw/src/efsw/DirectorySnapshot.hpp @@ -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 + diff --git a/dep/efsw/src/efsw/DirectorySnapshotDiff.cpp b/dep/efsw/src/efsw/DirectorySnapshotDiff.cpp new file mode 100644 index 00000000000..eabc6fdbda1 --- /dev/null +++ b/dep/efsw/src/efsw/DirectorySnapshotDiff.cpp @@ -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(); +} + +} diff --git a/dep/efsw/src/efsw/DirectorySnapshotDiff.hpp b/dep/efsw/src/efsw/DirectorySnapshotDiff.hpp new file mode 100644 index 00000000000..042de9ce02c --- /dev/null +++ b/dep/efsw/src/efsw/DirectorySnapshotDiff.hpp @@ -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 + diff --git a/dep/efsw/src/efsw/FileInfo.cpp b/dep/efsw/src/efsw/FileInfo.cpp new file mode 100644 index 00000000000..7003afc2a15 --- /dev/null +++ b/dep/efsw/src/efsw/FileInfo.cpp @@ -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); +} + +}  diff --git a/dep/efsw/src/efsw/FileInfo.hpp b/dep/efsw/src/efsw/FileInfo.hpp new file mode 100644 index 00000000000..45cca6a7953 --- /dev/null +++ b/dep/efsw/src/efsw/FileInfo.hpp @@ -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 + diff --git a/dep/efsw/src/efsw/FileSystem.cpp b/dep/efsw/src/efsw/FileSystem.cpp new file mode 100644 index 00000000000..e3afa0b4046 --- /dev/null +++ b/dep/efsw/src/efsw/FileSystem.cpp @@ -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 ); +} + +} diff --git a/dep/efsw/src/efsw/FileSystem.hpp b/dep/efsw/src/efsw/FileSystem.hpp new file mode 100644 index 00000000000..4e2e1aeb7cd --- /dev/null +++ b/dep/efsw/src/efsw/FileSystem.hpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcher.cpp b/dep/efsw/src/efsw/FileWatcher.cpp new file mode 100644 index 00000000000..e33d5ec46fb --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcher.cpp @@ -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; +} + +} diff --git a/dep/efsw/src/efsw/FileWatcherCWrapper.cpp b/dep/efsw/src/efsw/FileWatcherCWrapper.cpp new file mode 100644 index 00000000000..2739e756bb2 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherCWrapper.cpp @@ -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(); +} + diff --git a/dep/efsw/src/efsw/FileWatcherFSEvents.cpp b/dep/efsw/src/efsw/FileWatcherFSEvents.cpp new file mode 100644 index 00000000000..40156674132 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherFSEvents.cpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherFSEvents.hpp b/dep/efsw/src/efsw/FileWatcherFSEvents.hpp new file mode 100644 index 00000000000..6aafbc0b5ea --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherFSEvents.hpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherGeneric.cpp b/dep/efsw/src/efsw/FileWatcherGeneric.cpp new file mode 100644 index 00000000000..1534b6a9279 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherGeneric.cpp @@ -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; +} + +} diff --git a/dep/efsw/src/efsw/FileWatcherGeneric.hpp b/dep/efsw/src/efsw/FileWatcherGeneric.hpp new file mode 100644 index 00000000000..fc9826580ab --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherGeneric.hpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherImpl.cpp b/dep/efsw/src/efsw/FileWatcherImpl.cpp new file mode 100644 index 00000000000..e6e0fc72a13 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherImpl.cpp @@ -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 ); +} + +} diff --git a/dep/efsw/src/efsw/FileWatcherImpl.hpp b/dep/efsw/src/efsw/FileWatcherImpl.hpp new file mode 100644 index 00000000000..8f472bf56c5 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherImpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherInotify.cpp b/dep/efsw/src/efsw/FileWatcherInotify.cpp new file mode 100644 index 00000000000..19c20761663 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherInotify.cpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherInotify.hpp b/dep/efsw/src/efsw/FileWatcherInotify.hpp new file mode 100644 index 00000000000..43ee9ca6afc --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherInotify.hpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherKqueue.cpp b/dep/efsw/src/efsw/FileWatcherKqueue.cpp new file mode 100644 index 00000000000..600fd085b35 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherKqueue.cpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherKqueue.hpp b/dep/efsw/src/efsw/FileWatcherKqueue.hpp new file mode 100644 index 00000000000..0a2431e3777 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherKqueue.hpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherWin32.cpp b/dep/efsw/src/efsw/FileWatcherWin32.cpp new file mode 100644 index 00000000000..fe78dd122cf --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherWin32.cpp @@ -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 diff --git a/dep/efsw/src/efsw/FileWatcherWin32.hpp b/dep/efsw/src/efsw/FileWatcherWin32.hpp new file mode 100644 index 00000000000..505cd772b82 --- /dev/null +++ b/dep/efsw/src/efsw/FileWatcherWin32.hpp @@ -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 diff --git a/dep/efsw/src/efsw/Log.cpp b/dep/efsw/src/efsw/Log.cpp new file mode 100644 index 00000000000..8e2860ac099 --- /dev/null +++ b/dep/efsw/src/efsw/Log.cpp @@ -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; +} + +}} diff --git a/dep/efsw/src/efsw/Mutex.cpp b/dep/efsw/src/efsw/Mutex.cpp new file mode 100644 index 00000000000..b34ba066ee9 --- /dev/null +++ b/dep/efsw/src/efsw/Mutex.cpp @@ -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(); +} + +} diff --git a/dep/efsw/src/efsw/Mutex.hpp b/dep/efsw/src/efsw/Mutex.hpp new file mode 100644 index 00000000000..e6e89def175 --- /dev/null +++ b/dep/efsw/src/efsw/Mutex.hpp @@ -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 diff --git a/dep/efsw/src/efsw/String.cpp b/dep/efsw/src/efsw/String.cpp new file mode 100644 index 00000000000..f703d25cb35 --- /dev/null +++ b/dep/efsw/src/efsw/String.cpp @@ -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; +} + +} diff --git a/dep/efsw/src/efsw/String.hpp b/dep/efsw/src/efsw/String.hpp new file mode 100644 index 00000000000..ce7e3b75f89 --- /dev/null +++ b/dep/efsw/src/efsw/String.hpp @@ -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. +**/ diff --git a/dep/efsw/src/efsw/System.cpp b/dep/efsw/src/efsw/System.cpp new file mode 100644 index 00000000000..f8767cbc58f --- /dev/null +++ b/dep/efsw/src/efsw/System.cpp @@ -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(); +} + +}  diff --git a/dep/efsw/src/efsw/System.hpp b/dep/efsw/src/efsw/System.hpp new file mode 100644 index 00000000000..1d6f4a70f91 --- /dev/null +++ b/dep/efsw/src/efsw/System.hpp @@ -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 diff --git a/dep/efsw/src/efsw/Thread.cpp b/dep/efsw/src/efsw/Thread.cpp new file mode 100644 index 00000000000..fff41517dc1 --- /dev/null +++ b/dep/efsw/src/efsw/Thread.cpp @@ -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(); +} + +} diff --git a/dep/efsw/src/efsw/Thread.hpp b/dep/efsw/src/efsw/Thread.hpp new file mode 100644 index 00000000000..c7da9e8b33c --- /dev/null +++ b/dep/efsw/src/efsw/Thread.hpp @@ -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 diff --git a/dep/efsw/src/efsw/Utf.hpp b/dep/efsw/src/efsw/Utf.hpp new file mode 100644 index 00000000000..925a7cc149b --- /dev/null +++ b/dep/efsw/src/efsw/Utf.hpp @@ -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) +/// +//////////////////////////////////////////////////////////// diff --git a/dep/efsw/src/efsw/Utf.inl b/dep/efsw/src/efsw/Utf.inl new file mode 100644 index 00000000000..db8fd5d61ee --- /dev/null +++ b/dep/efsw/src/efsw/Utf.inl @@ -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 diff --git a/dep/efsw/src/efsw/Watcher.cpp b/dep/efsw/src/efsw/Watcher.cpp new file mode 100644 index 00000000000..d81c84228ad --- /dev/null +++ b/dep/efsw/src/efsw/Watcher.cpp @@ -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 ) +{ +} + +}  diff --git a/dep/efsw/src/efsw/Watcher.hpp b/dep/efsw/src/efsw/Watcher.hpp new file mode 100644 index 00000000000..5a35cb9a2ac --- /dev/null +++ b/dep/efsw/src/efsw/Watcher.hpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherFSEvents.cpp b/dep/efsw/src/efsw/WatcherFSEvents.cpp new file mode 100644 index 00000000000..3a9700c77ec --- /dev/null +++ b/dep/efsw/src/efsw/WatcherFSEvents.cpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherFSEvents.hpp b/dep/efsw/src/efsw/WatcherFSEvents.hpp new file mode 100644 index 00000000000..d4fc5c9a8d3 --- /dev/null +++ b/dep/efsw/src/efsw/WatcherFSEvents.hpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherGeneric.cpp b/dep/efsw/src/efsw/WatcherGeneric.cpp new file mode 100644 index 00000000000..94170d3ce87 --- /dev/null +++ b/dep/efsw/src/efsw/WatcherGeneric.cpp @@ -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 ); +} + +} diff --git a/dep/efsw/src/efsw/WatcherGeneric.hpp b/dep/efsw/src/efsw/WatcherGeneric.hpp new file mode 100644 index 00000000000..8794e921b40 --- /dev/null +++ b/dep/efsw/src/efsw/WatcherGeneric.hpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherInotify.cpp b/dep/efsw/src/efsw/WatcherInotify.cpp new file mode 100644 index 00000000000..741641bf43c --- /dev/null +++ b/dep/efsw/src/efsw/WatcherInotify.cpp @@ -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; +} +	 +}  diff --git a/dep/efsw/src/efsw/WatcherInotify.hpp b/dep/efsw/src/efsw/WatcherInotify.hpp new file mode 100644 index 00000000000..1caf399679b --- /dev/null +++ b/dep/efsw/src/efsw/WatcherInotify.hpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherKqueue.cpp b/dep/efsw/src/efsw/WatcherKqueue.cpp new file mode 100644 index 00000000000..8347fb53439 --- /dev/null +++ b/dep/efsw/src/efsw/WatcherKqueue.cpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherKqueue.hpp b/dep/efsw/src/efsw/WatcherKqueue.hpp new file mode 100644 index 00000000000..4babbe73354 --- /dev/null +++ b/dep/efsw/src/efsw/WatcherKqueue.hpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherWin32.cpp b/dep/efsw/src/efsw/WatcherWin32.cpp new file mode 100644 index 00000000000..01d7b0fcd99 --- /dev/null +++ b/dep/efsw/src/efsw/WatcherWin32.cpp @@ -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 diff --git a/dep/efsw/src/efsw/WatcherWin32.hpp b/dep/efsw/src/efsw/WatcherWin32.hpp new file mode 100644 index 00000000000..3c6d988fbf4 --- /dev/null +++ b/dep/efsw/src/efsw/WatcherWin32.hpp @@ -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 diff --git a/dep/efsw/src/efsw/base.hpp b/dep/efsw/src/efsw/base.hpp new file mode 100644 index 00000000000..26108c76985 --- /dev/null +++ b/dep/efsw/src/efsw/base.hpp @@ -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 diff --git a/dep/efsw/src/efsw/inotify-nosys.h b/dep/efsw/src/efsw/inotify-nosys.h new file mode 100644 index 00000000000..75df5d3ced3 --- /dev/null +++ b/dep/efsw/src/efsw/inotify-nosys.h @@ -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 */ diff --git a/dep/efsw/src/efsw/platform/platformimpl.hpp b/dep/efsw/src/efsw/platform/platformimpl.hpp new file mode 100644 index 00000000000..86a74eee0c8 --- /dev/null +++ b/dep/efsw/src/efsw/platform/platformimpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/posix/FileSystemImpl.cpp b/dep/efsw/src/efsw/platform/posix/FileSystemImpl.cpp new file mode 100644 index 00000000000..e061b25d56b --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/FileSystemImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/posix/FileSystemImpl.hpp b/dep/efsw/src/efsw/platform/posix/FileSystemImpl.hpp new file mode 100644 index 00000000000..865b3f8dfdf --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/FileSystemImpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/posix/MutexImpl.cpp b/dep/efsw/src/efsw/platform/posix/MutexImpl.cpp new file mode 100644 index 00000000000..6f2af5abc61 --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/MutexImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/posix/MutexImpl.hpp b/dep/efsw/src/efsw/platform/posix/MutexImpl.hpp new file mode 100644 index 00000000000..d51eecb16aa --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/MutexImpl.hpp @@ -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 +  diff --git a/dep/efsw/src/efsw/platform/posix/SystemImpl.cpp b/dep/efsw/src/efsw/platform/posix/SystemImpl.cpp new file mode 100644 index 00000000000..22e37095afd --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/SystemImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/posix/SystemImpl.hpp b/dep/efsw/src/efsw/platform/posix/SystemImpl.hpp new file mode 100644 index 00000000000..34734104467 --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/SystemImpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/posix/ThreadImpl.cpp b/dep/efsw/src/efsw/platform/posix/ThreadImpl.cpp new file mode 100644 index 00000000000..2d3671db9b8 --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/ThreadImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/posix/ThreadImpl.hpp b/dep/efsw/src/efsw/platform/posix/ThreadImpl.hpp new file mode 100644 index 00000000000..be6dc1b3e58 --- /dev/null +++ b/dep/efsw/src/efsw/platform/posix/ThreadImpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/win/FileSystemImpl.cpp b/dep/efsw/src/efsw/platform/win/FileSystemImpl.cpp new file mode 100644 index 00000000000..376a474e662 --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/FileSystemImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/win/FileSystemImpl.hpp b/dep/efsw/src/efsw/platform/win/FileSystemImpl.hpp new file mode 100644 index 00000000000..597edc4a647 --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/FileSystemImpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/win/MutexImpl.cpp b/dep/efsw/src/efsw/platform/win/MutexImpl.cpp new file mode 100644 index 00000000000..0c8c36d8b39 --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/MutexImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/win/MutexImpl.hpp b/dep/efsw/src/efsw/platform/win/MutexImpl.hpp new file mode 100644 index 00000000000..da1e20c5fa9 --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/MutexImpl.hpp @@ -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 +  diff --git a/dep/efsw/src/efsw/platform/win/SystemImpl.cpp b/dep/efsw/src/efsw/platform/win/SystemImpl.cpp new file mode 100644 index 00000000000..ddbe1e5c45c --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/SystemImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/win/SystemImpl.hpp b/dep/efsw/src/efsw/platform/win/SystemImpl.hpp new file mode 100644 index 00000000000..2f785e3565c --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/SystemImpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/win/ThreadImpl.cpp b/dep/efsw/src/efsw/platform/win/ThreadImpl.cpp new file mode 100644 index 00000000000..2fa30f30060 --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/ThreadImpl.cpp @@ -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 diff --git a/dep/efsw/src/efsw/platform/win/ThreadImpl.hpp b/dep/efsw/src/efsw/platform/win/ThreadImpl.hpp new file mode 100644 index 00000000000..506c659c675 --- /dev/null +++ b/dep/efsw/src/efsw/platform/win/ThreadImpl.hpp @@ -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 diff --git a/dep/efsw/src/efsw/sophist.h b/dep/efsw/src/efsw/sophist.h new file mode 100644 index 00000000000..3a645040e2f --- /dev/null +++ b/dep/efsw/src/efsw/sophist.h @@ -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__ */ diff --git a/dep/efsw/src/test/efsw-test.cpp b/dep/efsw/src/test/efsw-test.cpp new file mode 100644 index 00000000000..a49e3414fcc --- /dev/null +++ b/dep/efsw/src/test/efsw-test.cpp @@ -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; +} diff --git a/dep/g3dlite/CMakeLists.txt b/dep/g3dlite/CMakeLists.txt index f1166c72e6d..4e579951d63 100644 --- a/dep/g3dlite/CMakeLists.txt +++ b/dep/g3dlite/CMakeLists.txt @@ -8,7 +8,6 @@  # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -  set(g3dlib_STAT_SRCS    source/AABox.cpp    source/Any.cpp @@ -55,20 +54,18 @@ set(g3dlib_STAT_SRCS    source/Vector4.cpp  ) -if(WIN32) -  include_directories( -    ${CMAKE_CURRENT_SOURCE_DIR}/include -    ${CMAKE_SOURCE_DIR}/dep/zlib -  ) -else() -  include_directories( -    ${CMAKE_CURRENT_SOURCE_DIR}/include -  ) -endif() -  add_library(g3dlib STATIC ${g3dlib_STAT_SRCS}) +target_include_directories(g3dlib +  PUBLIC +    ${CMAKE_CURRENT_SOURCE_DIR}/include) +  target_link_libraries(g3dlib -  ${ZLIB_LIBRARIES} -  ${CMAKE_THREAD_LIBS_INIT} -) +  PUBLIC +    zlib +    threads) + +set_target_properties(g3dlib +    PROPERTIES +      FOLDER +        "dep") diff --git a/dep/gsoap/CMakeLists.txt b/dep/gsoap/CMakeLists.txt index b5fed7809af..bdcadf6e4dc 100644 --- a/dep/gsoap/CMakeLists.txt +++ b/dep/gsoap/CMakeLists.txt @@ -10,17 +10,22 @@  file(GLOB sources *.cpp *.h) -set(gsoap_STAT_SRCS -   ${sources} -) +add_library(gsoap STATIC ${sources}) -include_directories( -   ${CMAKE_CURRENT_SOURCE_DIR} -) +set_target_properties(gsoap PROPERTIES LINKER_LANGUAGE CXX) -# Little fix for MSVC / Windows platforms -add_definitions(-D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=0) +target_include_directories(gsoap +  PUBLIC +    ${CMAKE_CURRENT_SOURCE_DIR}) -add_library(gsoap STATIC ${gsoap_STAT_SRCS}) +set_target_properties(gsoap +    PROPERTIES +      FOLDER +        "dep") -set_target_properties(gsoap PROPERTIES LINKER_LANGUAGE CXX) +if (MSVC) +  # Little fix for MSVC / Windows platforms +  target_compile_definitions(gsoap +    PRIVATE +      -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=0) +endif() diff --git a/dep/jemalloc/CMakeLists.txt b/dep/jemalloc/CMakeLists.txt index cf0ac435f0a..6774e5a75d2 100644 --- a/dep/jemalloc/CMakeLists.txt +++ b/dep/jemalloc/CMakeLists.txt @@ -8,54 +8,79 @@  # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# We need to generate the jemalloc_def.h header based on platform-specific settings -if (PLATFORM EQUAL 32) -  set(JEM_SIZEDEF 2) -  set(JEM_TLSMODEL) +if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT NOJEM) +  # We need to generate the jemalloc_def.h header based on platform-specific settings +  if (PLATFORM EQUAL 32) +    set(JEM_SIZEDEF 2) +    set(JEM_TLSMODEL) +  else() +    set(JEM_SIZEDEF 3) +    set(JEM_TLSMODEL "__attribute__\(\(tls_model\(\"initial-exec\"\)\)\)") +  endif() + +  # Create the header, so we can use it +  configure_file( +    "${CMAKE_SOURCE_DIR}/dep/jemalloc/jemalloc_defs.h.in.cmake" +    "${BUILDDIR}/jemalloc_defs.h" +    @ONLY +  ) + +  # Done, let's continue +  set(jemalloc_STAT_SRC +    ${CMAKE_CURRENT_SOURCE_DIR}/src/arena.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/atomic.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/base.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/bitmap.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/chunk.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/chunk_dss.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/chunk_mmap.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/ckh.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/ctl.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/extent.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/hash.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/huge.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/jemalloc.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/mb.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/mutex.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/prof.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/quarantine.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/rtree.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/stats.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/tcache.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/tsd.c +    ${CMAKE_CURRENT_SOURCE_DIR}/src/util.c +  ) + +  add_library(jemalloc STATIC ${jemalloc_STAT_SRC}) + +  target_include_directories(jemalloc +    PRIVATE +      ${BUILDDIR} +      ${CMAKE_CURRENT_SOURCE_DIR}/include) + +  target_compile_definitions(jemalloc +    PUBLIC +      -DNO_BUFFERPOOL +    PRIVATE +      -D_GNU_SOURCE +      -D_REENTRAN) + +  target_link_libraries(jemalloc +    PUBLIC +      threads +      valgrind) + +  set_target_properties(jemalloc +      PROPERTIES +        FOLDER +          "dep") +  else() -  set(JEM_SIZEDEF 3) -  set(JEM_TLSMODEL "__attribute__\(\(tls_model\(\"initial-exec\"\)\)\)") -endif() +  # Provide a dummy target for jemalloc which is used when jemalloc +  # is disabled or not supported. +  add_library(jemalloc INTERFACE) +  target_link_libraries(jemalloc +    INTERFACE +      valgrind) -# Create the header, so we can use it -configure_file( -  "${CMAKE_SOURCE_DIR}/dep/jemalloc/jemalloc_defs.h.in.cmake" -  "${BUILDDIR}/jemalloc_defs.h" -  @ONLY -) - -# Done, let's continue -set(jemalloc_STAT_SRC -  ${CMAKE_CURRENT_SOURCE_DIR}/src/arena.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/atomic.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/base.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/bitmap.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/chunk.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/chunk_dss.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/chunk_mmap.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/ckh.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/ctl.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/extent.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/hash.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/huge.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/jemalloc.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/mb.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/mutex.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/prof.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/quarantine.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/rtree.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/stats.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/tcache.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/tsd.c -  ${CMAKE_CURRENT_SOURCE_DIR}/src/util.c -) - -include_directories( -  ${BUILDDIR}/ -  ${CMAKE_CURRENT_SOURCE_DIR}/include -  ${VALGRIND_INCLUDE_DIR} -) - -add_definitions(-D_GNU_SOURCE -D_REENTRANT) - -add_library(jemalloc STATIC ${jemalloc_STAT_SRC}) +endif() diff --git a/dep/libmpq/CMakeLists.txt b/dep/libmpq/CMakeLists.txt index 1213e6b11b7..e8b420fca53 100644 --- a/dep/libmpq/CMakeLists.txt +++ b/dep/libmpq/CMakeLists.txt @@ -8,27 +8,29 @@  # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -file(GLOB sources_mpq libmpq/*.c libmpq/*.h) +file(GLOB sources libmpq/*.c libmpq/*.h) -set(mpq_STAT_SRCS -  ${sources_mpq} -) +add_library(mpq STATIC ${sources}) -if( UNIX ) -  include_directories( -    ${CMAKE_CURRENT_SOURCE_DIR} -    ${CMAKE_SOURCE_DIR}/dep/zlib -    ${CMAKE_SOURCE_DIR}/dep/bzip2 -  ) -elseif( WIN32 ) -  include_directories( +set_target_properties(mpq PROPERTIES LINKER_LANGUAGE CXX) + +if(WIN32) +  set(WIN_EXTRA_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/win) +endif() + +target_include_directories(mpq +  PUBLIC      ${CMAKE_CURRENT_SOURCE_DIR} -    ${CMAKE_CURRENT_SOURCE_DIR}/win +    ${WIN_EXTRA_INCLUDE}      ${CMAKE_SOURCE_DIR}/dep/zlib -    ${CMAKE_SOURCE_DIR}/dep/bzip2 -  ) -endif() +    ${CMAKE_SOURCE_DIR}/dep/bzip2) -add_library(mpq STATIC ${mpq_STAT_SRCS}) +target_link_libraries(mpq +  PUBLIC +    zlib +    bzip2) -set_target_properties(mpq PROPERTIES LINKER_LANGUAGE CXX) +set_target_properties(mpq +    PROPERTIES +      FOLDER +        "dep") diff --git a/dep/mysql/CMakeLists.txt b/dep/mysql/CMakeLists.txt new file mode 100644 index 00000000000..472535b0356 --- /dev/null +++ b/dep/mysql/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +if (NOT MYSQL_FOUND) +  message(FATAL_ERROR "MySQL wasn't found on your system but it's required to build the servers!") +endif() + +add_library(mysql STATIC IMPORTED GLOBAL) + +set_target_properties(mysql +  PROPERTIES +    IMPORTED_LOCATION +      "${MYSQL_LIBRARY}" +    INTERFACE_INCLUDE_DIRECTORIES +      "${MYSQL_INCLUDE_DIR}") diff --git a/dep/openssl/CMakeLists.txt b/dep/openssl/CMakeLists.txt new file mode 100644 index 00000000000..98561b2a0ed --- /dev/null +++ b/dep/openssl/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + +# basic packagesearching and setup +# (further support will be needed, this is a preliminary release!) +set(OPENSSL_EXPECTED_VERSION 1.0.0) + +find_package(OpenSSL REQUIRED) + +add_library(openssl INTERFACE) + +target_link_libraries(openssl +  INTERFACE +    ${OPENSSL_LIBRARIES}) + +target_include_directories(openssl +  INTERFACE +    ${OPENSSL_INCLUDE_DIR}) diff --git a/dep/process/CMakeLists.txt b/dep/process/CMakeLists.txt new file mode 100644 index 00000000000..5a51917a00c --- /dev/null +++ b/dep/process/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +add_library(process INTERFACE) + +target_include_directories(process +  INTERFACE +    ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(process +  INTERFACE +    boost) diff --git a/dep/readline/CMakeLists.txt b/dep/readline/CMakeLists.txt new file mode 100644 index 00000000000..0e8679ba718 --- /dev/null +++ b/dep/readline/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +if( UNIX ) +  # find Readline (terminal input library) includes and library +  # +  # READLINE_INCLUDE_DIR - where the directory containing the READLINE headers can be found +  # READLINE_LIBRARY     - full path to the READLINE library +  find_path(READLINE_INCLUDE_DIR readline/readline.h) +  find_library(READLINE_LIBRARY NAMES readline) + +  message(STATUS "Found Readline library: ${READLINE_LIBRARY}") +  message(STATUS "Include dir is: ${READLINE_INCLUDE_DIR}") + +  if (NOT READLINE_INCLUDE_DIR OR NOT READLINE_LIBRARY) +    message(FATAL_ERROR "** Readline library not found!\n** Your distro may provide a binary for Readline e.g. for ubuntu try apt-get install libreadline5-dev") +  endif () + +  add_library(readline SHARED IMPORTED GLOBAL) + +  set_target_properties(readline +    PROPERTIES +      IMPORTED_LOCATION +        "${READLINE_LIBRARY}" +      INTERFACE_INCLUDE_DIRECTORIES +        "${READLINE_INCLUDE_DIR}") + +else() +  # Provide a dummy target +  add_library(readline INTERFACE) +endif() diff --git a/dep/recastnavigation/Detour/CMakeLists.txt b/dep/recastnavigation/Detour/CMakeLists.txt index b21e4ca6273..12be71d32b4 100644 --- a/dep/recastnavigation/Detour/CMakeLists.txt +++ b/dep/recastnavigation/Detour/CMakeLists.txt @@ -16,14 +16,18 @@ set(Detour_STAT_SRCS      Source/DetourNavMeshQuery.cpp       Source/DetourNode.cpp   ) -include_directories(Include) - -if(WIN32) -  include_directories( -    ${CMAKE_SOURCE_DIR}/dep/zlib -  ) -endif()  add_library(Detour STATIC ${Detour_STAT_SRCS}) -target_link_libraries(Detour ${ZLIB_LIBRARIES}) +target_include_directories(Detour +  PUBLIC +    ${CMAKE_CURRENT_SOURCE_DIR}/Include) + +target_link_libraries(Detour +  PUBLIC +    zlib) + +set_target_properties(Detour +    PROPERTIES +      FOLDER +        "dep") diff --git a/dep/recastnavigation/Recast/CMakeLists.txt b/dep/recastnavigation/Recast/CMakeLists.txt index 738c010eb05..1eac4e75399 100644 --- a/dep/recastnavigation/Recast/CMakeLists.txt +++ b/dep/recastnavigation/Recast/CMakeLists.txt @@ -14,21 +14,24 @@ set(Recast_STAT_SRCS      Source/RecastArea.cpp       Source/RecastContour.cpp       Source/RecastFilter.cpp  -	Source/RecastLayers.cpp +    Source/RecastLayers.cpp      Source/RecastMesh.cpp       Source/RecastMeshDetail.cpp       Source/RecastRasterization.cpp       Source/RecastRegion.cpp   ) -include_directories(Include) +add_library(Recast STATIC ${Recast_STAT_SRCS}) -if(WIN32) -  include_directories( -    ${CMAKE_SOURCE_DIR}/dep/zlib -  ) -endif() +target_include_directories(Recast +  PUBLIC +    ${CMAKE_CURRENT_SOURCE_DIR}/Include) -add_library(Recast STATIC ${Recast_STAT_SRCS}) +target_link_libraries(Recast +  PUBLIC +    zlib) -target_link_libraries(Recast ${ZLIB_LIBRARIES})
\ No newline at end of file +set_target_properties(Recast +    PROPERTIES +      FOLDER +        "dep") diff --git a/dep/threads/CMakeLists.txt b/dep/threads/CMakeLists.txt new file mode 100644 index 00000000000..48e5eb00723 --- /dev/null +++ b/dep/threads/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +find_package(Threads REQUIRED) + +add_library(threads INTERFACE) +target_link_libraries(threads +  INTERFACE +    ${CMAKE_THREAD_LIBS_INIT}) diff --git a/dep/utf8cpp/CMakeLists.txt b/dep/utf8cpp/CMakeLists.txt new file mode 100644 index 00000000000..edf8604d44c --- /dev/null +++ b/dep/utf8cpp/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +add_library(utf8cpp INTERFACE) + +target_include_directories(utf8cpp +  INTERFACE +    ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/dep/valgrind/CMakeLists.txt b/dep/valgrind/CMakeLists.txt new file mode 100644 index 00000000000..d67cd33b426 --- /dev/null +++ b/dep/valgrind/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +add_library(valgrind INTERFACE) + +target_include_directories(valgrind +  INTERFACE +    "${VALGRIND_INCLUDE_DIR}") diff --git a/dep/zlib/CMakeLists.txt b/dep/zlib/CMakeLists.txt index 7feb134bcd5..b3e3d58fe55 100644 --- a/dep/zlib/CMakeLists.txt +++ b/dep/zlib/CMakeLists.txt @@ -8,22 +8,43 @@  # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -SET(zlib_STAT_SRCS -  adler32.c                                                                                                                                            -  compress.c                                                                                                                                           -  crc32.c                                                                                                                                              -  deflate.c                                                                                                                                            -  infback.c                                                                                                                                            -  inffast.c                                                                                                                                            -  inflate.c                                                                                                                                            -  inftrees.c                                                                                                                                           -  trees.c                                                                                                                                              -  uncompr.c                                                                                                                                            -  zutil.c    -) +if(UNIX) +  # Look for an installed zlib on unix +  find_package(ZLIB REQUIRED) -include_directories( -  ${CMAKE_CURRENT_SOURCE_DIR} -) +  add_library(zlib SHARED IMPORTED GLOBAL) -add_library(zlib STATIC ${zlib_STAT_SRCS}) +  set_target_properties(zlib +    PROPERTIES +      IMPORTED_LOCATION +        "${ZLIB_LIBRARIES}" +      INTERFACE_INCLUDE_DIRECTORIES +        "${ZLIB_INCLUDE_DIRS}") +else() +  # Use the bundled source on windows +  SET(zlib_STAT_SRCS +    adler32.c +    compress.c +    crc32.c +    deflate.c +    infback.c +    inffast.c +    inflate.c +    inftrees.c +    trees.c +    uncompr.c +    zutil.c +  ) + +  add_library(zlib STATIC +    ${zlib_STAT_SRCS}) + +  target_include_directories(zlib +    PUBLIC +      ${CMAKE_CURRENT_SOURCE_DIR}) + +  set_target_properties(zlib +      PROPERTIES +        FOLDER +          "dep") +endif() | 
