Compare commits

14 Commits

Author SHA1 Message Date
4f7f3f2147 Use __has_cpp_attribute for NOWIDE_FALLTHROUGH 2021-06-30 16:22:27 -04:00
6eadd55f7a Merge remote-tracking branch 'origin/utf8' into utf8
# Conflicts:
#	lib/logvisor.cpp
2021-06-29 00:09:20 -04:00
df3c283186 Update fmt to 8.0.0; drop support for wchar
Adds some boost-nowide headers to assist in
UTF-8/16 conversion for Windows API.
2021-06-25 15:04:38 -04:00
139281f667 Update fmt to 8.0.0; drop support for wchar
Adds some boost-nowide headers to assist in
UTF-8/16 conversion for Windows API.
2021-06-22 15:13:37 -04:00
274ad5ef07 Fix compiling on linux 2021-06-19 14:43:23 -07:00
Henrique Gemignani Passos Lima
ed3714e55c Fix ConsoleLogger misusing Widen result lifetime 2021-06-19 13:40:17 +03:00
ee3276d7c5 Merge pull request #7 from Minty-Meeo/wide-printing-fixes 2021-05-07 23:17:57 -07:00
Minty-Meeo
f23179e168 Wide char homogeneity
If wide char stdlib functions are used on a narrow char stream, they attempt to narrow the character or string.  If this fails, nothing is written to the stream and an error is indicated.  The {fmt} library, at its core, uses stdlib functions for printing to a stream.  However, it takes it a step further and acts upon errors while printing by throwing an exception.

If narrow char stdlib functions are used on a wide char stream, they throw an assertion.  The {fmt} library somehow does not throw an assertion, but whatever it ends up printing is garbage anyhow.

So from either end, it is generally a bad idea to mix narrow and wide character printing.  While you can get away with using wide char functions on a narrow char stream, the moment you start using anything other than English characters, it all falls apart.  Just as well, narrow chars on wide char streams do not work at all.  Despite this, I found many situations where wide char printing and narrow char printing was being used at the same time.  This PR makes character printing homogeneous to avoid the previously discussed issues.
2021-05-08 00:52:03 -05:00
7901fc496d Don't install if not building directly 2021-04-13 00:48:30 -04:00
d874350205 Fetch Sentry as needed; don't reimport fmt unnecessarily 2021-04-13 00:05:09 -04:00
028617d225 Update sentry-native 2021-04-12 15:52:58 -04:00
984d87348e Set FMT_ARM_ABI_COMPATIBILITY; move fmt defines to CMake 2021-04-06 17:55:15 -04:00
33e7322595 Update submodules; set submodule tracking branch 2021-04-06 12:46:56 -04:00
bb113e03f8 Fix sentry submodule URL 2021-04-05 16:31:04 -04:00
15 changed files with 1396 additions and 140 deletions

4
.gitmodules vendored
View File

@@ -1,6 +1,4 @@
[submodule "fmt"] [submodule "fmt"]
path = fmt path = fmt
url = https://github.com/fmtlib/fmt url = https://github.com/fmtlib/fmt
[submodule "sentry"] branch = master
path = sentry
url = git@github.com:getsentry/sentry-native.git

View File

