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.
This commit is contained in:
Luke Street 2021-06-22 15:13:37 -04:00
parent 274ad5ef07
commit df3c283186
13 changed files with 1337 additions and 290 deletions

View File

@ -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 ()

2
fmt

@ -1 +1 @@
Subproject commit 95da4847274c42c110fd5bf1bf61d3f94be4a08e
Subproject commit 9e8b86fd2d9806672cc73133d21780dd182bfd24

View File

@ -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<std::recursive_mutex>(mutex);
else
return std::unique_lock<std::recursive_mutex>();
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
*/

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

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

@ -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 <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(__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

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

@ -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<std::thread::id, const char*> ThreadMap;
static std::wstring Widen(std::string str) {
std::wstring wide(str.size(), L'\0');
std::locale loc;
std::use_facet<std::ctype<wchar_t>>(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<SYMBOL_INFO*>(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<std::unique_ptr<ILogger>> 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() {
@ -506,8 +468,6 @@ struct ConsoleLogger : public ILogger {
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,
fmt::format_args args) override {
if (!m_ready)
@ -566,9 +526,6 @@ struct ConsoleLogger : public ILogger {
SendBuffer(bufOut);
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {}
};
#else
@ -603,7 +560,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 +672,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<double>(std::chrono::steady_clock::duration::period::den);
const std::thread::id thrId = std::this_thread::get_id();
std::optional<std::wstring> 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 +679,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 +686,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 +696,15 @@ void RegisterConsoleLogger() {
if (!ConsoleLoggerRegistered) {
MainLoggers.emplace_back(new ConsoleLogger);
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
}
}
@ -921,7 +758,7 @@ struct FileLogger : public ILogger {
openFile();
}
}
virtual void closeFile() {
virtual void closeFile() {
if (fp) {
std::fclose(fp);
fp = nullptr;
@ -971,48 +808,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<double>(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 +815,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,19 +822,11 @@ 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) {}
explicit FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {}
void openFile() override { fp = std::fopen(m_filepath, "a"); }
~FileLogger8() override = default;
};
@ -1056,30 +836,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<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