From 139281f6677bc3e08e39ebcc8b84a894cbc5fb6e Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 22 Jun 2021 15:13:37 -0400 Subject: [PATCH] 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. --- CMakeLists.txt | 4 +- fmt | 2 +- include/logvisor/logvisor.hpp | 21 +- include/nowide/args.hpp | 194 ++++++++ include/nowide/config.hpp | 79 +++ include/nowide/convert.hpp | 135 ++++++ include/nowide/detail/is_string_container.hpp | 94 ++++ include/nowide/replacement.hpp | 19 + include/nowide/stackstring.hpp | 213 +++++++++ include/nowide/utf/convert.hpp | 96 ++++ include/nowide/utf/utf.hpp | 450 ++++++++++++++++++ include/nowide/windows.hpp | 32 ++ lib/logvisor.cpp | 279 +---------- 13 files changed, 1333 insertions(+), 285 deletions(-) create mode 100644 include/nowide/args.hpp create mode 100644 include/nowide/config.hpp create mode 100644 include/nowide/convert.hpp create mode 100644 include/nowide/detail/is_string_container.hpp create mode 100644 include/nowide/replacement.hpp create mode 100644 include/nowide/stackstring.hpp create mode 100644 include/nowide/utf/convert.hpp create mode 100644 include/nowide/utf/utf.hpp create mode 100644 include/nowide/windows.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 532355e..26762e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,11 +10,11 @@ include (CMakePackageConfigHelpers) if (NOT TARGET fmt) add_subdirectory(fmt) target_compile_definitions(fmt PUBLIC - FMT_STRING_ALIAS=1 FMT_ARM_ABI_COMPATIBILITY=1 FMT_EXCEPTIONS=0) target_compile_definitions(fmt INTERFACE - FMT_ENFORCE_COMPILE_STRING=1) + FMT_ENFORCE_COMPILE_STRING=1 + FMT_UNICODE=1) set(FMT_LIB fmt) endif () diff --git a/fmt b/fmt index 95da484..9e8b86f 160000 --- a/fmt +++ b/fmt @@ -1 +1 @@ -Subproject commit 95da4847274c42c110fd5bf1bf61d3f94be4a08e +Subproject commit 9e8b86fd2d9806672cc73133d21780dd182bfd24 diff --git a/include/logvisor/logvisor.hpp b/include/logvisor/logvisor.hpp index 85d0111..1b0ec87 100644 --- a/include/logvisor/logvisor.hpp +++ b/include/logvisor/logvisor.hpp @@ -21,10 +21,6 @@ namespace logvisor { [[noreturn]] void logvisorAbort(); -#if _WIN32 && UNICODE -#define LOG_UCS2 1 -#endif - /* True if ANSI color available */ extern bool XtermColor; @@ -48,11 +44,8 @@ public: ILogger(uint64_t typeHash) : m_typeHash(typeHash) {} 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::wstring_view format, fmt::wformat_args args) = 0; virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, 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; } }; @@ -106,7 +99,7 @@ struct LogMutex { if (enabled) return std::unique_lock(mutex); else - return std::unique_lock(); + return {}; } }; extern LogMutex _LogMutex; @@ -168,18 +161,6 @@ void RegisterSentry(const char* appName, const char* appVersion, const char* cac void CreateWin32Console(); #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 */ diff --git a/include/nowide/args.hpp b/include/nowide/args.hpp new file mode 100644 index 0000000..2991711 --- /dev/null +++ b/include/nowide/args.hpp @@ -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 +#ifdef NOWIDE_WINDOWS +#include +#include +#include +#include +#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 args_; + std::vector arg_values_; + stackstring env_; + std::vector 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 diff --git a/include/nowide/config.hpp b/include/nowide/config.hpp new file mode 100644 index 0000000..26b8287 --- /dev/null +++ b/include/nowide/config.hpp @@ -0,0 +1,79 @@ +// +// 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 + +#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(__GNUC__) && __GNUC__ >= 7 +#define NOWIDE_FALLTHROUGH __attribute__((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 diff --git a/include/nowide/convert.hpp b/include/nowide/convert.hpp new file mode 100644 index 0000000..e25e890 --- /dev/null +++ b/include/nowide/convert.hpp @@ -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 +#include +#include + +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> + inline std::string narrow(const T_Char* s, size_t count) + { + return utf::convert_string(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> + 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> + inline std::string narrow(const StringOrStringView& s) + { + return utf::convert_string(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> + inline std::wstring widen(const T_Char* s, size_t count) + { + return utf::convert_string(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> + 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> + inline std::wstring widen(const StringOrStringView& s) + { + return utf::convert_string(s.data(), s.data() + s.size()); + } +} // namespace nowide + +#endif diff --git a/include/nowide/detail/is_string_container.hpp b/include/nowide/detail/is_string_container.hpp new file mode 100644 index 0000000..f868544 --- /dev/null +++ b/include/nowide/detail/is_string_container.hpp @@ -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 +#include + +namespace nowide { + namespace detail { + template + struct make_void + { + typedef void type; + }; + + template + using void_t = typename make_void::type; + + template + struct is_char_type : std::false_type + {}; + template<> + struct is_char_type : std::true_type + {}; + template<> + struct is_char_type : std::true_type + {}; + template<> + struct is_char_type : std::true_type + {}; + template<> + struct is_char_type : std::true_type + {}; +#ifdef __cpp_char8_t + template<> + struct is_char_type : std::true_type + {}; +#endif + + template + struct is_c_string : std::false_type + {}; + template + struct is_c_string : is_char_type + {}; + + template + using const_data_result = decltype(std::declval().data()); + /// Return the size of the char type returned by the data() member function + template + using get_data_width = + std::integral_constant>::type)>; + template + using size_result = decltype(std::declval().size()); + /// Return true if the data() member function returns a pointer to a type of size 1 + template + using has_narrow_data = std::integral_constant::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 + struct is_string_container : std::false_type + {}; + // clang-format off + template + struct is_string_container, const_data_result>> + : std::integral_constant::value + && std::is_integral>::value + && is_c_string>::value + && isNarrow == has_narrow_data::value> + {}; + // clang-format on + template + using requires_narrow_string_container = typename std::enable_if::value>::type; + template + using requires_wide_string_container = typename std::enable_if::value>::type; + + template + using requires_narrow_char = typename std::enable_if::value>::type; + template + using requires_wide_char = typename std::enable_if<(sizeof(T) > 1) && is_char_type::value>::type; + + } // namespace detail +} // namespace nowide + +#endif diff --git a/include/nowide/replacement.hpp b/include/nowide/replacement.hpp new file mode 100644 index 0000000..d00db0f --- /dev/null +++ b/include/nowide/replacement.hpp @@ -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 diff --git a/include/nowide/stackstring.hpp b/include/nowide/stackstring.hpp new file mode 100644 index 0000000..968bc61 --- /dev/null +++ b/include/nowide/stackstring.hpp @@ -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 +#include +#include +#include + +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 + 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::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; + /// + /// Convenience typedef + /// + using stackstring = basic_stackstring; + /// + /// Convenience typedef + /// + using wshort_stackstring = basic_stackstring; + /// + /// Convenience typedef + /// + using short_stackstring = basic_stackstring; + +} // namespace nowide + +#endif diff --git a/include/nowide/utf/convert.hpp b/include/nowide/utf/convert.hpp new file mode 100644 index 0000000..4d9ab56 --- /dev/null +++ b/include/nowide/utf/convert.hpp @@ -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 +#include +#include +#include +#include + +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 + 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 + 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::decode(source_begin, source_end); + if(c == illegal || c == incomplete) + { + c = NOWIDE_REPLACEMENT_CHARACTER; + } + size_t width = utf_traits::width(c); + if(buffer_size < width) + { + rv = NULL; + break; + } + buffer = utf_traits::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 + std::basic_string convert_string(const CharIn* begin, const CharIn* end) + { + std::basic_string result; + result.reserve(end - begin); + using inserter_type = std::back_insert_iterator>; + inserter_type inserter(result); + code_point c; + while(begin != end) + { + c = utf_traits::decode(begin, end); + if(c == illegal || c == incomplete) + { + c = NOWIDE_REPLACEMENT_CHARACTER; + } + utf_traits::encode(c, inserter); + } + return result; + } + + } // namespace utf +} // namespace nowide + +#endif diff --git a/include/nowide/utf/utf.hpp b/include/nowide/utf/utf.hpp new file mode 100644 index 0000000..c862361 --- /dev/null +++ b/include/nowide/utf/utf.hpp @@ -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 +#include + +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 + 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 + 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 + 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 + static code_point decode_valid(Iterator& p); + }; + +#else + + template + struct utf_traits; + + template + struct utf_traits + { + 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 + 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 + 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(*p++) & 0x3F); NOWIDE_FALLTHROUGH; + case 2: c = (c << 6) | (static_cast(*p++) & 0x3F); NOWIDE_FALLTHROUGH; + case 1: c = (c << 6) | (static_cast(*p++) & 0x3F); + } + + return c; + } + + template + static Iterator encode(code_point value, Iterator out) + { + if(value <= 0x7F) + { + *out++ = static_cast(value); + } else if(value <= 0x7FF) + { + *out++ = static_cast((value >> 6) | 0xC0); + *out++ = static_cast((value & 0x3F) | 0x80); + } else if(NOWIDE_LIKELY(value <= 0xFFFF)) + { + *out++ = static_cast((value >> 12) | 0xE0); + *out++ = static_cast(((value >> 6) & 0x3F) | 0x80); + *out++ = static_cast((value & 0x3F) | 0x80); + } else + { + *out++ = static_cast((value >> 18) | 0xF0); + *out++ = static_cast(((value >> 12) & 0x3F) | 0x80); + *out++ = static_cast(((value >> 6) & 0x3F) | 0x80); + *out++ = static_cast((value & 0x3F) | 0x80); + } + return out; + } + }; // utf8 + + template + struct utf_traits + { + 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 + 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 + 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 + static It encode(code_point u, It out) + { + if(NOWIDE_LIKELY(u <= 0xFFFF)) + { + *out++ = static_cast(u); + } else + { + u -= 0x10000; + *out++ = static_cast(0xD800 | (u >> 10)); + *out++ = static_cast(0xDC00 | (u & 0x3FF)); + } + return out; + } + }; // utf16; + + template + struct utf_traits + { + 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 + static code_point decode_valid(It& current) + { + return *current++; + } + + template + 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 + static It encode(code_point u, It out) + { + *out++ = static_cast(u); + return out; + } + + }; // utf32 + +#endif + + } // namespace utf +} // namespace nowide + +#endif diff --git a/include/nowide/windows.hpp b/include/nowide/windows.hpp new file mode 100644 index 0000000..940488a --- /dev/null +++ b/include/nowide/windows.hpp @@ -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 +#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 diff --git a/lib/logvisor.cpp b/lib/logvisor.cpp index 459fba2..33c8313 100644 --- a/lib/logvisor.cpp +++ b/lib/logvisor.cpp @@ -49,14 +49,6 @@ #define BOLD "\x1b[1m" #define NORMAL "\x1b[0m" -#define W_RED L"\x001b[1;31m" -#define W_YELLOW L"\x001b[1;33m" -#define W_GREEN L"\x001b[1;32m" -#define W_MAGENTA L"\x001b[1;35m" -#define W_CYAN L"\x001b[1;36m" -#define W_BOLD L"\x001b[1m" -#define W_NORMAL L"\x001b[0m" - #if _WIN32 #define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE #endif @@ -72,13 +64,6 @@ static Module Log("logvisor"); static std::unordered_map ThreadMap; -static std::wstring Widen(std::string str) { - std::wstring wide(str.size(), L'\0'); - std::locale loc; - std::use_facet>(loc).widen(str.data(), str.data()+str.size(), wide.data()); - return wide; -} - static void AddThreadToMap(const char* name) { auto lk = LockLog(); ThreadMap[std::this_thread::get_id()] = name; @@ -119,16 +104,16 @@ void KillProcessTree() { HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (::Process32FirstW(hSnap, &pe)) { + if (::Process32FirstW(hSnap, &pe) == TRUE) { BOOL bContinue = TRUE; // kill child processes - while (bContinue) { + while (bContinue == TRUE) { // only kill child processes and let console window remain if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) { HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); - if (hChildProc) { + if (hChildProc != nullptr) { ::TerminateProcess(hChildProc, 1); ::CloseHandle(hChildProc); } @@ -148,9 +133,9 @@ void KillProcessTree() { HANDLE process; process = GetCurrentProcess(); - SymInitialize(process, NULL, TRUE); - frames = CaptureStackBackTrace(0, 100, stack, NULL); - symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1); + SymInitialize(process, nullptr, TRUE); + frames = CaptureStackBackTrace(0, 100, stack, nullptr); + symbol = static_cast(calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1)); symbol->MaxNameLen = 255; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); @@ -268,28 +253,6 @@ void KillProcessTree() {} LogMutex _LogMutex; -#if _WIN32 -static void AbortHandler(int signum) { - _LogMutex.enabled = false; - switch (signum) { - case SIGSEGV: - Log.report(logvisor::Fatal, FMT_STRING(L"Segmentation Fault")); - break; - case SIGILL: - Log.report(logvisor::Fatal, FMT_STRING(L"Bad Execution")); - break; - case SIGFPE: - Log.report(logvisor::Fatal, FMT_STRING(L"Floating Point Exception")); - break; - case SIGABRT: - Log.report(logvisor::Fatal, FMT_STRING(L"Abort Signal")); - break; - default: - Log.report(logvisor::Fatal, FMT_STRING(L"unknown signal {}"), signum); - break; - } -} -#else static void AbortHandler(int signum) { _LogMutex.enabled = false; switch (signum) { @@ -310,15 +273,14 @@ static void AbortHandler(int signum) { break; } } -#endif uint64_t _LogCounter; std::vector> MainLoggers; std::atomic_size_t ErrorCount(0); -static std::chrono::steady_clock MonoClock; -static std::chrono::steady_clock::time_point GlobalStart = MonoClock.now(); -static inline std::chrono::steady_clock::duration CurrentUptime() { return MonoClock.now() - GlobalStart; } +using MonoClock = std::chrono::steady_clock; +static MonoClock::time_point GlobalStart = MonoClock::now(); +static inline MonoClock::duration CurrentUptime() { return MonoClock::now() - GlobalStart; } std::atomic_uint_fast64_t FrameIndex(0); static inline int ConsoleWidth() { @@ -603,7 +565,7 @@ struct ConsoleLogger : public ILogger { static void _reportHead(const char* modName, const char* sourceInfo, Level severity) { /* Clear current line out */ - std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' '); + // std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' '); const std::chrono::steady_clock::duration tm = CurrentUptime(); const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num / @@ -715,120 +677,6 @@ struct ConsoleLogger : public ILogger { } } - static void _reportHead(const wchar_t* modName, const wchar_t* sourceInfo, Level severity) { - /* Clear current line out */ - std::fwprintf(stderr, L"\r%*c\r", ConsoleWidth(), L' '); - - const std::chrono::steady_clock::duration tm = CurrentUptime(); - const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num / - static_cast(std::chrono::steady_clock::duration::period::den); - const std::thread::id thrId = std::this_thread::get_id(); - std::optional thrName; - if (ThreadMap.find(thrId) != ThreadMap.end()) - thrName = Widen(ThreadMap[thrId]); - - if (XtermColor) { - std::fputws(W_BOLD L"[", stderr); - fmt::print(stderr, FMT_STRING(W_GREEN L"{:.4f} "), tmd); - const uint_fast64_t fIdx = FrameIndex.load(); - if (fIdx != 0) - fmt::print(stderr, FMT_STRING(L"({}) "), fIdx); - switch (severity) { - case Info: - std::fputws(W_BOLD W_CYAN L"INFO", stderr); - break; - case Warning: - std::fputws(W_BOLD W_YELLOW L"WARNING", stderr); - break; - case Error: - std::fputws(W_RED W_BOLD L"ERROR", stderr); - break; - case Fatal: - std::fputws(W_BOLD W_RED L"FATAL ERROR", stderr); - break; - default: - break; - }; - fmt::print(stderr, FMT_STRING(W_NORMAL W_BOLD L" {}"), modName); - if (sourceInfo) - fmt::print(stderr, FMT_STRING(W_BOLD W_YELLOW L" {{}}"), sourceInfo); - if (thrName) - fmt::print(stderr, FMT_STRING(W_BOLD W_MAGENTA L" ({})"), *thrName); - std::fputws(W_NORMAL W_BOLD L"] " W_NORMAL, stderr); - } else { -#if _WIN32 -#if !WINDOWS_STORE - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE); - std::fputwc(L'[', stderr); - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN); - fmt::print(stderr, FMT_STRING(L"{:.4f} "), tmd); - const uint64_t fi = FrameIndex.load(); - if (fi != 0) - std::fwprintf(stderr, L"(%" PRIu64 L") ", fi); - switch (severity) { - case Info: - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE); - std::fputws(L"INFO", stderr); - break; - case Warning: - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN); - std::fputws(L"WARNING", stderr); - break; - case Error: - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED); - std::fputws(L"ERROR", stderr); - break; - case Fatal: - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED); - std::fputws(L"FATAL ERROR", stderr); - break; - default: - break; - } - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE); - fmt::print(stderr, FMT_STRING(L" {}"), modName); - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN); - if (sourceInfo) - fmt::print(stderr, FMT_STRING(L" {{}}"), sourceInfo); - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE); - if (thrName) - fmt::print(stderr, FMT_STRING(L" ({})"), *thrName); - SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE); - std::fputws(L"] ", stderr); - SetConsoleTextAttribute(Term, FOREGROUND_WHITE); -#endif -#else - std::fputwc(L'[', stderr); - fmt::print(stderr, FMT_STRING(L"{:.4f} "), tmd); - uint_fast64_t fIdx = FrameIndex.load(); - if (fIdx) - fmt::print(stderr, FMT_STRING(L"({}) "), fIdx); - switch (severity) { - case Info: - std::fputws(L"INFO", stderr); - break; - case Warning: - std::fputws(L"WARNING", stderr); - break; - case Error: - std::fputws(L"ERROR", stderr); - break; - case Fatal: - std::fputws(L"FATAL ERROR", stderr); - break; - default: - break; - } - fmt::print(stderr, FMT_STRING(L" {}"), modName); - if (sourceInfo) - fmt::print(stderr, FMT_STRING(L" {{}}"), sourceInfo); - if (thrName) - fmt::print(stderr, FMT_STRING(L" ({})"), *thrName); - std::fputws(L"] ", stderr); -#endif - } - } - void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override { _reportHead(modName, nullptr, severity); fmt::vprint(stderr, format, args); @@ -836,13 +684,6 @@ struct ConsoleLogger : public ILogger { std::fflush(stderr); } - void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override { - _reportHead(Widen(modName).c_str(), nullptr, severity); - fmt::vprint(stderr, format, args); - std::fputwc(L'\n', stderr); - std::fflush(stderr); - } - void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format, fmt::format_args args) override { _reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity); @@ -850,14 +691,6 @@ struct ConsoleLogger : public ILogger { std::fputc('\n', 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(Widen(modName).c_str(), fmt::format(FMT_STRING(L"{}:{}"), Widen(file).c_str(), linenum).c_str(), severity); - fmt::vprint(stderr, format, args); - std::fputwc(L'\n', stderr); - std::fflush(stderr); - } }; #endif @@ -868,6 +701,11 @@ void RegisterConsoleLogger() { if (!ConsoleLoggerRegistered) { MainLoggers.emplace_back(new ConsoleLogger); ConsoleLoggerRegistered = true; +#if _WIN32 && 0 + if (GetACP() != CP_UTF8) { + Log.report(Fatal, FMT_STRING("UTF-8 codepage not active! (Windows 10 1903+ required)")); + } +#endif } } @@ -971,48 +809,6 @@ struct FileLogger : public ILogger { std::fputs("] ", fp); } - void _reportHead(const wchar_t* modName, const wchar_t* sourceInfo, Level severity) { - const std::chrono::steady_clock::duration tm = CurrentUptime(); - const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num / - static_cast(std::chrono::steady_clock::duration::period::den); - const std::thread::id thrId = std::this_thread::get_id(); - const wchar_t* thrName = nullptr; - if (ThreadMap.find(thrId) != ThreadMap.end()) { - thrName = Widen(ThreadMap[thrId]).c_str(); - } - - std::fputwc(L'[', fp); - std::fwprintf(fp, L"%5.4f ", tmd); - const uint_fast64_t fIdx = FrameIndex.load(); - if (fIdx != 0) { - std::fwprintf(fp, L"(%" PRIu64 L") ", fIdx); - } - switch (severity) { - case Info: - std::fputws(L"INFO", fp); - break; - case Warning: - std::fputws(L"WARNING", fp); - break; - case Error: - std::fputws(L"ERROR", fp); - break; - case Fatal: - std::fputws(L"FATAL ERROR", fp); - break; - default: - break; - }; - std::fwprintf(fp, L" %s", modName); - if (sourceInfo) { - std::fwprintf(fp, L" {%s}", sourceInfo); - } - if (thrName) { - std::fwprintf(fp, L" (%s)", thrName); - } - std::fputws(L"] ", fp); - } - void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override { openFileIfNeeded(); _reportHead(modName, nullptr, severity); @@ -1020,13 +816,6 @@ struct FileLogger : public ILogger { std::fputc('\n', fp); } - void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override { - openFileIfNeeded(); - _reportHead(Widen(modName).c_str(), nullptr, severity); - fmt::vprint(fp, format, args); - std::fputwc(L'\n', fp); - } - void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format, fmt::format_args args) override { openFileIfNeeded(); @@ -1034,20 +823,12 @@ struct FileLogger : public ILogger { fmt::vprint(fp, format, args); 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(Widen(modName).c_str(), Widen(fmt::format(FMT_STRING("{}:{}"), file, linenum)).c_str(), severity); - fmt::vprint(fp, format, args); - std::fputwc(L'\n', fp); - } }; struct FileLogger8 : public FileLogger { const char* m_filepath; - FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {} - void openFile() override { fp = std::fopen(m_filepath, "a"); } + explicit FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {} + void openFile() override { fp = std::fopen(m_filepath, "ae"); } ~FileLogger8() override = default; }; @@ -1056,30 +837,4 @@ void RegisterFileLogger(const char* 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(logger.get()); - if (!wcscmp(filepath, fl->m_filepath)) { - return; - } - } - } - - /* Otherwise construct new file logger */ - MainLoggers.emplace_back(new FileLogger16(filepath)); -} - -#endif - } // namespace logvisor