@@ -1,15 +1,22 @@
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17 project(nod VERSION 0.1)
project(nod VERSION 0.1) if (NOT MSVC)
if (NOT MSVC) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON) endif ()
endif()
endif()
include (CMakePackageConfigHelpers) include (CMakePackageConfigHelpers)
add_subdirectory(fmt) if (NOT TARGET fmt)
add_subdirectory(fmt)
target_compile_definitions(fmt PUBLIC
FMT_ARM_ABI_COMPATIBILITY=1
FMT_EXCEPTIONS=0)
target_compile_definitions(fmt INTERFACE
FMT_ENFORCE_COMPILE_STRING=1
FMT_UNICODE=1)
set(FMT_LIB fmt)
endif ()
add_library(logvisor add_library(logvisor
lib/logvisor.cpp lib/logvisor.cpp
@@ -22,7 +29,12 @@ if ("${SENTRY_DSN}" STREQUAL "")
set(BREAKPAD_CLIENT "") set(BREAKPAD_CLIENT "")
else () else ()
message(STATUS "Enabling Sentry integration") message(STATUS "Enabling Sentry integration")
add_subdirectory(sentry) include(FetchContent)
FetchContent_Declare(sentry
GIT_REPOSITORY https://github.com/getsentry/sentry-native.git
GIT_TAG 5fcb1dc4d1c8b85fc5ab225d29c7394b375fd1e9
)
FetchContent_MakeAvailable(sentry)
target_compile_definitions(logvisor PUBLIC SENTRY_ENABLED=1) target_compile_definitions(logvisor PUBLIC SENTRY_ENABLED=1)
target_compile_definitions(logvisor PRIVATE SENTRY_DSN="${SENTRY_DSN}") target_compile_definitions(logvisor PRIVATE SENTRY_DSN="${SENTRY_DSN}")
set(SENTRY_LIB sentry) set(SENTRY_LIB sentry)
@@ -55,8 +67,11 @@ endif()
target_include_directories(logvisor PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) target_include_directories(logvisor PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
install(DIRECTORY include/logvisor DESTINATION include) install(DIRECTORY include/logvisor DESTINATION include)
install(DIRECTORY fmt/include/fmt DESTINATION include) if (FMT_LIB)
install(DIRECTORY fmt/include/fmt DESTINATION include)
endif ()
set(version_config_file "${PROJECT_BINARY_DIR}/logvisorConfigVersion.cmake") set(version_config_file "${PROJECT_BINARY_DIR}/logvisorConfigVersion.cmake")
set(config_file "${PROJECT_BINARY_DIR}/logvisorConfig.cmake") set(config_file "${PROJECT_BINARY_DIR}/logvisorConfig.cmake")
@@ -64,7 +79,7 @@ set(config_install_dir "lib/cmake/logvisor")
# Associate target with export # Associate target with export
install( install(
TARGETS logvisor fmt ${SENTRY_LIB} ${BREAKPAD_CLIENT} TARGETS logvisor ${FMT_LIB} ${SENTRY_LIB} ${BREAKPAD_CLIENT}
EXPORT logvisorTargets EXPORT logvisorTargets
ARCHIVE DESTINATION "lib" ARCHIVE DESTINATION "lib"
INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target. INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target.
@@ -95,3 +110,4 @@ install(
FILES "${config_file}" "${version_config_file}" FILES "${config_file}" "${version_config_file}"
DESTINATION ${config_install_dir} DESTINATION ${config_install_dir}
) )
endif()

2
fmt

Submodule fmt updated: 9bdd1596ce...9e8b86fd2d

View File

@@ -8,8 +8,6 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#define FMT_STRING_ALIAS 1
#define FMT_ENFORCE_COMPILE_STRING 1
#include <fmt/format.h> #include <fmt/format.h>
#if defined(__SWITCH__) && !defined(LOGVISOR_NX_LM) #if defined(__SWITCH__) && !defined(LOGVISOR_NX_LM)
@@ -23,10 +21,6 @@ namespace logvisor {
[[noreturn]] void logvisorAbort(); [[noreturn]] void logvisorAbort();
#if _WIN32 && UNICODE
#define LOG_UCS2 1
#endif
/* True if ANSI color available */ /* True if ANSI color available */
extern bool XtermColor; extern bool XtermColor;
@@ -50,11 +44,8 @@ public:
ILogger(uint64_t typeHash) : m_typeHash(typeHash) {} ILogger(uint64_t typeHash) : m_typeHash(typeHash) {}
virtual ~ILogger() = default; virtual ~ILogger() = default;
virtual void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) = 0; virtual void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) = 0;
virtual void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) = 0;
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
fmt::string_view format, fmt::format_args args) = 0; fmt::string_view format, fmt::format_args args) = 0;
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
fmt::wstring_view format, fmt::wformat_args args) = 0;
[[nodiscard]] uint64_t getTypeId() const { return m_typeHash; } [[nodiscard]] uint64_t getTypeId() const { return m_typeHash; }
}; };
@@ -108,7 +99,7 @@ struct LogMutex {
if (enabled) if (enabled)
return std::unique_lock<std::recursive_mutex>(mutex); return std::unique_lock<std::recursive_mutex>(mutex);
else else
return std::unique_lock<std::recursive_mutex>(); return {};
} }
}; };
extern LogMutex _LogMutex; extern LogMutex _LogMutex;
@@ -170,18 +161,6 @@ void RegisterSentry(const char* appName, const char* appVersion, const char* cac
void CreateWin32Console(); void CreateWin32Console();
#endif #endif
#if LOG_UCS2
/**
* @brief Construct and register a file logger (wchar_t version)
* @param filepath Path to write the file
*
* If there's already a file logger registered to the same file, this is a no-op.
*/
void RegisterFileLogger(const wchar_t* filepath);
#endif
/** /**
* @brief This is constructed per-subsystem in a locally centralized fashion * @brief This is constructed per-subsystem in a locally centralized fashion
*/ */
@@ -236,7 +215,7 @@ public:
return; return;
_vreport(severity, fmt::to_string_view<Char>(format), _vreport(severity, fmt::to_string_view<Char>(format),
fmt::basic_format_args<fmt::buffer_context<Char>>( fmt::basic_format_args<fmt::buffer_context<Char>>(
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...))); fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
} }
template <typename Char> template <typename Char>
@@ -260,7 +239,7 @@ public:
return; return;
_vreportSource(severity, file, linenum, fmt::to_string_view<Char>(format), _vreportSource(severity, file, linenum, fmt::to_string_view<Char>(format),
fmt::basic_format_args<fmt::buffer_context<Char>>( fmt::basic_format_args<fmt::buffer_context<Char>>(
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...))); fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
} }
template <typename Char> template <typename Char>
@@ -319,5 +298,5 @@ void quicklog(const S& format, Args&&... args) {
logvisor::MainLoggers[0]->report( logvisor::MainLoggers[0]->report(
"quick", logvisor::Info, fmt::to_string_view<Char>(format), "quick", logvisor::Info, fmt::to_string_view<Char>(format),
fmt::basic_format_args<fmt::buffer_context<Char>>( fmt::basic_format_args<fmt::buffer_context<Char>>(
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...))); fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
} }

194
include/nowide/args.hpp Normal file
View File

