diff --git a/.travis.yml b/.travis.yml index 5fd85ab..0128bbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -262,8 +262,14 @@ script: else false fi + # DYLD_LIBRARY_PATH causes problems on system() spawned processes + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + LINK=static + else + LINK=static,shared + fi - which $TRAVIS_COMPILER - $TRAVIS_COMPILER --version - | echo "using $TOOLSET : : $TRAVIS_COMPILER ;" > ~/user-config.jam - - ./b2 -j3 libs/nowide/test toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${UBSAN:+cxxflags=-fsanitize=undefined cxxflags=-fno-sanitize-recover=undefined linkflags=-fsanitize=undefined define=UBSAN=1 debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS} ${VISIBILITY:+visibility=$VISIBILITY} + - ./b2 -j3 libs/nowide/test toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release link=$LINK ${UBSAN:+cxxflags=-fsanitize=undefined cxxflags=-fno-sanitize-recover=undefined linkflags=-fsanitize=undefined define=UBSAN=1 debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS} ${VISIBILITY:+visibility=$VISIBILITY} diff --git a/CMakeLists.txt b/CMakeLists.txt index 42e31cd..2529b14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ endif() # Using glob here is ok as it is only for headers file(GLOB_RECURSE headers include/*.hpp) -add_library(boost_nowide src/iostream.cpp ${headers}) +add_library(boost_nowide src/cstdio.cpp src/cstdlib.cpp src/iostream.cpp ${headers}) add_library(Boost::nowide ALIAS boost_nowide) set_target_properties(boost_nowide PROPERTIES CXX_VISIBILITY_PRESET hidden diff --git a/appveyor.yml b/appveyor.yml index 48760af..50cbe79 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,7 @@ branches: - /feature\/.*/ matrix: - fast_finish: true + fast_finish: false environment: matrix: @@ -81,7 +81,7 @@ test_script: - PATH=%ADDPATH%%PATH% - if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD% - if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD% - - b2 -j3 libs/nowide/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release + - b2 -j3 libs/nowide/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release link=shared,static - ps: | If ("$env:BOOST_CMAKE" -eq "1") { ./b2 --clean diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 95d5632..639492d 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -14,7 +14,7 @@ project boost/nowide shared:BOOST_NOWIDE_DYN_LINK=1 ; -SOURCES = iostream ; +SOURCES = cstdio cstdlib iostream ; lib boost_nowide : $(SOURCES).cpp diff --git a/include/boost/nowide/config.hpp b/include/boost/nowide/config.hpp index ab14944..15f24b5 100644 --- a/include/boost/nowide/config.hpp +++ b/include/boost/nowide/config.hpp @@ -46,22 +46,31 @@ #include #endif // auto-linking disabled -/// @def BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS -/// @brief Define to 1 to use internal classes from fstream.hpp +/// @def BOOST_NOWIDE_USE_WCHAR_OVERLOADS +/// @brief Whether to use the wchar_t* overloads in fstream/filebuf +/// Enabled on Windows and Cygwin as the latter may use wchar_t in filesystem::path +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#define BOOST_NOWIDE_USE_WCHAR_OVERLOADS 1 +#else +#define BOOST_NOWIDE_USE_WCHAR_OVERLOADS 0 +#endif + +/// @def BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +/// @brief Define to 1 to use internal class from filebuf.hpp /// -/// - On Non-Windows platforms: Define to 1 to use the same classes from header -/// that are used on Windows. +/// - On Non-Windows platforms: Define to 1 to use the same class from header +/// that is used on Windows. /// - On Windows: No effect, always overwritten to 1 /// /// Affects boost::nowide::basic_filebuf, /// boost::nowide::basic_ofstream, boost::nowide::basic_ifstream, boost::nowide::basic_fstream -#if defined(BOOST_WINDOWS) -#ifdef BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS -#undef BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS +#if defined(BOOST_WINDOWS) || BOOST_NOWIDE_USE_WCHAR_OVERLOADS +#ifdef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +#undef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT #endif -#define BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS 1 -#elif !defined(BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS) -#define BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS 0 +#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT 1 +#elif !defined(BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT) +#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT 0 #endif #if BOOST_VERSION < 106500 && defined(BOOST_GCC) && __GNUC__ >= 7 diff --git a/include/boost/nowide/cstdio.hpp b/include/boost/nowide/cstdio.hpp index 7a313ad..bf48045 100644 --- a/include/boost/nowide/cstdio.hpp +++ b/include/boost/nowide/cstdio.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2020 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -8,18 +9,9 @@ #ifndef BOOST_NOWIDE_CSTDIO_HPP_INCLUDED #define BOOST_NOWIDE_CSTDIO_HPP_INCLUDED -#include - -#ifdef BOOST_WINDOWS -#include -#endif +#include #include -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable : 4996) -#endif - namespace boost { namespace nowide { #if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) @@ -32,43 +24,24 @@ namespace nowide { /// /// \brief Same as freopen but file_name and mode are UTF-8 strings /// - inline FILE* freopen(const char* file_name, const char* mode, FILE* stream) - { - const wstackstring wname(file_name); - const wshort_stackstring wmode(mode); - return _wfreopen(wname.get(), wmode.get(), stream); - } + BOOST_NOWIDE_DECL FILE* freopen(const char* file_name, const char* mode, FILE* stream); /// /// \brief Same as fopen but file_name and mode are UTF-8 strings /// - inline FILE* fopen(const char* file_name, const char* mode) - { - const wstackstring wname(file_name); - const wshort_stackstring wmode(mode); - return _wfopen(wname.get(), wmode.get()); - } + BOOST_NOWIDE_DECL FILE* fopen(const char* file_name, const char* mode); /// /// \brief Same as rename but old_name and new_name are UTF-8 strings /// - inline int rename(const char* old_name, const char* new_name) - { - const wstackstring wold(old_name), wnew(new_name); - return _wrename(wold.get(), wnew.get()); - } + BOOST_NOWIDE_DECL int rename(const char* old_name, const char* new_name); /// /// \brief Same as rename but name is UTF-8 string /// - inline int remove(const char* name) - { - const wstackstring wname(name); - return _wremove(wname.get()); - } + BOOST_NOWIDE_DECL int remove(const char* name); #endif + namespace detail { + BOOST_NOWIDE_DECL FILE* wfopen(const wchar_t* filename, const wchar_t* mode); + } } // namespace nowide } // namespace boost -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif - #endif diff --git a/include/boost/nowide/cstdlib.hpp b/include/boost/nowide/cstdlib.hpp index 3385fbb..657222e 100644 --- a/include/boost/nowide/cstdlib.hpp +++ b/include/boost/nowide/cstdlib.hpp @@ -8,12 +8,8 @@ #ifndef BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED #define BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED -#include -#ifdef BOOST_WINDOWS -#include -#include -#include -#else +#include +#if !BOOST_WINDOWS #include #endif @@ -21,9 +17,6 @@ namespace boost { namespace nowide { #if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) using std::getenv; - using ::setenv; - using ::unsetenv; - using ::putenv; using std::system; #else /// @@ -31,91 +24,43 @@ namespace nowide { /// /// This function is not thread safe or reenterable as defined by the standard library /// - inline char* getenv(const char* key) - { - static stackstring value; - - const wshort_stackstring name(key); - - static const size_t buf_size = 64; - wchar_t buf[buf_size]; - std::vector tmp; - wchar_t* ptr = buf; - size_t n = GetEnvironmentVariableW(name.get(), buf, buf_size); - if(n == 0 && GetLastError() == 203) // ERROR_ENVVAR_NOT_FOUND - return 0; - if(n >= buf_size) - { - tmp.resize(n + 1, L'\0'); - n = GetEnvironmentVariableW(name.get(), &tmp[0], static_cast(tmp.size() - 1)); - // The size may have changed - if(n >= tmp.size() - 1) - return 0; - ptr = &tmp[0]; - } - value.convert(ptr); - return value.get(); - } - /// - /// \brief UTF-8 aware setenv, \a key - the variable name, \a value is a new UTF-8 value, - /// - /// if overwrite is not 0, that the old value is always overwritten, otherwise, - /// if the variable exists it remains unchanged - /// - inline int setenv(const char* key, const char* value, int overwrite) - { - const wshort_stackstring name(key); - if(!overwrite) - { - wchar_t unused[2]; - if(GetEnvironmentVariableW(name.get(), unused, 2) != 0 || GetLastError() != 203) // ERROR_ENVVAR_NOT_FOUND - return 0; - } - const wstackstring wval(value); - if(SetEnvironmentVariableW(name.get(), wval.get())) - return 0; - return -1; - } - /// - /// \brief Remove environment variable \a key - /// - inline int unsetenv(const char* key) - { - const wshort_stackstring name(key); - if(SetEnvironmentVariableW(name.get(), 0)) - return 0; - return -1; - } - /// - /// \brief UTF-8 aware putenv implementation, expects string in format KEY=VALUE - /// - inline int putenv(char* string) - { - const char* key = string; - const char* key_end = string; - while(*key_end != '=' && *key_end != '\0') - key_end++; - if(*key_end == '\0') - return -1; - const wshort_stackstring wkey(key, key_end); - const wstackstring wvalue(key_end + 1); - - if(SetEnvironmentVariableW(wkey.get(), wvalue.get())) - return 0; - return -1; - } + BOOST_NOWIDE_DECL char* getenv(const char* key); /// /// Same as std::system but cmd is UTF-8. /// - inline int system(const char* cmd) - { - if(!cmd) - return _wsystem(0); - const wstackstring wcmd(cmd); - return _wsystem(wcmd.get()); - } + BOOST_NOWIDE_DECL int system(const char* cmd); + #endif + /// + /// \brief Set environment variable \a key to \a value + /// + /// if overwrite is not 0, that the old value is always overwritten, otherwise, + /// if the variable exists it remains unchanged + /// + /// \a key and \a value are UTF-8 on Windows + /// \return zero on success, else nonzero + /// + BOOST_NOWIDE_DECL int setenv(const char* key, const char* value, int overwrite); + + /// + /// \brief Remove environment variable \a key + /// + /// \a key is UTF-8 on Windows + /// \return zero on success, else nonzero + /// + BOOST_NOWIDE_DECL int unsetenv(const char* key); + + /// + /// \brief Adds or changes an environment variable, \a string must be in format KEY=VALUE + /// + /// \a string MAY become part of the environment, hence changes to the value MAY change + /// the environment. For portability it is hence recommended NOT to change it. + /// \a string is UTF-8 on Windows + /// \return zero on success, else nonzero + /// + BOOST_NOWIDE_DECL int putenv(char* string); + } // namespace nowide } // namespace boost diff --git a/include/boost/nowide/filebuf.hpp b/include/boost/nowide/filebuf.hpp index 4fbe428..aa4c1be 100644 --- a/include/boost/nowide/filebuf.hpp +++ b/include/boost/nowide/filebuf.hpp @@ -1,6 +1,6 @@ // // Copyright (c) 2012 Artyom Beilis (Tonkikh) -// Copyright (c) 2019 Alexander Grund +// Copyright (c) 2019-2020 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -10,7 +10,8 @@ #define BOOST_NOWIDE_FILEBUF_HPP_INCLUDED #include -#if BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS +#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +#include #include #include #include @@ -23,18 +24,12 @@ #include #endif -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable : 4996 4244 4800) -#endif - namespace boost { namespace nowide { -#if !BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS && !defined(BOOST_NOWIDE_DOXYGEN) +#if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT && !defined(BOOST_NOWIDE_DOXYGEN) using std::basic_filebuf; using std::filebuf; #else // Windows - /// /// \brief This forward declaration defines the basic_filebuf type. /// @@ -97,20 +92,13 @@ namespace nowide { if(is_open()) return NULL; validate_cvt(this->getloc()); - const bool ate = bool(mode & std::ios_base::ate); + const bool ate = (mode & std::ios_base::ate) != 0; if(ate) - mode = mode ^ std::ios_base::ate; + mode &= ~std::ios_base::ate; const wchar_t* smode = get_mode(mode); if(!smode) return 0; -#ifdef BOOST_WINDOWS - file_ = ::_wfopen(s, smode); -#else - const stackstring name(s); - const short_stackstring smode2(smode); - file_ = std::fopen(name.get(), smode2.get()); -#endif - + file_ = detail::wfopen(s, smode); if(!file_) return 0; if(ate && std::fseek(file_, 0, SEEK_END) != 0) @@ -177,7 +165,7 @@ namespace nowide { if(owns_buffer_) delete[] buffer_; buffer_ = s; - buffer_size_ = (n >= 0) ? n : 0; + buffer_size_ = (n >= 0) ? static_cast(n) : 0; return this; } @@ -197,7 +185,7 @@ namespace nowide { setp(buffer_, buffer_ + buffer_size_); if(c != EOF) { - *buffer_ = c; + *buffer_ = Traits::to_char_type(c); pbump(1); } } else if(c != EOF) @@ -206,7 +194,7 @@ namespace nowide { { make_buffer(); setp(buffer_, buffer_ + buffer_size_); - *buffer_ = c; + *buffer_ = Traits::to_char_type(c); pbump(1); } else if(std::fputc(c, file_) == EOF) { @@ -247,7 +235,7 @@ namespace nowide { const int c = std::fgetc(file_); if(c == EOF) return EOF; - last_char_ = c; + last_char_ = Traits::to_char_type(c); setg(&last_char_, &last_char_, &last_char_ + 1); } else { @@ -305,7 +293,8 @@ namespace nowide { case std::ios_base::end: whence = SEEK_END; break; default: assert(false); return EOF; } - if(std::fseek(file_, off, whence) != 0) + assert(off <= std::numeric_limits::max()); + if(std::fseek(file_, static_cast(off), whence) != 0) return EOF; return std::ftell(file_); } @@ -330,7 +319,7 @@ namespace nowide { const std::streamsize off = gptr() - egptr(); setg(0, 0, 0); assert(off <= std::numeric_limits::max()); - if(off && std::fseek(file_, off, SEEK_CUR) != 0) + if(off && std::fseek(file_, static_cast(off), SEEK_CUR) != 0) return false; } return true; @@ -427,8 +416,4 @@ namespace nowide { } // namespace nowide } // namespace boost -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif - #endif diff --git a/include/boost/nowide/fstream.hpp b/include/boost/nowide/fstream.hpp index 0d22fa8..754c528 100644 --- a/include/boost/nowide/fstream.hpp +++ b/include/boost/nowide/fstream.hpp @@ -77,7 +77,7 @@ namespace nowide { { open(file_name, mode); } -#ifdef BOOST_WINDOWS +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS explicit basic_ifstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::in) { open(file_name, mode); @@ -118,7 +118,7 @@ namespace nowide { { open(file_name, mode); } -#ifdef BOOST_WINDOWS +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS explicit basic_ofstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::out) { open(file_name, mode); @@ -162,7 +162,7 @@ namespace nowide { { open(file_name, mode); } -#ifdef BOOST_WINDOWS +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS explicit basic_fstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { @@ -187,9 +187,6 @@ namespace nowide { using fstream_impl::close; using fstream_impl::rdbuf; }; -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif /// /// Same as std::filebuf but accepts UTF-8 strings under Windows @@ -225,6 +222,7 @@ namespace nowide { public T_StreamType::template stream_base::type { typedef basic_filebuf internal_buffer_type; + typedef buf_holder base_buf_holder; typedef typename T_StreamType::template stream_base::type stream_base; public: @@ -232,7 +230,9 @@ namespace nowide { using stream_base::clear; protected: - fstream_impl() : stream_base(rdbuf()) + using base_buf_holder::buf_; + + fstream_impl() : stream_base(&buf_) {} void open(const std::string& file_name, std::ios_base::openmode mode = T_StreamType::mode()) @@ -243,7 +243,7 @@ namespace nowide { typename detail::enable_if_path::type open(const Path& file_name, std::ios_base::openmode mode = T_StreamType::mode()) { - return open(file_name.c_str(), mode); + open(file_name.c_str(), mode); } void open(const char* file_name, std::ios_base::openmode mode = T_StreamType::mode()) { @@ -252,7 +252,7 @@ namespace nowide { else clear(); } -#ifdef BOOST_WINDOWS +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS void open(const wchar_t* file_name, std::ios_base::openmode mode = T_StreamType::mode()) { if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier())) @@ -277,10 +277,12 @@ namespace nowide { internal_buffer_type* rdbuf() const { - return const_cast(&this->buf_); + return const_cast(&buf_); } }; - +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif /// Trait to heuristically check for a *::filesystem::path /// Done by checking for make_preferred and filename member functions with correct signature template diff --git a/include/boost/nowide/stackstring.hpp b/include/boost/nowide/stackstring.hpp index 555f733..39de405 100644 --- a/include/boost/nowide/stackstring.hpp +++ b/include/boost/nowide/stackstring.hpp @@ -34,50 +34,6 @@ namespace nowide { typedef CharOut output_char; typedef CharIn input_char; - basic_stackstring(const basic_stackstring& other) : data_(NULL) - { - *this = other; - } - - friend void swap(basic_stackstring& lhs, basic_stackstring& rhs) - { - if(lhs.uses_stack_memory()) - { - if(rhs.uses_stack_memory()) - { - for(size_t i = 0; i < buffer_size; i++) - std::swap(lhs.buffer_[i], rhs.buffer_[i]); - } else - { - lhs.data_ = rhs.data_; - rhs.data_ = rhs.buffer_; - for(size_t i = 0; i < buffer_size; i++) - rhs.buffer_[i] = lhs.buffer_[i]; - } - } else if(rhs.uses_stack_memory()) - { - rhs.data_ = lhs.data_; - lhs.data_ = lhs.buffer_; - for(size_t i = 0; i < buffer_size; i++) - lhs.buffer_[i] = rhs.buffer_[i]; - } else - std::swap(lhs.data_, rhs.data_); - } - basic_stackstring& operator=(const basic_stackstring& other) - { - if(this != &other) - { - clear(); - const size_t len = other.length(); - if(other.uses_stack_memory()) - data_ = buffer_; - else - data_ = new output_char[len + 1]; - std::memcpy(data_, other.data_, sizeof(output_char) * (len + 1)); - } - return *this; - } - basic_stackstring() : data_(NULL) { buffer_[0] = 0; @@ -90,6 +46,36 @@ namespace nowide { { convert(begin, end); } + + basic_stackstring(const basic_stackstring& other) : data_(NULL) + { + *this = other; + } + basic_stackstring& operator=(const basic_stackstring& other) + { + if(this != &other) + { + clear(); + const size_t len = other.length(); + if(other.uses_stack_memory()) + data_ = buffer_; + else if(other.data_) + data_ = new output_char[len + 1]; + else + { + data_ = NULL; + return *this; + } + std::memcpy(data_, other.data_, sizeof(output_char) * (len + 1)); + } + return *this; + } + + ~basic_stackstring() + { + clear(); + } + output_char* convert(const input_char* input) { if(input) @@ -132,9 +118,30 @@ namespace nowide { delete[] data_; data_ = NULL; } - ~basic_stackstring() + + friend void swap(basic_stackstring& lhs, basic_stackstring& rhs) { - clear(); + if(lhs.uses_stack_memory()) + { + if(rhs.uses_stack_memory()) + { + for(size_t i = 0; i < buffer_size; i++) + std::swap(lhs.buffer_[i], rhs.buffer_[i]); + } else + { + lhs.data_ = rhs.data_; + rhs.data_ = rhs.buffer_; + for(size_t i = 0; i < buffer_size; i++) + rhs.buffer_[i] = lhs.buffer_[i]; + } + } else if(rhs.uses_stack_memory()) + { + rhs.data_ = lhs.data_; + lhs.data_ = lhs.buffer_; + for(size_t i = 0; i < buffer_size; i++) + lhs.buffer_[i] = rhs.buffer_[i]; + } else + std::swap(lhs.data_, rhs.data_); } private: diff --git a/src/cstdio.cpp b/src/cstdio.cpp new file mode 100644 index 0000000..0b0e75c --- /dev/null +++ b/src/cstdio.cpp @@ -0,0 +1,74 @@ +// +// Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#define BOOST_NOWIDE_SOURCE + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#elif(defined(__MINGW32__) || defined(__CYGWIN__)) && defined(__STRICT_ANSI__) +// Need the _w* functions which are extensions on MinGW/Cygwin +#undef __STRICT_ANSI__ +#endif + +#include +#include + +namespace boost { +namespace nowide { + namespace detail { + FILE* wfopen(const wchar_t* filename, const wchar_t* mode) + { +#ifdef BOOST_WINDOWS + return ::_wfopen(filename, mode); +#else + const stackstring name(filename); + const short_stackstring smode2(mode); + return std::fopen(name.get(), smode2.get()); +#endif + } + } // namespace detail + +#ifdef BOOST_WINDOWS + /// + /// \brief Same as freopen but file_name and mode are UTF-8 strings + /// + FILE* freopen(const char* file_name, const char* mode, FILE* stream) + { + const wstackstring wname(file_name); + const wshort_stackstring wmode(mode); + return _wfreopen(wname.get(), wmode.get(), stream); + } + /// + /// \brief Same as fopen but file_name and mode are UTF-8 strings + /// + FILE* fopen(const char* file_name, const char* mode) + { + const wstackstring wname(file_name); + const wshort_stackstring wmode(mode); + return _wfopen(wname.get(), wmode.get()); + } + /// + /// \brief Same as rename but old_name and new_name are UTF-8 strings + /// + int rename(const char* old_name, const char* new_name) + { + const wstackstring wold(old_name), wnew(new_name); + return _wrename(wold.get(), wnew.get()); + } + /// + /// \brief Same as rename but name is UTF-8 string + /// + int remove(const char* name) + { + const wstackstring wname(name); + return _wremove(wname.get()); + } +#endif +} // namespace nowide +} // namespace boost diff --git a/src/cstdlib.cpp b/src/cstdlib.cpp new file mode 100644 index 0000000..8fbda07 --- /dev/null +++ b/src/cstdlib.cpp @@ -0,0 +1,121 @@ +// +// Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#define BOOST_NOWIDE_SOURCE + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#elif(defined(__MINGW32__) || defined(__CYGWIN__)) && defined(__STRICT_ANSI__) +// Need the _w* functions which are extensions on MinGW/Cygwin +#undef __STRICT_ANSI__ +#endif + +#include + +#if !BOOST_WINDOWS +namespace boost { +namespace nowide { + int setenv(const char* key, const char* value, int overwrite) + { + return ::setenv(key, value, overwrite); + } + + int unsetenv(const char* key) + { + return ::unsetenv(key); + } + + int putenv(char* string) + { + return ::putenv(string); + } +} // namespace nowide +} // namespace boost +#else +#include +#include +#include + +namespace boost { +namespace nowide { + char* getenv(const char* key) + { + static stackstring value; + + const wshort_stackstring name(key); + + static const size_t buf_size = 64; + wchar_t buf[buf_size]; + std::vector tmp; + wchar_t* ptr = buf; + size_t n = GetEnvironmentVariableW(name.get(), buf, buf_size); + if(n == 0 && GetLastError() == 203) // ERROR_ENVVAR_NOT_FOUND + return 0; + if(n >= buf_size) + { + tmp.resize(n + 1, L'\0'); + n = GetEnvironmentVariableW(name.get(), &tmp[0], static_cast(tmp.size() - 1)); + // The size may have changed + if(n >= tmp.size() - 1) + return 0; + ptr = &tmp[0]; + } + value.convert(ptr); + return value.get(); + } + + int setenv(const char* key, const char* value, int overwrite) + { + const wshort_stackstring name(key); + if(!overwrite) + { + wchar_t unused[2]; + if(GetEnvironmentVariableW(name.get(), unused, 2) != 0 || GetLastError() != 203) // ERROR_ENVVAR_NOT_FOUND + return 0; + } + const wstackstring wval(value); + if(SetEnvironmentVariableW(name.get(), wval.get())) + return 0; + return -1; + } + + int unsetenv(const char* key) + { + const wshort_stackstring name(key); + if(SetEnvironmentVariableW(name.get(), 0)) + return 0; + return -1; + } + + int putenv(char* string) + { + const char* key = string; + const char* key_end = string; + while(*key_end != '=' && *key_end != '\0') + key_end++; + if(*key_end == '\0') + return -1; + const wshort_stackstring wkey(key, key_end); + const wstackstring wvalue(key_end + 1); + + if(SetEnvironmentVariableW(wkey.get(), wvalue.get())) + return 0; + return -1; + } + + int system(const char* cmd) + { + if(!cmd) + return _wsystem(0); + const wstackstring wcmd(cmd); + return _wsystem(wcmd.get()); + } +} // namespace nowide +} // namespace boost +#endif diff --git a/standalone/config.hpp b/standalone/config.hpp index ec438ae..43bdc6a 100644 --- a/standalone/config.hpp +++ b/standalone/config.hpp @@ -31,12 +31,12 @@ #endif #if defined(NOWIDE_WINDOWS) -#ifdef BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS -#undef BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS +#ifdef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +#undef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT #endif -#define BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS 1 -#elif !defined(BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS) -#define BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS 0 +#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT 1 +#elif !defined(BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT) +#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT 0 #endif #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9d5d4e1..3ec5973 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,7 +27,7 @@ boost_nowide_add_test(test_system_n SRC test_system.cpp DEFINITIONS BOOST_NOWIDE if(WIN32) boost_nowide_add_test(test_system_w SRC test_system.cpp DEFINITIONS BOOST_NOWIDE_TEST_USE_NARROW=0) else() - boost_nowide_add_test(test_internal_fstream SRC test_fstream.cpp DEFINITIONS BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS=1) + boost_nowide_add_test(test_internal_fstream SRC test_fstream.cpp DEFINITIONS BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT=1) endif() if(NOT BOOST_NOWIDE_STANDALONE) @@ -40,4 +40,4 @@ endif() if(NOT BOOST_SUPERPROJECT_SOURCE_DIR) find_package(Boost 1.56 REQUIRED COMPONENTS chrono) endif() -boost_nowide_add_test(benchmark_fstream COMPILE_ONLY DEFINITIONS BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS=1 LIBRARIES Boost::chrono) +boost_nowide_add_test(benchmark_fstream COMPILE_ONLY DEFINITIONS BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT=1 LIBRARIES Boost::chrono) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e14a636..4478814 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -10,6 +10,7 @@ import testing ; project : requirements + /boost/nowide//boost_nowide pedantic on gcc:-Wno-long-long @@ -26,12 +27,11 @@ run test_env.cpp ; run test_env.cpp : : : BOOST_NOWIDE_TEST_INCLUDE_WINDOWS=1 : test_env_win ; run test_fs.cpp : : : /boost/filesystem//boost_filesystem/off : ; run test_fstream.cpp ; -run test_fstream.cpp : : : BOOST_NOWIDE_USE_FSTREAM_REPLACEMENTS=1 windows:no : test_internal_fstream; -run test_iostream.cpp : : : /boost/nowide//boost_nowide static : test_iostream_static ; -run test_iostream.cpp : : : /boost/nowide//boost_nowide shared : test_iostream_shared ; +run test_fstream.cpp : : : BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT=1 windows:no : test_internal_fstream ; +run test_iostream.cpp ; run test_stackstring.cpp ; run test_stdio.cpp ; run test_system.cpp : : : BOOST_NOWIDE_TEST_USE_NARROW=1 windows:shell32 : test_system_n ; -run test_system.cpp : : : BOOST_NOWIDE_TEST_USE_NARROW=0 windows:shell32 no windows:yes: test_system_w ; +run test_system.cpp : : : BOOST_NOWIDE_TEST_USE_NARROW=0 windows:shell32 no windows:yes : test_system_w ; -compile benchmark_fstream.cpp : BOOST_NOWIDE_USE_WIN_FSTREAM=1 /boost/chrono//boost_chrono/; +compile benchmark_fstream.cpp : BOOST_NOWIDE_USE_WIN_FSTREAM=1 /boost/chrono//boost_chrono/ ; diff --git a/test/benchmark_fstream.cpp b/test/benchmark_fstream.cpp index 56279c0..2f22a57 100644 --- a/test/benchmark_fstream.cpp +++ b/test/benchmark_fstream.cpp @@ -19,11 +19,17 @@ #include #include #include +#include #include -#ifdef BOOST_MSVC -#pragma warning(disable : 4996) // function unsafe/deprecated -#endif +template +Value get(const std::map& map, const Key2& key) +{ + typename std::map::const_iterator it = map.find(key); + if(it == map.end()) + throw std::runtime_error("Key not found"); + return it->second; +} namespace nw = boost::nowide; template @@ -66,7 +72,7 @@ class io_stdio public: io_stdio(const char* file, bool read) { - f_ = std::fopen(file, read ? "r" : "w+"); + f_ = nw::fopen(file, read ? "r" : "w+"); TEST(f_); } ~io_stdio() @@ -201,8 +207,8 @@ perf_data test_io_driver(const char* file, const char* type) double read_speed = 0, write_speed = 0; for(int i = 0; i < repeats; i++) { - read_speed += results[i].read.at(block_size); - write_speed += results[i].write.at(block_size); + read_speed += get(results[i].read, block_size); + write_speed += get(results[i].write, block_size); } results[0].read[block_size] = read_speed / repeats; results[0].write[block_size] = write_speed / repeats; @@ -221,9 +227,9 @@ void print_perf_data(const std::map& stdio_data, for(int block_size = MIN_BLOCK_SIZE; block_size <= MAX_BLOCK_SIZE; block_size *= 2) { std::cout << std::setw(8) << block_size << " "; - std::cout << std::fixed << std::setprecision(3) << std::setw(8) << stdio_data.at(block_size) << " MB/s "; - std::cout << std::fixed << std::setprecision(3) << std::setw(8) << std_data.at(block_size) << " MB/s "; - std::cout << std::fixed << std::setprecision(3) << std::setw(8) << nowide_data.at(block_size) << " MB/s "; + std::cout << std::fixed << std::setprecision(3) << std::setw(8) << get(stdio_data, block_size) << " MB/s "; + std::cout << std::fixed << std::setprecision(3) << std::setw(8) << get(std_data, block_size) << " MB/s "; + std::cout << std::fixed << std::setprecision(3) << std::setw(8) << get(nowide_data, block_size) << " MB/s "; std::cout << std::endl; } } diff --git a/test/test.hpp b/test/test.hpp index c7faeea..5e243e1 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -9,23 +9,64 @@ #ifndef BOOST_NOWIDE_LIB_TEST_H_INCLUDED #define BOOST_NOWIDE_LIB_TEST_H_INCLUDED +#include #include #include +#if defined(_MSC_VER) && defined(_CPPLIB_VER) && defined(_DEBUG) +#include +#endif + +namespace boost { +namespace nowide { + struct test_monitor + { + test_monitor() + { +#if defined(_MSC_VER) && (_MSC_VER > 1310) + // disable message boxes on assert(), abort() + ::_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif +#if defined(_MSC_VER) && defined(_CPPLIB_VER) && defined(_DEBUG) + // disable message boxes on iterator debugging violations + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); +#endif + } + }; +} // namespace nowide +} // namespace boost + +inline boost::nowide::test_monitor& test_mon() +{ + static boost::nowide::test_monitor instance; + return instance; +} + /// Function called when a test failed to be able set a breakpoint for debugging inline void test_failed(const std::string& msg) { throw std::runtime_error(msg); } +#ifdef _MSC_VER +#define DISABLE_CONST_EXPR_DETECTED __pragma(warning(push)) __pragma(warning(disable : 4127)) +#define DISABLE_CONST_EXPR_DETECTED_POP __pragma(warning(pop)) +#else +#define DISABLE_CONST_EXPR_DETECTED +#define DISABLE_CONST_EXPR_DETECTED_POP +#endif + #define TEST(x) \ do \ { \ + test_mon(); \ if(x) \ break; \ std::ostringstream ss; \ ss << "Error " #x " in " << __FILE__ << ':' << __LINE__ << " " << __FUNCTION__; \ test_failed(ss.str()); \ - } while(0) + DISABLE_CONST_EXPR_DETECTED \ + } while(0) DISABLE_CONST_EXPR_DETECTED_POP #endif // #ifndef BOOST_NOWIDE_LIB_TEST_H_INCLUDED diff --git a/test/test_env.cpp b/test/test_env.cpp index 1fa9629..7b3a8c7 100644 --- a/test/test_env.cpp +++ b/test/test_env.cpp @@ -16,9 +16,17 @@ #include "test.hpp" -#ifdef BOOST_MSVC -#pragma warning(disable : 4996) // function unsafe/deprecated -#endif +// "Safe" strcpy version with NULL termination to make MSVC runtime happy +// which warns when using strncpy +template +void strcpy_safe(char (&dest)[size], const char* src) +{ + size_t len = std::strlen(src); + if(len >= size) + len = size - 1u; + std::memcpy(dest, src, len); + dest[len] = 0; +} int main() { @@ -26,7 +34,7 @@ int main() { std::string example = "\xd7\xa9-\xd0\xbc-\xce\xbd"; char penv[256] = {0}; - strncpy(penv, ("BOOST_TEST2=" + example + "x").c_str(), sizeof(penv) - 1); + strcpy_safe(penv, ("BOOST_TEST2=" + example + "x").c_str()); TEST(boost::nowide::setenv("BOOST_TEST1", example.c_str(), 1) == 0); TEST(boost::nowide::getenv("BOOST_TEST1")); @@ -42,7 +50,7 @@ int main() // But GLIBC has an extension that unsets the env var instead char penv2[256] = {0}; const char* sPenv2 = "BOOST_TEST1SOMEGARBAGE="; - strncpy(penv2, sPenv2, sizeof(penv2) - 1); + strcpy_safe(penv2, sPenv2); // End the string before the equals sign -> Expect fail penv2[strlen("BOOST_TEST1")] = '\0'; TEST(boost::nowide::putenv(penv2) == -1); diff --git a/test/test_fstream.cpp b/test/test_fstream.cpp index cee73f9..61d1df9 100644 --- a/test/test_fstream.cpp +++ b/test/test_fstream.cpp @@ -50,7 +50,6 @@ bool file_contents_equal(const char* filepath, const char (&contents)[N], bool b return true; } -template void test_with_different_buffer_sizes(const char* filepath) { /* Important part of the standard for mixing input with output: @@ -63,13 +62,13 @@ void test_with_different_buffer_sizes(const char* filepath) { std::cout << "Buffer size = " << i << std::endl; char buf[16]; - FStream f; + nw::fstream f; // Different conditions when setbuf might be called: Usually before opening a file is OK if(i >= 0) f.rdbuf()->pubsetbuf((i == 0) ? NULL : buf, i); f.open(filepath, std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary); TEST(f); - + // Add 'abcdefg' TEST(f.put('a')); TEST(f.put('b')); TEST(f.put('c')); @@ -77,67 +76,84 @@ void test_with_different_buffer_sizes(const char* filepath) TEST(f.put('e')); TEST(f.put('f')); TEST(f.put('g')); + // Read first char TEST(f.seekg(0)); TEST(f.get() == 'a'); + TEST(f.gcount() == 1u); + // Skip next char TEST(f.seekg(1, std::ios::cur)); TEST(f.get() == 'c'); + TEST(f.gcount() == 1u); + // Go back 1 char TEST(f.seekg(-1, std::ios::cur)); TEST(f.get() == 'c'); + TEST(f.gcount() == 1u); + + // Test switching between read->write->read + // case 1) overwrite, flush, read TEST(f.seekg(1)); TEST(f.put('B')); TEST(f.flush()); // Flush when changing out->in TEST(f.get() == 'c'); + TEST(f.gcount() == 1u); TEST(f.seekg(1)); TEST(f.get() == 'B'); + TEST(f.gcount() == 1u); + // case 2) overwrite, seek, read TEST(f.seekg(2)); TEST(f.put('C')); TEST(f.seekg(3)); // Seek when changing out->in TEST(f.get() == 'd'); + TEST(f.gcount() == 1u); + + // Check that sequence from start equals expected TEST(f.seekg(0)); TEST(f.get() == 'a'); TEST(f.get() == 'B'); TEST(f.get() == 'C'); TEST(f.get() == 'd'); TEST(f.get() == 'e'); - // Putback after flush + + // Putback after flush is implementation defined + // Boost.Nowide: Works +#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT TEST(f << std::flush); TEST(f.putback('e')); TEST(f.putback('d')); TEST(f.get() == 'd'); TEST(f.get() == 'e'); +#endif + // Rest of sequence TEST(f.get() == 'f'); TEST(f.get() == 'g'); TEST(f.get() == EOF); + + // Put back until front of file is reached f.clear(); TEST(f.seekg(1)); TEST(f.get() == 'B'); TEST(f.putback('B')); // Putting back multiple chars is not possible on all implementations after a seek/flush - if(f.putback('a')) - { - TEST(!f.putback('x')); // At beginning of file -> No putback possible - // Get characters that were putback to avoid MSVC bug https://github.com/microsoft/STL/issues/342 - f.clear(); - TEST(f.get() == 'a'); - TEST(f.get() == 'B'); - } else - { - f.clear(); - TEST(f.get() == 'B'); - } +#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT + TEST(f.putback('a')); + TEST(!f.putback('x')); // At beginning of file -> No putback possible + // Get characters that were putback to avoid MSVC bug https://github.com/microsoft/STL/issues/342 + f.clear(); + TEST(f.get() == 'a'); +#endif + TEST(f.get() == 'B'); f.close(); TEST(nw::remove(filepath) == 0); } } -template void test_close(const char* filepath) { const std::string filepath2 = std::string(filepath) + ".2"; // Make sure file does not exist yet nw::remove(filepath2.c_str()); TEST(!file_exists(filepath2.c_str())); - FileBuf buf; + nw::filebuf buf; TEST(buf.open(filepath, std::ios_base::out) == &buf); TEST(buf.is_open()); // Opening when already open fails @@ -487,17 +503,11 @@ int main(int, char** argv) test_fstream(exampleFilename.c_str()); test_is_open(exampleFilename.c_str()); - std::cout << "Complex IO - Sanity Check" << std::endl; - // Don't use chars the std stream can't properly handle - test_with_different_buffer_sizes((std::string(argv[0]) + "-bufferSize.txt").c_str()); - std::cout << "Complex IO - Test" << std::endl; - test_with_different_buffer_sizes(exampleFilename.c_str()); + std::cout << "Complex IO" << std::endl; + test_with_different_buffer_sizes(exampleFilename.c_str()); - std::cout << "filebuf::close - Sanity Check" << std::endl; - // Don't use chars the std stream can't properly handle - test_close((std::string(argv[0]) + "-bufferSize.txt").c_str()); - std::cout << "filebuf::close - Test" << std::endl; - test_close(exampleFilename.c_str()); + std::cout << "filebuf::close" << std::endl; + test_close(exampleFilename.c_str()); std::cout << "Flush - Sanity Check" << std::endl; test_flush(exampleFilename.c_str()); diff --git a/test/test_stackstring.cpp b/test/test_stackstring.cpp index 4046f11..a187ba2 100644 --- a/test/test_stackstring.cpp +++ b/test/test_stackstring.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2019-2020 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -10,6 +11,7 @@ #include "test_sets.hpp" #include #include +#include #if defined(BOOST_MSVC) && BOOST_MSVC < 1700 #pragma warning(disable : 4428) // universal-character-name encountered in source @@ -48,19 +50,19 @@ int main() const wchar_t* wempty = L""; { - // Default constructed string is NULL + std::cout << "-- Default constructed string is NULL" << std::endl; const boost::nowide::short_stackstring s; TEST(s.get() == NULL); } { - // NULL ptr passed to ctor results in NULL + std::cout << "-- NULL ptr passed to ctor results in NULL" << std::endl; const boost::nowide::short_stackstring s(NULL); TEST(s.get() == NULL); const boost::nowide::short_stackstring s2(NULL, NULL); TEST(s2.get() == NULL); } { - // NULL ptr passed to convert results in NULL + std::cout << "-- NULL ptr passed to convert results in NULL" << std::endl; boost::nowide::short_stackstring s(L"foo"); TEST(s.get() == std::string("foo")); s.convert(NULL); @@ -71,7 +73,7 @@ int main() TEST(s2.get() == NULL); } { - // An empty string is accepted + std::cout << "-- An empty string is accepted" << std::endl; const boost::nowide::short_stackstring s(wempty); TEST(s.get()); TEST(s.get() == std::string()); @@ -80,7 +82,7 @@ int main() TEST(s2.get() == std::string()); } { - // An empty string is accepted + std::cout << "-- An empty string is accepted" << std::endl; boost::nowide::short_stackstring s, s2; TEST(s.convert(wempty)); TEST(s.get() == std::string()); @@ -88,8 +90,7 @@ int main() TEST(s2.get() == std::string()); } { - // Will be put on heap - TEST(whello.size() >= 3); + std::cout << "-- Will be put on heap" << std::endl; boost::nowide::basic_stackstring sw; TEST(sw.convert(hello.c_str())); TEST(sw.get() == whello); @@ -97,53 +98,61 @@ int main() TEST(sw.get() == whello); } { - // Will be put on stack - TEST(whello.size() < 5); - boost::nowide::basic_stackstring sw; + std::cout << "-- Will be put on stack" << std::endl; + boost::nowide::basic_stackstring sw; TEST(sw.convert(hello.c_str())); TEST(sw.get() == whello); TEST(sw.convert(hello.c_str(), hello.c_str() + hello.size())); TEST(sw.get() == whello); } { - // Will be put on heap - TEST(hello.size() >= 5); - boost::nowide::basic_stackstring sw; + std::cout << "-- Will be put on heap" << std::endl; + boost::nowide::basic_stackstring sw; TEST(sw.convert(whello.c_str())); TEST(sw.get() == hello); TEST(sw.convert(whello.c_str(), whello.c_str() + whello.size())); TEST(sw.get() == hello); } { - // Will be put on stack - TEST(hello.size() < 10); - boost::nowide::basic_stackstring sw; + std::cout << "-- Will be put on stack" << std::endl; + boost::nowide::basic_stackstring sw; TEST(sw.convert(whello.c_str())); TEST(sw.get() == hello); TEST(sw.convert(whello.c_str(), whello.c_str() + whello.size())); TEST(sw.get() == hello); } { - typedef boost::nowide::basic_stackstring stackstring; - const std::string heapVal = hello; - TEST(heapVal.size() >= 5); // Will be put on heap - const std::wstring wtest = L"test"; - const std::string stackVal = "test"; - TEST(stackVal.size() < 5); // Will be put on stack - const stackstring heap(whello.c_str()); - const stackstring stack(wtest.c_str()); + typedef boost::nowide::basic_stackstring stackstring; + const std::wstring heapVal = L"heapValue"; + TEST(heapVal.size() >= 6); // Will be put on heap + const std::wstring stackVal = L"stack"; + TEST(stackVal.size() < 6); // Will be put on stack + const stackstring heap(boost::nowide::narrow(heapVal).c_str()); + const stackstring stack(boost::nowide::narrow(stackVal).c_str()); { - stackstring sw2(heap), sw3; + stackstring sw2(heap), sw3, sEmpty; sw3 = heap; TEST(sw2.get() == heapVal); TEST(sw3.get() == heapVal); + // Self assign avoiding clang self-assign-overloaded warning + sw3 = static_cast(sw3); + TEST(sw3.get() == heapVal); + // Assign empty + sw3 = sEmpty; + TEST(sw3.get() == NULL); } { - stackstring sw2(stack), sw3; + stackstring sw2(stack), sw3, sEmpty; sw3 = stack; TEST(sw2.get() == stackVal); TEST(sw3.get() == stackVal); + // Self assign avoiding clang self-assign-overloaded warning + sw3 = static_cast(sw3); + TEST(sw3.get() == stackVal); + // Assign empty + sw3 = sEmpty; + TEST(sw3.get() == NULL); } { stackstring sw2(stack); @@ -156,18 +165,24 @@ int main() TEST(sw2.get() == stackVal); } { - stackstring sw2(heap), sw3(stack); + stackstring sw2(heap), sw3(stack), sEmpty1, sEmpty2; swap(sw2, sw3); TEST(sw2.get() == stackVal); TEST(sw3.get() == heapVal); swap(sw2, sw3); TEST(sw2.get() == heapVal); TEST(sw3.get() == stackVal); + swap(sw2, sEmpty1); + TEST(sEmpty1.get() == heapVal); + TEST(sw2.get() == NULL); + swap(sw3, sEmpty2); + TEST(sEmpty2.get() == stackVal); + TEST(sw3.get() == NULL); } { stackstring sw2(heap), sw3(heap); sw3.get()[0] = 'z'; - const std::string val2 = sw3.get(); + const std::wstring val2 = sw3.get(); swap(sw2, sw3); TEST(sw2.get() == val2); TEST(sw3.get() == heapVal); @@ -175,15 +190,28 @@ int main() { stackstring sw2(stack), sw3(stack); sw3.get()[0] = 'z'; - const std::string val2 = sw3.get(); + const std::wstring val2 = sw3.get(); swap(sw2, sw3); TEST(sw2.get() == val2); TEST(sw3.get() == stackVal); } - // Sanity check + std::cout << "-- Sanity check" << std::endl; TEST(stack.get() == stackVal); TEST(heap.get() == heapVal); } + { + std::cout << "-- Test putting stackstrings into vector (done by args) class" << std::endl; + // Use a smallish buffer, to have stack and heap values + typedef boost::nowide::basic_stackstring stackstring; + std::vector strings; + strings.resize(2); + TEST(strings[0].convert("1234") == std::wstring(L"1234")); + TEST(strings[1].convert("Hello World") == std::wstring(L"Hello World")); + strings.push_back(stackstring("FooBar")); + TEST(strings[0].get() == std::wstring(L"1234")); + TEST(strings[1].get() == std::wstring(L"Hello World")); + TEST(strings[2].get() == std::wstring(L"FooBar")); + } std::cout << "- Stackstring" << std::endl; run_all(stackstring_to_wide, stackstring_to_narrow); std::cout << "- Heap Stackstring" << std::endl; diff --git a/test/test_stdio.cpp b/test/test_stdio.cpp index 92c6efa..5766458 100644 --- a/test/test_stdio.cpp +++ b/test/test_stdio.cpp @@ -8,19 +8,18 @@ // #include + +#include +#include #include #include #include "test.hpp" -#ifdef BOOST_MSVC -#pragma warning(disable : 4996) // function unsafe/deprecated -#endif - bool file_exists(const std::string& filename) { #ifdef BOOST_WINDOWS - FILE* f = _wfopen(boost::nowide::widen(filename).c_str(), L"r"); + FILE* f = boost::nowide::detail::wfopen(boost::nowide::widen(filename).c_str(), L"r"); #else FILE* f = std::fopen(filename.c_str(), "r"); #endif @@ -36,7 +35,7 @@ bool file_exists(const std::string& filename) void create_test_file(const std::string& filename) { #ifdef BOOST_WINDOWS - FILE* f = _wfopen(boost::nowide::widen(filename).c_str(), L"w"); + FILE* f = boost::nowide::detail::wfopen(boost::nowide::widen(filename).c_str(), L"w"); #else FILE* f = std::fopen(filename.c_str(), "w"); #endif @@ -45,14 +44,24 @@ void create_test_file(const std::string& filename) std::fclose(f); } +#if BOOST_MSVC +#include // For _CrtSetReportMode +void noop_invalid_param_handler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned, uintptr_t) +{} +#endif + int main(int, char** argv) { const std::string prefix = argv[0]; const std::string filename = prefix + "\xd7\xa9-\xd0\xbc-\xce\xbd.txt"; +#if BOOST_MSVC + // Prevent abort on freopen(NULL, ...) + _set_invalid_parameter_handler(noop_invalid_param_handler); +#endif try { - // fopen - existing file + std::cout << " -- fopen - existing file" << std::endl; { create_test_file(filename); FILE* f = boost::nowide::fopen(filename.c_str(), "r"); @@ -62,26 +71,26 @@ int main(int, char** argv) TEST(strcmp(buf, "test\n") == 0); std::fclose(f); } - // remove + std::cout << " -- remove" << std::endl; { create_test_file(filename); TEST(file_exists(filename)); TEST(boost::nowide::remove(filename.c_str()) == 0); TEST(!file_exists(filename)); } - // fopen non-existing file + std::cout << " -- fopen non-existing file" << std::endl; { boost::nowide::remove(filename.c_str()); TEST(!file_exists(filename)); TEST(boost::nowide::fopen(filename.c_str(), "r") == NULL); TEST(!file_exists(filename)); } - // freopen + std::cout << " -- freopen" << std::endl; { create_test_file(filename); FILE* f = boost::nowide::fopen(filename.c_str(), "r+"); TEST(f); - // Can read & write + std::cout << " -- Can read & write" << std::endl; { char buf[32]; TEST(std::fgets(buf, 32, f) != 0); @@ -95,7 +104,7 @@ int main(int, char** argv) FILE* f2 = boost::nowide::freopen(NULL, "r", f); if(!f2) f2 = boost::nowide::freopen(filename.c_str(), "r", f); - // no write possible + std::cout << " -- no write possible" << std::endl; { TEST(f2 == f); TEST(std::fputs("not-written\n", f) < 0); @@ -106,7 +115,7 @@ int main(int, char** argv) TEST(std::fgets(buf, 32, f) != 0); TEST(strcmp(buf, "foobar\n") == 0); } - // Reopen different file + std::cout << " -- Reopen different file" << std::endl; const std::string filename2 = filename + ".1.txt"; TEST(boost::nowide::freopen(filename2.c_str(), "w", f) == f); { @@ -121,7 +130,7 @@ int main(int, char** argv) std::fclose(f); boost::nowide::remove(filename2.c_str()); } - // rename + std::cout << " -- rename" << std::endl; { create_test_file(filename); const std::string filename2 = filename + ".1.txt";