@@ -0,0 +1,194 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_ARGS_HPP_INCLUDED
#define NOWIDE_ARGS_HPP_INCLUDED
#include <nowide/config.hpp>
#ifdef NOWIDE_WINDOWS
#include <nowide/stackstring.hpp>
#include <nowide/windows.hpp>
#include <stdexcept>
#include <vector>
#endif
namespace nowide {
#if !defined(NOWIDE_WINDOWS) && !defined(NOWIDE_DOXYGEN)
class args
{
public:
args(int&, char**&)
{}
args(int&, char**&, char**&)
{}
};
#else
///
/// \brief \c args is a class that temporarily replaces standard main() function arguments with their
/// equal, but UTF-8 encoded values under Microsoft Windows for the lifetime of the instance.
///
/// The class uses \c GetCommandLineW(), \c CommandLineToArgvW() and \c GetEnvironmentStringsW()
/// in order to obtain Unicode-encoded values.
/// It does not relate to actual values of argc, argv and env under Windows.
///
/// It restores the original values in its destructor (usually at the end of the \c main function).
///
/// If any of the system calls fails, an exception of type std::runtime_error will be thrown
/// and argc, argv, env remain unchanged.
///
/// \note The class owns the memory of the newly allocated strings.
/// So you need to keep it alive as long as you use the values.
///
/// Usage:
/// \code
/// int main(int argc, char** argv, char** env) {
/// nowide::args _(argc, argv, env); // Note the _ as a "don't care" name for the instance
/// // Use argv and env as usual, they are now UTF-8 encoded on Windows
/// return 0; // Memory held by args is released
/// }
/// \endcode
class args
{
public:
///
/// Fix command line arguments
///
args(int& argc, char**& argv) :
old_argc_(argc), old_argv_(argv), old_env_(0), old_argc_ptr_(&argc), old_argv_ptr_(&argv), old_env_ptr_(0)
{
fix_args(argc, argv);
}
///
/// Fix command line arguments and environment
///
args(int& argc, char**& argv, char**& env) :
old_argc_(argc), old_argv_(argv), old_env_(env), old_argc_ptr_(&argc), old_argv_ptr_(&argv),
old_env_ptr_(&env)
{
fix_args(argc, argv);
fix_env(env);
}
///
/// Restore original argc, argv, env values, if changed
///
~args()
{
if(old_argc_ptr_)
*old_argc_ptr_ = old_argc_;
if(old_argv_ptr_)
*old_argv_ptr_ = old_argv_;
if(old_env_ptr_)
*old_env_ptr_ = old_env_;
}
private:
class wargv_ptr
{
wchar_t** p;
int argc;
public:
wargv_ptr()
{
p = CommandLineToArgvW(GetCommandLineW(), &argc);
}
~wargv_ptr()
{
if(p)
LocalFree(p);
}
wargv_ptr(const wargv_ptr&) = delete;
wargv_ptr& operator=(const wargv_ptr&) = delete;
int size() const
{
return argc;
}
operator bool() const
{
return p != NULL;
}
const wchar_t* operator[](size_t i) const
{
return p[i];
}
};
class wenv_ptr
{
wchar_t* p;
public:
wenv_ptr() : p(GetEnvironmentStringsW())
{}
~wenv_ptr()
{
if(p)
FreeEnvironmentStringsW(p);
}
wenv_ptr(const wenv_ptr&) = delete;
wenv_ptr& operator=(const wenv_ptr&) = delete;
operator const wchar_t*() const
{
return p;
}
};
void fix_args(int& argc, char**& argv)
{
const wargv_ptr wargv;
if(!wargv)
throw std::runtime_error("Could not get command line!");
args_.resize(wargv.size() + 1, 0);
arg_values_.resize(wargv.size());
for(int i = 0; i < wargv.size(); i++)
args_[i] = arg_values_[i].convert(wargv[i]);
argc = wargv.size();
argv = &args_[0];
}
void fix_env(char**& env)
{
const wenv_ptr wstrings;
if(!wstrings)
throw std::runtime_error("Could not get environment strings!");
const wchar_t* wstrings_end = 0;
int count = 0;
for(wstrings_end = wstrings; *wstrings_end; wstrings_end += wcslen(wstrings_end) + 1)
count++;
env_.convert(wstrings, wstrings_end);
envp_.resize(count + 1, 0);
char* p = env_.get();
int pos = 0;
for(int i = 0; i < count; i++)
{
if(*p != '=')
envp_[pos++] = p;
p += strlen(p) + 1;
}
env = &envp_[0];
}
std::vector<char*> args_;
std::vector<short_stackstring> arg_values_;
stackstring env_;
std::vector<char*> envp_;
int old_argc_;
char** old_argv_;
char** old_env_;
int* old_argc_ptr_;
char*** old_argv_ptr_;
char*** old_env_ptr_;
};
#endif
} // namespace nowide
#endif

85
include/nowide/config.hpp Normal file
View File

@@ -0,0 +1,85 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_CONFIG_HPP_INCLUDED
#define NOWIDE_CONFIG_HPP_INCLUDED
#include <nowide/replacement.hpp>
#if(defined(__WIN32) || defined(_WIN32) || defined(WIN32)) && !defined(__CYGWIN__)
#define NOWIDE_WINDOWS
#endif
#ifdef _MSC_VER
#define NOWIDE_MSVC _MSC_VER
#endif
#ifdef __GNUC__
#define NOWIDE_SYMBOL_VISIBLE __attribute__((__visibility__("default")))
#endif
#ifndef NOWIDE_SYMBOL_VISIBLE
#define NOWIDE_SYMBOL_VISIBLE
#endif
#ifdef NOWIDE_WINDOWS
#define NOWIDE_SYMBOL_EXPORT __declspec(dllexport)
#define NOWIDE_SYMBOL_IMPORT __declspec(dllimport)
#else
#define NOWIDE_SYMBOL_EXPORT NOWIDE_SYMBOL_VISIBLE
#define NOWIDE_SYMBOL_IMPORT
#endif
#if defined(NOWIDE_DYN_LINK)
#ifdef NOWIDE_SOURCE
#define NOWIDE_DECL NOWIDE_SYMBOL_EXPORT
#else
#define NOWIDE_DECL NOWIDE_SYMBOL_IMPORT
#endif // NOWIDE_SOURCE
#else
#define NOWIDE_DECL
#endif // NOWIDE_DYN_LINK
#ifndef NOWIDE_DECL
#define NOWIDE_DECL
#endif
#if defined(NOWIDE_WINDOWS)
#ifdef NOWIDE_USE_FILEBUF_REPLACEMENT
#undef NOWIDE_USE_FILEBUF_REPLACEMENT
#endif
#define NOWIDE_USE_FILEBUF_REPLACEMENT 1
#elif !defined(NOWIDE_USE_FILEBUF_REPLACEMENT)
#define NOWIDE_USE_FILEBUF_REPLACEMENT 0
#endif
#if defined(__has_cpp_attribute)
#define NOWIDE_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
#else
#define NOWIDE_HAS_CPP_ATTRIBUTE(attr) (0)
#endif
#if NOWIDE_HAS_CPP_ATTRIBUTE(fallthrough)
#define NOWIDE_FALLTHROUGH [[fallthrough]]
#else
#define NOWIDE_FALLTHROUGH
#endif
#if defined __GNUC__
#define NOWIDE_LIKELY(x) __builtin_expect(x, 1)
#define NOWIDE_UNLIKELY(x) __builtin_expect(x, 0)
#else
#if !defined(NOWIDE_LIKELY)
#define NOWIDE_LIKELY(x) x
#endif
#if !defined(NOWIDE_UNLIKELY)
#define NOWIDE_UNLIKELY(x) x
#endif
#endif
#endif

135
include/nowide/convert.hpp Normal file
View File

@@ -0,0 +1,135 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_CONVERT_HPP_INCLUDED
#define NOWIDE_CONVERT_HPP_INCLUDED
#include <nowide/detail/is_string_container.hpp>
#include <nowide/utf/convert.hpp>
#include <string>
namespace nowide {
///
/// Convert wide string (UTF-16/32) in range [begin,end) to NULL terminated narrow string (UTF-8)
/// stored in \a output of size \a output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
inline char* narrow(char* output, size_t output_size, const wchar_t* begin, const wchar_t* end)
{
return utf::convert_buffer(output, output_size, begin, end);
}
///
/// Convert NULL terminated wide string (UTF-16/32) to NULL terminated narrow string (UTF-8)
/// stored in \a output of size \a output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
inline char* narrow(char* output, size_t output_size, const wchar_t* source)
{
return narrow(output, output_size, source, source + utf::strlen(source));
}
///
/// Convert narrow string (UTF-8) in range [begin,end) to NULL terminated wide string (UTF-16/32)
/// stored in \a output of size \a output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
inline wchar_t* widen(wchar_t* output, size_t output_size, const char* begin, const char* end)
{
return utf::convert_buffer(output, output_size, begin, end);
}
///
/// Convert NULL terminated narrow string (UTF-8) to NULL terminated wide string (UTF-16/32)
/// most output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
inline wchar_t* widen(wchar_t* output, size_t output_size, const char* source)
{
return widen(output, output_size, source, source + utf::strlen(source));
}
///
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
///
/// \param s Input string
/// \param count Number of characters to convert
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_wide_char<T_Char>>
inline std::string narrow(const T_Char* s, size_t count)
{
return utf::convert_string<char>(s, s + count);
}
///
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
///
/// \param s NULL terminated input string
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_wide_char<T_Char>>
inline std::string narrow(const T_Char* s)
{
return narrow(s, utf::strlen(s));
}
///
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
///
/// \param s Input string
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
template<typename StringOrStringView, typename = detail::requires_wide_string_container<StringOrStringView>>
inline std::string narrow(const StringOrStringView& s)
{
return utf::convert_string<char>(s.data(), s.data() + s.size());
}
///
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
///
/// \param s Input string
/// \param count Number of characters to convert
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_narrow_char<T_Char>>
inline std::wstring widen(const T_Char* s, size_t count)
{
return utf::convert_string<wchar_t>(s, s + count);
}
///
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
///
/// \param s NULL terminated input string
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_narrow_char<T_Char>>
inline std::wstring widen(const T_Char* s)
{
return widen(s, utf::strlen(s));
}
///
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
///
/// \param s Input string
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
///
template<typename StringOrStringView, typename = detail::requires_narrow_string_container<StringOrStringView>>
inline std::wstring widen(const StringOrStringView& s)
{
return utf::convert_string<wchar_t>(s.data(), s.data() + s.size());
}
} // namespace nowide
#endif

View File

@@ -0,0 +1,94 @@
//
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED
#define NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED
#include <cstddef>
#include <type_traits>
namespace nowide {
namespace detail {
template<class...>
struct make_void
{
typedef void type;
};
template<class... Ts>
using void_t = typename make_void<Ts...>::type;
template<typename T>
struct is_char_type : std::false_type
{};
template<>
struct is_char_type<char> : std::true_type
{};
template<>
struct is_char_type<wchar_t> : std::true_type
{};
template<>
struct is_char_type<char16_t> : std::true_type
{};
template<>
struct is_char_type<char32_t> : std::true_type
{};
#ifdef __cpp_char8_t
template<>
struct is_char_type<char8_t> : std::true_type
{};
#endif
template<typename T>
struct is_c_string : std::false_type
{};
template<typename T>
struct is_c_string<const T*> : is_char_type<T>
{};
template<typename T>
using const_data_result = decltype(std::declval<const T>().data());
/// Return the size of the char type returned by the data() member function
template<typename T>
using get_data_width =
std::integral_constant<std::size_t, sizeof(typename std::remove_pointer<const_data_result<T>>::type)>;
template<typename T>
using size_result = decltype(std::declval<T>().size());
/// Return true if the data() member function returns a pointer to a type of size 1
template<typename T>
using has_narrow_data = std::integral_constant<bool, (get_data_width<T>::value == 1)>;
/// Return true if T is a string container, e.g. std::basic_string, std::basic_string_view
/// Requires a static value `npos`, a member function `size()` returning an integral,
/// and a member function `data()` returning a C string
template<typename T, bool isNarrow, typename = void>
struct is_string_container : std::false_type
{};
// clang-format off
template<typename T, bool isNarrow>
struct is_string_container<T, isNarrow, void_t<decltype(T::npos), size_result<T>, const_data_result<T>>>
: std::integral_constant<bool,
std::is_integral<decltype(T::npos)>::value
&& std::is_integral<size_result<T>>::value
&& is_c_string<const_data_result<T>>::value
&& isNarrow == has_narrow_data<T>::value>
{};
// clang-format on
template<typename T>
using requires_narrow_string_container = typename std::enable_if<is_string_container<T, true>::value>::type;
template<typename T>
using requires_wide_string_container = typename std::enable_if<is_string_container<T, false>::value>::type;
template<typename T>
using requires_narrow_char = typename std::enable_if<sizeof(T) == 1 && is_char_type<T>::value>::type;
template<typename T>
using requires_wide_char = typename std::enable_if<(sizeof(T) > 1) && is_char_type<T>::value>::type;
} // namespace detail
} // namespace nowide
#endif

View File

@@ -0,0 +1,19 @@
//
// Copyright (c) 2018 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_REPLACEMENT_HPP_INCLUDED
#define NOWIDE_REPLACEMENT_HPP_INCLUDED
/// @file
/// \def NOWIDE_REPLACEMENT_CHARACTER
/// Unicode character to be used to replace invalid UTF-8 sequences
#ifndef NOWIDE_REPLACEMENT_CHARACTER
#define NOWIDE_REPLACEMENT_CHARACTER 0xFFFD
#endif
#endif // boost/nowide/replacement.hpp

View File

@@ -0,0 +1,213 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_STACKSTRING_HPP_INCLUDED
#define NOWIDE_STACKSTRING_HPP_INCLUDED
#include <nowide/convert.hpp>
#include <nowide/utf/utf.hpp>
#include <cassert>
#include <cstring>
namespace nowide {
///
/// \brief A class that allows to create a temporary wide or narrow UTF strings from
/// wide or narrow UTF source.
///
/// It uses a stack buffer if the string is short enough
/// otherwise allocates a buffer on the heap.
///
/// Invalid UTF characters are replaced by the substitution character, see #NOWIDE_REPLACEMENT_CHARACTER
///
/// If a NULL pointer is passed to the constructor or convert method, NULL will be returned by c_str.
/// Similarily a default constructed stackstring will return NULL on calling c_str.
///
template<typename CharOut = wchar_t, typename CharIn = char, size_t BufferSize = 256>
class basic_stackstring
{
public:
/// Size of the stack buffer
static const size_t buffer_size = BufferSize;
/// Type of the output character (converted to)
using output_char = CharOut;
/// Type of the input character (converted from)
using input_char = CharIn;
/// Creates a NULL stackstring
basic_stackstring() : data_(NULL)
{
buffer_[0] = 0;
}
/// Convert the NULL terminated string input and store in internal buffer
/// If input is NULL, nothing will be stored
explicit basic_stackstring(const input_char* input) : data_(NULL)
{
convert(input);
}
explicit basic_stackstring(std::string_view input) : data_(NULL) {
convert(input.data(), input.data() + input.size());
}
/// Convert the sequence [begin, end) and store in internal buffer
/// If begin is NULL, nothing will be stored
basic_stackstring(const input_char* begin, const input_char* end) : data_(NULL)
{
convert(begin, end);
}
/// Copy construct from other
basic_stackstring(const basic_stackstring& other) : data_(NULL)
{
*this = other;
}
/// Copy assign from 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();
}
/// Convert the NULL terminated string input and store in internal buffer
/// If input is NULL, the current buffer will be reset to NULL
output_char* convert(const input_char* input)
{
if(input)
return convert(input, input + utf::strlen(input));
clear();
return get();
}
/// Convert the sequence [begin, end) and store in internal buffer
/// If begin is NULL, the current buffer will be reset to NULL
output_char* convert(const input_char* begin, const input_char* end)
{
clear();
if(begin)
{
const size_t input_len = end - begin;
// Minimum size required: 1 output char per input char + trailing NULL
const size_t min_output_size = input_len + 1;
// If there is a chance the converted string fits on stack, try it
if(min_output_size <= buffer_size && utf::convert_buffer(buffer_, buffer_size, begin, end))
data_ = buffer_;
else
{
// Fallback: Allocate a buffer that is surely large enough on heap
// Max size: Every input char is transcoded to the output char with maximum with + trailing NULL
const size_t max_output_size = input_len * utf::utf_traits<output_char>::max_width + 1;
data_ = new output_char[max_output_size];
const bool success = utf::convert_buffer(data_, max_output_size, begin, end) == data_;
assert(success);
(void)success;
}
}
return get();
}
/// Return the converted, NULL-terminated string or NULL if no string was converted
output_char* get()
{
return data_;
}
/// Return the converted, NULL-terminated string or NULL if no string was converted
const output_char* get() const
{
return data_;
}
/// Reset the internal buffer to NULL
void clear()
{
if(!uses_stack_memory())
delete[] data_;
data_ = NULL;
}
/// Swap lhs with rhs
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_);
}
protected:
/// True if the stack memory is used
bool uses_stack_memory() const
{
return data_ == buffer_;
}
/// Return the current length of the string excluding the NULL terminator
/// If NULL is stored returns NULL
size_t length() const
{
if(!data_)
return 0;
size_t len = 0;
while(data_[len])
len++;
return len;
}
private:
output_char buffer_[buffer_size];
output_char* data_;
}; // basic_stackstring
///
/// Convenience typedef
///
using wstackstring = basic_stackstring<wchar_t, char, 256>;
///
/// Convenience typedef
///
using stackstring = basic_stackstring<char, wchar_t, 256>;
///
/// Convenience typedef
///
using wshort_stackstring = basic_stackstring<wchar_t, char, 16>;
///
/// Convenience typedef
///
using short_stackstring = basic_stackstring<char, wchar_t, 16>;
} // namespace nowide
#endif

View File

@@ -0,0 +1,96 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_UTF_CONVERT_HPP_INCLUDED
#define NOWIDE_UTF_CONVERT_HPP_INCLUDED
#include <nowide/detail/is_string_container.hpp>
#include <nowide/replacement.hpp>
#include <nowide/utf/utf.hpp>
#include <iterator>
#include <string>
namespace nowide {
namespace utf {
/// Return the length of the given string in code units.
/// That is the number of elements of type Char until the first NULL character.
/// Equivalent to `std::strlen(s)` but can handle wide-strings
template<typename Char>
size_t strlen(const Char* s)
{
const Char* end = s;
while(*end)
end++;
return end - s;
}
/// Convert a buffer of UTF sequences in the range [source_begin, source_end)
/// from \a CharIn to \a CharOut to the output \a buffer of size \a buffer_size.
///
/// \return original buffer containing the NULL terminated string or NULL
///
/// If there is not enough room in the buffer NULL is returned, and the content of the buffer is undefined.
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
template<typename CharOut, typename CharIn>
CharOut*
convert_buffer(CharOut* buffer, size_t buffer_size, const CharIn* source_begin, const CharIn* source_end)
{
CharOut* rv = buffer;
if(buffer_size == 0)
return nullptr;
buffer_size--;
while(source_begin != source_end)
{
code_point c = utf_traits<CharIn>::decode(source_begin, source_end);
if(c == illegal || c == incomplete)
{
c = NOWIDE_REPLACEMENT_CHARACTER;
}
size_t width = utf_traits<CharOut>::width(c);
if(buffer_size < width)
{
rv = NULL;
break;
}
buffer = utf_traits<CharOut>::encode(c, buffer);
buffer_size -= width;
}
*buffer++ = 0;
return rv;
}
/// Convert the UTF sequences in range [begin, end) from \a CharIn to \a CharOut
/// and return it as a string
///
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
/// \tparam CharOut Output character type
template<typename CharOut, typename CharIn>
std::basic_string<CharOut> convert_string(const CharIn* begin, const CharIn* end)
{
std::basic_string<CharOut> result;
result.reserve(end - begin);
using inserter_type = std::back_insert_iterator<std::basic_string<CharOut>>;
inserter_type inserter(result);
code_point c;
while(begin != end)
{
c = utf_traits<CharIn>::decode(begin, end);
if(c == illegal || c == incomplete)
{
c = NOWIDE_REPLACEMENT_CHARACTER;
}
utf_traits<CharOut>::encode(c, inserter);
}
return result;
}
} // namespace utf
} // namespace nowide
#endif

450
include/nowide/utf/utf.hpp Normal file
View File

@@ -0,0 +1,450 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_UTF_HPP_INCLUDED
#define NOWIDE_UTF_HPP_INCLUDED
#include <nowide/config.hpp>
#include <cstdint>
namespace nowide {
///
/// \brief Namespace that holds basic operations on UTF encoded sequences
///
/// All functions defined in this namespace do not require linking with Boost.Nowide library.
/// Extracted from Boost.Locale
///
namespace utf {
///
/// \brief The integral type that can hold a Unicode code point
///
using code_point = uint32_t;
///
/// \brief Special constant that defines illegal code point
///
static const code_point illegal = 0xFFFFFFFFu;
///
/// \brief Special constant that defines incomplete code point
///
static const code_point incomplete = 0xFFFFFFFEu;
///
/// \brief the function checks if \a v is a valid code point
///
inline bool is_valid_codepoint(code_point v)
{
if(v > 0x10FFFF)
return false;
if(0xD800 <= v && v <= 0xDFFF) // surrogates
return false;
return true;
}
#ifdef NOWIDE_DOXYGEN
///
/// \brief UTF Traits class - functions to convert UTF sequences to and from Unicode code points
///
template<typename CharType, int size = sizeof(CharType)>
struct utf_traits
{
///
/// The type of the character
///
using char_type = CharType;
///
/// Read one code point from the range [p,e) and return it.
///
/// - If the sequence that was read is incomplete sequence returns \ref incomplete,
/// - If illegal sequence detected returns \ref illegal
///
/// Requirements
///
/// - Iterator is valid input iterator
///
/// Postconditions
///
/// - p points to the last consumed character
///
template<typename Iterator>
static code_point decode(Iterator& p, Iterator e);
///
/// Maximal width of valid sequence in the code units:
///
/// - UTF-8 - 4
/// - UTF-16 - 2
/// - UTF-32 - 1
///
static const int max_width;
///
/// The width of specific code point in the code units.
///
/// Requirement: value is a valid Unicode code point
/// Returns value in range [1..max_width]
///
static int width(code_point value);
///
/// Get the size of the trail part of variable length encoded sequence.
///
/// Returns -1 if C is not valid lead character
///
static int trail_length(char_type c);
///
/// Returns true if c is trail code unit, always false for UTF-32
///
static bool is_trail(char_type c);
///
/// Returns true if c is lead code unit, always true of UTF-32
///
static bool is_lead(char_type c);
///
/// Convert valid Unicode code point \a value to the UTF sequence.
///
/// Requirements:
///
/// - \a value is valid code point
/// - \a out is an output iterator should be able to accept at least width(value) units
///
/// Returns the iterator past the last written code unit.
///
template<typename Iterator>
static Iterator encode(code_point value, Iterator out);
///
/// Decodes valid UTF sequence that is pointed by p into code point.
///
/// If the sequence is invalid or points to end the behavior is undefined
///
template<typename Iterator>
static code_point decode_valid(Iterator& p);
};
#else
template<typename CharType, int size = sizeof(CharType)>
struct utf_traits;
template<typename CharType>
struct utf_traits<CharType, 1>
{
using char_type = CharType;
static int trail_length(char_type ci)
{
unsigned char c = ci;
if(c < 128)
return 0;
if(NOWIDE_UNLIKELY(c < 194))
return -1;
if(c < 224)
return 1;
if(c < 240)
return 2;
if(NOWIDE_LIKELY(c <= 244))
return 3;
return -1;
}
static const int max_width = 4;
static int width(code_point value)
{
if(value <= 0x7F)
{
return 1;
} else if(value <= 0x7FF)
{
return 2;
} else if(NOWIDE_LIKELY(value <= 0xFFFF))
{
return 3;
} else
{
return 4;
}
}
static bool is_trail(char_type ci)
{
unsigned char c = ci;
return (c & 0xC0) == 0x80;
}
static bool is_lead(char_type ci)
{
return !is_trail(ci);
}
template<typename Iterator>
static code_point decode(Iterator& p, Iterator e)
{
if(NOWIDE_UNLIKELY(p == e))
return incomplete;
unsigned char lead = *p++;
// First byte is fully validated here
int trail_size = trail_length(lead);
if(NOWIDE_UNLIKELY(trail_size < 0))
return illegal;
// OK as only ASCII may be of size = 0
// also optimize for ASCII text
if(trail_size == 0)
return lead;
code_point c = lead & ((1 << (6 - trail_size)) - 1);
// Read the rest
unsigned char tmp;
switch(trail_size)
{
case 3:
if(NOWIDE_UNLIKELY(p == e))
return incomplete;
tmp = *p++;
if(!is_trail(tmp))
return illegal;
c = (c << 6) | (tmp & 0x3F);
NOWIDE_FALLTHROUGH;
case 2:
if(NOWIDE_UNLIKELY(p == e))
return incomplete;
tmp = *p++;
if(!is_trail(tmp))
return illegal;
c = (c << 6) | (tmp & 0x3F);
NOWIDE_FALLTHROUGH;
case 1:
if(NOWIDE_UNLIKELY(p == e))
return incomplete;
tmp = *p++;
if(!is_trail(tmp))
return illegal;
c = (c << 6) | (tmp & 0x3F);
}
// Check code point validity:
// - no surrogates and valid range
// - most compact representation
if(NOWIDE_UNLIKELY(!is_valid_codepoint(c)) || NOWIDE_UNLIKELY(width(c) != trail_size + 1))
{
p -= trail_size;
return illegal;
}
return c;
}
template<typename Iterator>
static code_point decode_valid(Iterator& p)
{
unsigned char lead = *p++;
if(lead < 192)
return lead;
int trail_size;
if(lead < 224)
trail_size = 1;
else if(NOWIDE_LIKELY(lead < 240)) // non-BMP rare
trail_size = 2;
else
trail_size = 3;
code_point c = lead & ((1 << (6 - trail_size)) - 1);
switch(trail_size)
{
case 3: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F); NOWIDE_FALLTHROUGH;
case 2: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F); NOWIDE_FALLTHROUGH;
case 1: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F);
}
return c;
}
template<typename Iterator>
static Iterator encode(code_point value, Iterator out)
{
if(value <= 0x7F)
{
*out++ = static_cast<char_type>(value);
} else if(value <= 0x7FF)
{
*out++ = static_cast<char_type>((value >> 6) | 0xC0);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
} else if(NOWIDE_LIKELY(value <= 0xFFFF))
{
*out++ = static_cast<char_type>((value >> 12) | 0xE0);
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
} else
{
*out++ = static_cast<char_type>((value >> 18) | 0xF0);
*out++ = static_cast<char_type>(((value >> 12) & 0x3F) | 0x80);
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
}
return out;
}
}; // utf8
template<typename CharType>
struct utf_traits<CharType, 2>
{
using char_type = CharType;
// See RFC 2781
static bool is_first_surrogate(uint16_t x)
{
return 0xD800 <= x && x <= 0xDBFF;
}
static bool is_second_surrogate(uint16_t x)
{
return 0xDC00 <= x && x <= 0xDFFF;
}
static code_point combine_surrogate(uint16_t w1, uint16_t w2)
{
return ((code_point(w1 & 0x3FF) << 10) | (w2 & 0x3FF)) + 0x10000;
}
static int trail_length(char_type c)
{
if(is_first_surrogate(c))
return 1;
if(is_second_surrogate(c))
return -1;
return 0;
}
///
/// Returns true if c is trail code unit, always false for UTF-32
///
static bool is_trail(char_type c)
{
return is_second_surrogate(c);
}
///
/// Returns true if c is lead code unit, always true of UTF-32
///
static bool is_lead(char_type c)
{
return !is_second_surrogate(c);
}
template<typename It>
static code_point decode(It& current, It last)
{
if(NOWIDE_UNLIKELY(current == last))
return incomplete;
uint16_t w1 = *current++;
if(NOWIDE_LIKELY(w1 < 0xD800 || 0xDFFF < w1))
{
return w1;
}
if(w1 > 0xDBFF)
return illegal;
if(current == last)
return incomplete;
uint16_t w2 = *current++;
if(w2 < 0xDC00 || 0xDFFF < w2)
return illegal;
return combine_surrogate(w1, w2);
}
template<typename It>
static code_point decode_valid(It& current)
{
uint16_t w1 = *current++;
if(NOWIDE_LIKELY(w1 < 0xD800 || 0xDFFF < w1))
{
return w1;
}
uint16_t w2 = *current++;
return combine_surrogate(w1, w2);
}
static const int max_width = 2;
static int width(code_point u)
{
return u >= 0x10000 ? 2 : 1;
}
template<typename It>
static It encode(code_point u, It out)
{
if(NOWIDE_LIKELY(u <= 0xFFFF))
{
*out++ = static_cast<char_type>(u);
} else
{
u -= 0x10000;
*out++ = static_cast<char_type>(0xD800 | (u >> 10));
*out++ = static_cast<char_type>(0xDC00 | (u & 0x3FF));
}
return out;
}
}; // utf16;
template<typename CharType>
struct utf_traits<CharType, 4>
{
using char_type = CharType;
static int trail_length(char_type c)
{
if(is_valid_codepoint(c))
return 0;
return -1;
}
static bool is_trail(char_type /*c*/)
{
return false;
}
static bool is_lead(char_type /*c*/)
{
return true;
}
template<typename It>
static code_point decode_valid(It& current)
{
return *current++;
}
template<typename It>
static code_point decode(It& current, It last)
{
if(NOWIDE_UNLIKELY(current == last))
return incomplete;
code_point c = *current++;
if(NOWIDE_UNLIKELY(!is_valid_codepoint(c)))
return illegal;
return c;
}
static const int max_width = 1;
static int width(code_point /*u*/)
{
return 1;
}
template<typename It>
static It encode(code_point u, It out)
{
*out++ = static_cast<char_type>(u);
return out;
}
}; // utf32
#endif
} // namespace utf
} // namespace nowide
#endif

View File

@@ -0,0 +1,32 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef NOWIDE_WINDOWS_HPP_INCLUDED
#define NOWIDE_WINDOWS_HPP_INCLUDED
#ifdef NOWIDE_USE_WINDOWS_H
#include <windows.h>
#else
//
// These are function prototypes... Allow to avoid including windows.h
//
extern "C" {
__declspec(dllimport) wchar_t* __stdcall GetEnvironmentStringsW(void);
__declspec(dllimport) int __stdcall FreeEnvironmentStringsW(wchar_t*);
__declspec(dllimport) wchar_t* __stdcall GetCommandLineW(void);
__declspec(dllimport) wchar_t** __stdcall CommandLineToArgvW(const wchar_t*, int*);
__declspec(dllimport) unsigned long __stdcall GetLastError();
__declspec(dllimport) void* __stdcall LocalFree(void*);
__declspec(dllimport) int __stdcall SetEnvironmentVariableW(const wchar_t*, const wchar_t*);
__declspec(dllimport) unsigned long __stdcall GetEnvironmentVariableW(const wchar_t*, wchar_t*, unsigned long);
}
#endif
#endif

View File

@@ -32,6 +32,8 @@
#include <cstdio> #include <cstdio>
#include <cinttypes> #include <cinttypes>
#include <csignal> #include <csignal>
#include <locale>
#include <optional>
#include "logvisor/logvisor.hpp" #include "logvisor/logvisor.hpp"
#if SENTRY_ENABLED #if SENTRY_ENABLED
@@ -102,16 +104,16 @@ void KillProcessTree() {
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (::Process32FirstW(hSnap, &pe)) { if (::Process32FirstW(hSnap, &pe) == TRUE) {
BOOL bContinue = TRUE; BOOL bContinue = TRUE;
// kill child processes // kill child processes
while (bContinue) { while (bContinue == TRUE) {
// only kill child processes and let console window remain // only kill child processes and let console window remain
if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) { if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) {
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
if (hChildProc) { if (hChildProc != nullptr) {
::TerminateProcess(hChildProc, 1); ::TerminateProcess(hChildProc, 1);
::CloseHandle(hChildProc); ::CloseHandle(hChildProc);
} }
@@ -131,16 +133,16 @@ void KillProcessTree() {
HANDLE process; HANDLE process;
process = GetCurrentProcess(); process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE); SymInitialize(process, nullptr, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL); frames = CaptureStackBackTrace(0, 100, stack, nullptr);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1); symbol = static_cast<SYMBOL_INFO*>(calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1));
symbol->MaxNameLen = 255; symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for (i = 0; i < frames; i++) { for (i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol); SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
std::fprintf(stderr, "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address); std::fwprintf(stderr, L"%i: %S - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
DWORD dwDisplacement; DWORD dwDisplacement;
IMAGEHLP_LINE64 line; IMAGEHLP_LINE64 line;
@@ -149,9 +151,9 @@ void KillProcessTree() {
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) { if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
// SymGetLineFromAddr64 returned success // SymGetLineFromAddr64 returned success
std::fprintf(stderr, " LINE %d\n", int(line.LineNumber)); std::fwprintf(stderr, L" LINE %d\n", int(line.LineNumber));
} else { } else {
std::fputc('\n', stderr); std::fputwc('\n', stderr);
} }
} }
@@ -252,33 +254,33 @@ void KillProcessTree() {}
LogMutex _LogMutex; LogMutex _LogMutex;
static void AbortHandler(int signum) { static void AbortHandler(int signum) {
_LogMutex.enabled = false; _LogMutex.enabled = false;
switch (signum) { switch (signum) {
case SIGSEGV: case SIGSEGV:
Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault")); Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault"));
break; break;
case SIGILL: case SIGILL:
Log.report(logvisor::Fatal, FMT_STRING("Bad Execution")); Log.report(logvisor::Fatal, FMT_STRING("Bad Execution"));
break; break;
case SIGFPE: case SIGFPE:
Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception")); Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception"));
break; break;
case SIGABRT: case SIGABRT:
Log.report(logvisor::Fatal, FMT_STRING("Abort Signal")); Log.report(logvisor::Fatal, FMT_STRING("Abort Signal"));
break; break;
default: default:
Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum); Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum);
break; break;
} }
} }
uint64_t _LogCounter; uint64_t _LogCounter;
std::vector<std::unique_ptr<ILogger>> MainLoggers; std::vector<std::unique_ptr<ILogger>> MainLoggers;
std::atomic_size_t ErrorCount(0); std::atomic_size_t ErrorCount(0);
static std::chrono::steady_clock MonoClock; using MonoClock = std::chrono::steady_clock;
static std::chrono::steady_clock::time_point GlobalStart = MonoClock.now(); static MonoClock::time_point GlobalStart = MonoClock::now();
static inline std::chrono::steady_clock::duration CurrentUptime() { return MonoClock.now() - GlobalStart; } static inline MonoClock::duration CurrentUptime() { return MonoClock::now() - GlobalStart; }
std::atomic_uint_fast64_t FrameIndex(0); std::atomic_uint_fast64_t FrameIndex(0);
static inline int ConsoleWidth() { static inline int ConsoleWidth() {
@@ -466,8 +468,6 @@ struct ConsoleLogger : public ILogger {
SendBuffer(bufOut); SendBuffer(bufOut);
} }
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format, void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override { fmt::format_args args) override {
if (!m_ready) if (!m_ready)
@@ -526,9 +526,6 @@ struct ConsoleLogger : public ILogger {
SendBuffer(bufOut); SendBuffer(bufOut);
} }
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {}
}; };
#else #else
@@ -563,11 +560,7 @@ struct ConsoleLogger : public ILogger {
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) { static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
/* Clear current line out */ /* Clear current line out */
const int width = ConsoleWidth(); // std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' ');
std::fputc('\r', stderr);
for (int w = 0; w < width; ++w)
std::fputc(' ', stderr);
std::fputc('\r', stderr);
const std::chrono::steady_clock::duration tm = CurrentUptime(); const std::chrono::steady_clock::duration tm = CurrentUptime();
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num / const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
@@ -686,13 +679,6 @@ struct ConsoleLogger : public ILogger {
std::fflush(stderr); std::fflush(stderr);
} }
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
_reportHead(modName, nullptr, severity);
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
std::fflush(stderr);
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format, void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override { fmt::format_args args) override {
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity); _reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
@@ -700,14 +686,6 @@ struct ConsoleLogger : public ILogger {
std::fputc('\n', stderr); std::fputc('\n', stderr);
std::fflush(stderr); std::fflush(stderr);
} }
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
std::fflush(stderr);
}
}; };
#endif #endif
@@ -718,6 +696,15 @@ void RegisterConsoleLogger() {
if (!ConsoleLoggerRegistered) { if (!ConsoleLoggerRegistered) {
MainLoggers.emplace_back(new ConsoleLogger); MainLoggers.emplace_back(new ConsoleLogger);
ConsoleLoggerRegistered = true; ConsoleLoggerRegistered = true;
#if _WIN32
#if 0
if (GetACP() != CP_UTF8) {
Log.report(Fatal, FMT_STRING("UTF-8 codepage not active! (Windows 10 1903+ required)"));
}
#else
SetConsoleOutputCP(CP_UTF8);
#endif
#endif
} }
} }
@@ -828,13 +815,6 @@ struct FileLogger : public ILogger {
std::fputc('\n', fp); std::fputc('\n', fp);
} }
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
openFileIfNeeded();
_reportHead(modName, nullptr, severity);
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format, void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override { fmt::format_args args) override {
openFileIfNeeded(); openFileIfNeeded();
@@ -842,19 +822,11 @@ struct FileLogger : public ILogger {
fmt::vprint(fp, format, args); fmt::vprint(fp, format, args);
std::fputc('\n', fp); std::fputc('\n', fp);
} }
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {
openFileIfNeeded();
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
}
}; };
struct FileLogger8 : public FileLogger { struct FileLogger8 : public FileLogger {
const char* m_filepath; const char* m_filepath;
FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {} explicit FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {}
void openFile() override { fp = std::fopen(m_filepath, "a"); } void openFile() override { fp = std::fopen(m_filepath, "a"); }
~FileLogger8() override = default; ~FileLogger8() override = default;
}; };
@@ -864,30 +836,4 @@ void RegisterFileLogger(const char* filepath) {
MainLoggers.emplace_back(new FileLogger8(filepath)); MainLoggers.emplace_back(new FileLogger8(filepath));
} }
#if LOG_UCS2
struct FileLogger16 : public FileLogger {
const wchar_t* m_filepath;
FileLogger16(const wchar_t* filepath) : FileLogger(log_typeid(FileLogger16)), m_filepath(filepath) {}
void openFile() override { fp = _wfopen(m_filepath, L"a"); }
~FileLogger16() override = default;
};
void RegisterFileLogger(const wchar_t* filepath) {
/* Determine if file logger already added */
for (const auto& logger : MainLoggers) {
if (logger->getTypeId() == log_typeid(FileLogger16)) {
const auto* fl = static_cast<const FileLogger16*>(logger.get());
if (!wcscmp(filepath, fl->m_filepath)) {
return;
}
}
}
/* Otherwise construct new file logger */
MainLoggers.emplace_back(new FileLogger16(filepath));
}
#endif
} // namespace logvisor } // namespace logvisor

1
sentry

Submodule sentry deleted from aee5dc1a55