diff --git a/CMakeLists.txt b/CMakeLists.txt index 95ba5236..ec9f5da2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,11 +191,11 @@ set(SPDLOG_HEADERS "include/spdlog/details/periodic_worker.h" "include/spdlog/details/registry.h" "include/spdlog/details/synchronous_factory.h" - "include/spdlog/details/tcp_client-windows.h" - "include/spdlog/details/tcp_client.h" + + "include/spdlog/details/tcp_client_unix.h" "include/spdlog/details/thread_pool.h" - "include/spdlog/details/udp_client-windows.h" - "include/spdlog/details/udp_client.h" + "include/spdlog/details/udp_client_windows.h" + "include/spdlog/details/udp_client_unix.h" "include/spdlog/details/windows_include.h" "include/spdlog/fmt/bin_to_hex.h" "include/spdlog/fmt/fmt.h" @@ -236,7 +236,6 @@ set(SPDLOG_SRCS "src/details/file_helper.cpp" "src/details/log_msg.cpp" "src/details/log_msg_buffer.cpp" - "src/details/os.cpp" "src/details/periodic_worker.cpp" "src/details/registry.cpp" "src/details/thread_pool.cpp" @@ -249,6 +248,15 @@ set(SPDLOG_SRCS "src/sinks/stdout_sinks.cpp" "src/sinks/wincolor_sink.cpp") +if(WIN32) + list(APPEND SPDLOG_SRCS "src/details/os_windows.cpp") + list(APPEND SPDLOG_HEADERS "include/spdlog/details/tcp_client_windows.h") + list(APPEND SPDLOG_HEADERS "include/spdlog/details/udp_client_windows.h") +else() + list(APPEND SPDLOG_SRCS "src/details/os_unix.cpp") + list(APPEND SPDLOG_HEADERS "include/spdlog/details/tcp_client_unix.h") + list(APPEND SPDLOG_HEADERS "include/spdlog/details/udp_client_unix.h") +endif() # Generate spdlog_config.h based on the current configuration set(OUT_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/include/spdlog/spdlog_config.h") message(STATUS "Generating ${OUT_CONFIG_FILE}") @@ -256,7 +264,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/spdlog_config.h.in" ${OUT_CONF list(APPEND SPDLOG_HEADERS ${OUT_CONFIG_FILE}) if(BUILD_SHARED_LIBS) if(WIN32) - set(VERSION_RC ${CMAKE_CURRENT_BINARY_DIR}/version.rc ) + set(VERSION_RC ${CMAKE_CURRENT_BINARY_DIR}/version.rc + src/details/os_unix.cpp) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY) endif() add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_HEADERS} ${VERSION_RC}) diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client_unix.h similarity index 97% rename from include/spdlog/details/tcp_client.h rename to include/spdlog/details/tcp_client_unix.h index f92a3684..9364eda5 100644 --- a/include/spdlog/details/tcp_client.h +++ b/include/spdlog/details/tcp_client_unix.h @@ -4,7 +4,7 @@ #pragma once #ifdef _WIN32 - #error include tcp_client-windows.h instead + #error include tcp_client_windows.h instead #endif // tcp client helper @@ -23,7 +23,7 @@ namespace spdlog { namespace details { -class tcp_client { +class tcp_client_unix { int socket_ = -1; public: @@ -38,7 +38,7 @@ public: int fd() const { return socket_; } - ~tcp_client() { close(); } + ~tcp_client_unix() { close(); } // try to connect or throw on failure void connect(const std::string &host, int port) { diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client_windows.h similarity index 97% rename from include/spdlog/details/tcp_client-windows.h rename to include/spdlog/details/tcp_client_windows.h index 49710c92..c626e473 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client_windows.h @@ -22,7 +22,7 @@ namespace spdlog { namespace details { -class tcp_client { +class tcp_client_unix { SOCKET socket_ = INVALID_SOCKET; static void init_winsock_() { @@ -42,9 +42,9 @@ class tcp_client { } public: - tcp_client() { init_winsock_(); } + tcp_client_unix() { init_winsock_(); } - ~tcp_client() { + ~tcp_client_unix() { close(); ::WSACleanup(); } diff --git a/include/spdlog/details/udp_client.h b/include/spdlog/details/udp_client_unix.h similarity index 92% rename from include/spdlog/details/udp_client.h rename to include/spdlog/details/udp_client_unix.h index e0cc4432..1b46da4a 100644 --- a/include/spdlog/details/udp_client.h +++ b/include/spdlog/details/udp_client_unix.h @@ -10,7 +10,7 @@ #include "./os.h" #ifdef _WIN32 - #error "include udp_client-windows.h instead" + #error "include udp_client_windows.h instead" #endif #include @@ -26,7 +26,7 @@ namespace spdlog { namespace details { -class udp_client { +class udp_client_unix { static constexpr int TX_BUFFER_SIZE = 1024 * 10; int socket_ = -1; struct sockaddr_in sockAddr_; @@ -39,7 +39,7 @@ class udp_client { } public: - udp_client(const std::string &host, uint16_t port) { + udp_client_unix(const std::string &host, uint16_t port) { socket_ = ::socket(PF_INET, SOCK_DGRAM, 0); if (socket_ < 0) { throw_spdlog_ex("error: Create Socket Failed!"); @@ -63,7 +63,7 @@ public: ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero)); } - ~udp_client() { cleanup_(); } + ~udp_client_unix() { cleanup_(); } int fd() const { return socket_; } diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client_windows.h similarity index 95% rename from include/spdlog/details/udp_client-windows.h rename to include/spdlog/details/udp_client_windows.h index 8bae36b0..3b952115 100644 --- a/include/spdlog/details/udp_client-windows.h +++ b/include/spdlog/details/udp_client_windows.h @@ -25,7 +25,7 @@ namespace spdlog { namespace details { -class udp_client { +class udp_client_unix { static constexpr int TX_BUFFER_SIZE = 1024 * 10; SOCKET socket_ = INVALID_SOCKET; sockaddr_in addr_ = {}; @@ -55,7 +55,7 @@ class udp_client { } public: - udp_client(const std::string &host, uint16_t port) { + udp_client_unix(const std::string &host, uint16_t port) { init_winsock_(); addr_.sin_family = PF_INET; @@ -83,7 +83,7 @@ public: } } - ~udp_client() { cleanup_(); } + ~udp_client_unix() { cleanup_(); } SOCKET fd() const { return socket_; } diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h index d6c7b587..9e1d3333 100644 --- a/include/spdlog/sinks/tcp_sink.h +++ b/include/spdlog/sinks/tcp_sink.h @@ -9,7 +9,7 @@ #ifdef _WIN32 #include "../details/tcp_client-windows.h" #else - #include "../details/tcp_client.h" + #include "../details/tcp_client_unix.h" #endif #include @@ -65,7 +65,7 @@ protected: void flush_() override {} tcp_sink_config config_; - details::tcp_client client_; + details::tcp_client_unix client_; }; using tcp_sink_mt = tcp_sink; diff --git a/include/spdlog/sinks/udp_sink.h b/include/spdlog/sinks/udp_sink.h index 1735ecf7..e86bd0b0 100644 --- a/include/spdlog/sinks/udp_sink.h +++ b/include/spdlog/sinks/udp_sink.h @@ -9,7 +9,7 @@ #ifdef _WIN32 #include "../details/udp_client-windows.h" #else - #include "../details/udp_client.h" + #include "../details/udp_client_unix.h" #endif #include @@ -49,7 +49,7 @@ protected: } void flush_() override {} - details::udp_client client_; + details::udp_client_unix client_; }; using udp_sink_mt = udp_sink; diff --git a/src/details/os.cpp b/src/details/os_unix.cpp similarity index 55% rename from src/details/os.cpp rename to src/details/os_unix.cpp index 790a2217..c4e0390a 100644 --- a/src/details/os.cpp +++ b/src/details/os_unix.cpp @@ -1,9 +1,9 @@ // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -#include "spdlog/details/os.h" - +#include #include +#include #include #include @@ -16,47 +16,23 @@ #include #include "spdlog/common.h" +#include "spdlog/details/os.h" -// clang-format off -#ifdef _WIN32 - #include "spdlog/details/windows_include.h" - #include // for FlushFileBuffers - #include // for _get_osfhandle, _isatty, _fileno - #include // for _get_pid +#ifdef __linux__ + #include //Use gettid() syscall under linux to get thread id - #ifdef __MINGW32__ - #include - #endif +#elif defined(_AIX) + #include // for pthread_getthrds_np - #if defined(SPDLOG_WCHAR_FILENAMES) - #include - #include - #endif +#elif defined(__DragonFly__) || defined(__FreeBSD__) + #include // for pthread_getthreadid_np - #include // for _mkdir/_wmkdir +#elif defined(__NetBSD__) + #include // for _lwp_self -#else // unix - - #include - #include - - #ifdef __linux__ - #include //Use gettid() syscall under linux to get thread id - - #elif defined(_AIX) - #include // for pthread_getthrds_np - - #elif defined(__DragonFly__) || defined(__FreeBSD__) - #include // for pthread_getthreadid_np - - #elif defined(__NetBSD__) - #include // for _lwp_self - - #elif defined(__sun) - #include // for thr_self - #endif - -#endif // _WIN32 +#elif defined(__sun) + #include // for thr_self +#endif #if defined __APPLE__ #include @@ -84,15 +60,9 @@ spdlog::log_clock::time_point now() noexcept { #endif } std::tm localtime(const std::time_t &time_tt) noexcept { -#ifdef _WIN32 - std::tm tm; - const auto rv = ::localtime_s(&tm, &time_tt); - return rv == 0 ? tm : std::tm{}; -#else std::tm tm; const auto *rv = ::localtime_r(&time_tt, &tm); return rv != nullptr ? tm : std::tm{}; -#endif } std::tm localtime() noexcept { @@ -101,13 +71,8 @@ std::tm localtime() noexcept { } std::tm gmtime(const std::time_t &time_tt) noexcept { -#ifdef _WIN32 - std::tm tm; - ::gmtime_s(&tm, &time_tt); -#else std::tm tm; ::gmtime_r(&time_tt, &tm); -#endif return tm; } @@ -118,23 +83,7 @@ std::tm gmtime() noexcept { // fopen_s on non windows for writing bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) { -#ifdef _WIN32 - #ifdef SPDLOG_WCHAR_FILENAMES - *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); - #else - *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); - #endif - #if defined(SPDLOG_PREVENT_CHILD_FD) - if (*fp != nullptr) { - auto file_handle = reinterpret_cast(_get_osfhandle(::_fileno(*fp))); - if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) { - ::fclose(*fp); - *fp = nullptr; - } - } - #endif -#else // unix - #if defined(SPDLOG_PREVENT_CHILD_FD) +#if defined(SPDLOG_PREVENT_CHILD_FD) const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC; const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644)); if (fd == -1) { @@ -144,124 +93,58 @@ bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) { if (*fp == nullptr) { ::close(fd); } - #else +#else *fp = ::fopen((filename.c_str()), mode.c_str()); - #endif #endif - return *fp == nullptr; } -int remove(const filename_t &filename) noexcept { -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return ::_wremove(filename.c_str()); -#else - return std::remove(filename.c_str()); -#endif -} +int remove(const filename_t &filename) noexcept { return std::remove(filename.c_str()); } int remove_if_exists(const filename_t &filename) noexcept { return path_exists(filename) ? remove(filename) : 0; } int rename(const filename_t &filename1, const filename_t &filename2) noexcept { -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return ::_wrename(filename1.c_str(), filename2.c_str()); -#else return std::rename(filename1.c_str(), filename2.c_str()); -#endif } // Return true if path exists (file or directory) bool path_exists(const filename_t &filename) noexcept { -#ifdef _WIN32 - #ifdef SPDLOG_WCHAR_FILENAMES - auto attribs = ::GetFileAttributesW(filename.c_str()); - #else - auto attribs = ::GetFileAttributesA(filename.c_str()); - #endif - return attribs != INVALID_FILE_ATTRIBUTES; -#else // common linux/unix all have the stat system call struct stat buffer; return (::stat(filename.c_str(), &buffer) == 0); -#endif } -#ifdef _MSC_VER - // avoid warning about unreachable statement at the end of filesize() - #pragma warning(push) - #pragma warning(disable : 4702) -#endif - // Return file size according to open FILE* object size_t filesize(FILE *f) { if (f == nullptr) { throw_spdlog_ex("Failed getting file size. fd is null"); } -#if defined(_WIN32) && !defined(__CYGWIN__) - int fd = ::_fileno(f); - #if defined(_WIN64) // 64 bits - __int64 ret = ::_filelengthi64(fd); - if (ret >= 0) { - return static_cast(ret); - } - #else // windows 32 bits - long ret = ::_filelength(fd); - if (ret >= 0) { - return static_cast(ret); - } - #endif - -#else // unix // OpenBSD and AIX doesn't compile with :: before the fileno(..) - #if defined(__OpenBSD__) || defined(_AIX) +#if defined(__OpenBSD__) || defined(_AIX) int fd = fileno(f); - #else +#else int fd = ::fileno(f); - #endif +#endif // 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated) - #if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64)) +#if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64)) struct stat64 st; if (::fstat64(fd, &st) == 0) { return static_cast(st.st_size); } - #else // other unix or linux 32 bits or cygwin +#else // other unix or linux 32 bits or cygwin struct stat st; if (::fstat(fd, &st) == 0) { return static_cast(st.st_size); } - #endif #endif throw_spdlog_ex("Failed getting file size from fd", errno); return 0; // will not be reached. } -#ifdef _MSC_VER - #pragma warning(pop) -#endif - // Return utc offset in minutes or throw spdlog_ex on failure int utc_minutes_offset(const std::tm &tm) { -#ifdef _WIN32 - #if _WIN32_WINNT < _WIN32_WINNT_WS08 - TIME_ZONE_INFORMATION tzinfo; - auto rv = ::GetTimeZoneInformation(&tzinfo); - #else - DYNAMIC_TIME_ZONE_INFORMATION tzinfo; - auto rv = ::GetDynamicTimeZoneInformation(&tzinfo); - #endif - if (rv == TIME_ZONE_ID_INVALID) throw_spdlog_ex("Failed getting timezone info. ", errno); - - int offset = -tzinfo.Bias; - if (tm.tm_isdst) { - offset -= tzinfo.DaylightBias; - } else { - offset -= tzinfo.StandardBias; - } - return offset; -#else - - #if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \ - (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) +#if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \ + (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris struct helper { static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), @@ -290,21 +173,17 @@ int utc_minutes_offset(const std::tm &tm) { }; auto offset_seconds = helper::calculate_gmt_offset(tm); - #else +#else auto offset_seconds = tm.tm_gmtoff; - #endif - - return static_cast(offset_seconds / 60); #endif + return static_cast(offset_seconds / 60); } // Return current thread id as size_t // It exists because the std::this_thread::get_id() is much slower(especially // under VS 2013) size_t _thread_id() noexcept { -#ifdef _WIN32 - return static_cast(::GetCurrentThreadId()); -#elif defined(__linux__) +#if defined(__linux__) #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) #define SYS_gettid __NR_gettid #endif @@ -358,42 +237,17 @@ size_t thread_id() noexcept { return tid; } -// This is avoid msvc issue in sleep_for that happens if the clock changes. -// See https://github.com/gabime/spdlog/issues/609 void sleep_for_millis(unsigned int milliseconds) noexcept { -#if defined(_WIN32) - ::Sleep(milliseconds); -#else std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); -#endif } -// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -std::string filename_to_str(const filename_t &filename) { - memory_buf_t buf; - wstr_to_utf8buf(filename, buf); - return SPDLOG_BUF_TO_STRING(buf); -} -#else std::string filename_to_str(const filename_t &filename) { return filename; } -#endif -int pid() noexcept { -#ifdef _WIN32 - return static_cast(::GetCurrentProcessId()); -#else - return static_cast(::getpid()); -#endif -} +int pid() noexcept { return static_cast(::getpid()); } // Determine if the terminal supports colors // Based on: https://github.com/agauniyal/rang/ bool is_color_terminal() noexcept { -#ifdef _WIN32 - return true; -#else - static const bool result = []() { const char *env_colorterm_p = std::getenv("COLORTERM"); if (env_colorterm_p != nullptr) { @@ -414,88 +268,14 @@ bool is_color_terminal() noexcept { }(); return result; -#endif } // Determine if the terminal attached // Source: https://github.com/agauniyal/rang/ -bool in_terminal(FILE *file) noexcept { -#ifdef _WIN32 - return ::_isatty(_fileno(file)) != 0; -#else - return ::isatty(fileno(file)) != 0; -#endif -} - -#if defined(SPDLOG_WCHAR_FILENAMES) && defined(_WIN32) -void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) { - if (wstr.size() > static_cast((std::numeric_limits::max)()) / 2 - 1) { - throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8"); - } - - int wstr_size = static_cast(wstr.size()); - if (wstr_size == 0) { - target.resize(0); - return; - } - - int result_size = static_cast(target.capacity()); - if ((wstr_size + 1) * 2 > result_size) { - result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL); - } - - if (result_size > 0) { - target.resize(result_size); - result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL); - - if (result_size > 0) { - target.resize(result_size); - return; - } - } - - throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); -} - -void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) { - if (str.size() > static_cast((std::numeric_limits::max)()) - 1) { - throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16"); - } - - int str_size = static_cast(str.size()); - if (str_size == 0) { - target.resize(0); - return; - } - - // find the size to allocate for the result buffer - int result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0); - - if (result_size > 0) { - target.resize(result_size); - result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size); - if (result_size > 0) { - assert(result_size == target.size()); - return; - } - } - - throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError())); -} -#endif // defined(SPDLOG_WCHAR_FILENAMES) && defined(_WIN32) +bool in_terminal(FILE *file) noexcept { return ::isatty(fileno(file)) != 0; } // return true on success -static bool mkdir_(const filename_t &path) { -#ifdef _WIN32 - #ifdef SPDLOG_WCHAR_FILENAMES - return ::_wmkdir(path.c_str()) == 0; - #else - return ::_mkdir(path.c_str()) == 0; - #endif -#else - return ::mkdir(path.c_str(), mode_t(0755)) == 0; -#endif -} +static bool mkdir_(const filename_t &path) { return ::mkdir(path.c_str(), mode_t(0755)) == 0; } // create the given directory - and all directories leading to it // return true on success or if the directory already exists @@ -538,30 +318,13 @@ filename_t dir_name(const filename_t &path) { } std::string getenv(const char *field) { -#if defined(_MSC_VER) - #if defined(__cplusplus_winrt) - return std::string{}; // not supported under uwp - #else - size_t len = 0; - char buf[128]; - bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0; - return ok ? buf : std::string{}; - #endif -#else // revert to getenv char *buf = ::getenv(field); return buf != nullptr ? buf : std::string{}; -#endif } // Do fsync by FILE handlerpointer // Return true on success -bool fsync(FILE *fp) { -#ifdef _WIN32 - return FlushFileBuffers(reinterpret_cast(_get_osfhandle(_fileno(fp)))) != 0; -#else - return ::fsync(fileno(fp)) == 0; -#endif -} +bool fsync(FILE *fp) { return ::fsync(fileno(fp)) == 0; } } // namespace os } // namespace details diff --git a/src/details/os_windows.cpp b/src/details/os_windows.cpp new file mode 100644 index 00000000..0f87a678 --- /dev/null +++ b/src/details/os_windows.cpp @@ -0,0 +1,328 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef _WIN32 + #error "_WIN32 only source file" +#endif + +#include // for FlushFileBuffers +#include // for _get_osfhandle, _isatty, _fileno +#include // for _get_pid +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spdlog/common.h" +#include "spdlog/details/os.h" +#include "spdlog/details/windows_include.h" + +#ifdef __MINGW32__ + #include +#endif + +#if defined(SPDLOG_WCHAR_FILENAMES) + #include + #include +#endif + +#include // for _mkdir/_wmkdir + +// clang-format on +namespace spdlog { +namespace details { +namespace os { +spdlog::log_clock::time_point now() noexcept { return log_clock::now(); } + +std::tm localtime(const std::time_t &time_tt) noexcept { + std::tm tm; + const auto rv = ::localtime_s(&tm, &time_tt); + return rv == 0 ? tm : std::tm{}; +} + +std::tm localtime() noexcept { + std::time_t now_t = ::time(nullptr); + return localtime(now_t); +} + +std::tm gmtime(const std::time_t &time_tt) noexcept { + std::tm tm; + ::gmtime_s(&tm, &time_tt); + return tm; +} + +std::tm gmtime() noexcept { + std::time_t now_t = ::time(nullptr); + return gmtime(now_t); +} + +bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) { +#ifdef SPDLOG_WCHAR_FILENAMES + *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); +#else + *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); +#endif +#if defined(SPDLOG_PREVENT_CHILD_FD) + if (*fp != nullptr) { + auto file_handle = reinterpret_cast(_get_osfhandle(::_fileno(*fp))); + if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) { + ::fclose(*fp); + *fp = nullptr; + } + } +#endif + return *fp == nullptr; +} + +int remove(const filename_t &filename) noexcept { +#if defined(SPDLOG_WCHAR_FILENAMES) + return ::_wremove(filename.c_str()); +#else + return std::remove(filename.c_str()); +#endif +} + +int remove_if_exists(const filename_t &filename) noexcept { return path_exists(filename) ? remove(filename) : 0; } + +int rename(const filename_t &filename1, const filename_t &filename2) noexcept { +#if defined(SPDLOG_WCHAR_FILENAMES) + return ::_wrename(filename1.c_str(), filename2.c_str()); +#else + return std::rename(filename1.c_str(), filename2.c_str()); +#endif +} + +// Return true if path exists (file or directory) +bool path_exists(const filename_t &filename) noexcept { +#ifdef SPDLOG_WCHAR_FILENAMES + auto attribs = ::GetFileAttributesW(filename.c_str()); +#else + auto attribs = ::GetFileAttributesA(filename.c_str()); +#endif + return attribs != INVALID_FILE_ATTRIBUTES; +} + +#ifdef _MSC_VER +// avoid warning about unreachable statement at the end of filesize() + #pragma warning(push) + #pragma warning(disable : 4702) +#endif + +// Return file size according to open FILE* object +size_t filesize(FILE *f) { + if (f == nullptr) { + throw_spdlog_ex("Failed getting file size. fd is null"); + } + int fd = ::_fileno(f); +#if defined(_WIN64) // 64 bits + __int64 ret = ::_filelengthi64(fd); + if (ret >= 0) { + return static_cast(ret); + } + +#else // windows 32 bits + long ret = ::_filelength(fd); + if (ret >= 0) { + return static_cast(ret); + } +#endif + throw_spdlog_ex("Failed getting file size from fd", errno); + return 0; // will not be reached. +} + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +// Return utc offset in minutes or throw spdlog_ex on failure +int utc_minutes_offset(const std::tm &tm) { +#if _WIN32_WINNT < _WIN32_WINNT_WS08 + TIME_ZONE_INFORMATION tzinfo; + auto rv = ::GetTimeZoneInformation(&tzinfo); +#else + DYNAMIC_TIME_ZONE_INFORMATION tzinfo; + auto rv = ::GetDynamicTimeZoneInformation(&tzinfo); +#endif + if (rv == TIME_ZONE_ID_INVALID) throw_spdlog_ex("Failed getting timezone info. ", errno); + + int offset = -tzinfo.Bias; + if (tm.tm_isdst) { + offset -= tzinfo.DaylightBias; + } else { + offset -= tzinfo.StandardBias; + } + return offset; +} + +// Return current thread id as size_t +// It exists because the std::this_thread::get_id() is much slower(especially +// under VS 2013) +size_t _thread_id() noexcept { return static_cast(::GetCurrentThreadId()); } + +// Return current thread id as size_t (from thread local storage) +size_t thread_id() noexcept { + // cache thread id in tls + static thread_local const size_t tid = _thread_id(); + return tid; +} + +// This is avoid msvc issue in sleep_for that happens if the clock changes. +// See https://github.com/gabime/spdlog/issues/609 +void sleep_for_millis(unsigned int milliseconds) noexcept { ::Sleep(milliseconds); } + +// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) +#if defined(SPDLOG_WCHAR_FILENAMES) +std::string filename_to_str(const filename_t &filename) { + memory_buf_t buf; + wstr_to_utf8buf(filename, buf); + return SPDLOG_BUF_TO_STRING(buf); +} +#else +std::string filename_to_str(const filename_t &filename) { return filename; } +#endif + +int pid() noexcept { return static_cast(::GetCurrentProcessId()); } + + +bool is_color_terminal() noexcept { return true; } + +// Determine if the terminal attached +bool in_terminal(FILE *file) noexcept { return ::_isatty(_fileno(file)) != 0; } + +#if defined(SPDLOG_WCHAR_FILENAMES) +void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) { + if (wstr.size() > static_cast((std::numeric_limits::max)()) / 2 - 1) { + throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8"); + } + + int wstr_size = static_cast(wstr.size()); + if (wstr_size == 0) { + target.resize(0); + return; + } + + int result_size = static_cast(target.capacity()); + if ((wstr_size + 1) * 2 > result_size) { + result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL); + } + + if (result_size > 0) { + target.resize(result_size); + result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL); + + if (result_size > 0) { + target.resize(result_size); + return; + } + } + + throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); +} + +void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) { + if (str.size() > static_cast((std::numeric_limits::max)()) - 1) { + throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16"); + } + + int str_size = static_cast(str.size()); + if (str_size == 0) { + target.resize(0); + return; + } + + // find the size to allocate for the result buffer + int result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0); + + if (result_size > 0) { + target.resize(result_size); + result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size); + if (result_size > 0) { + assert(result_size == target.size()); + return; + } + } + + throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError())); +} +#endif // defined(SPDLOG_WCHAR_FILENAMES) + +// return true on success +static bool mkdir_(const filename_t &path) { +# +#ifdef SPDLOG_WCHAR_FILENAMES + return ::_wmkdir(path.c_str()) == 0; +#else + return ::_mkdir(path.c_str()) == 0; +#endif +} + +// create the given directory - and all directories leading to it +// return true on success or if the directory already exists +bool create_dir(const filename_t &path) { + if (path_exists(path)) { + return true; + } + + if (path.empty()) { + return false; + } + + size_t search_offset = 0; + do { + auto token_pos = path.find_first_of(folder_seps_filename, search_offset); + // treat the entire path as a folder if no folder separator not found + if (token_pos == filename_t::npos) { + token_pos = path.size(); + } + + auto subdir = path.substr(0, token_pos); + + if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) { + return false; // return error if failed creating dir + } + search_offset = token_pos + 1; + } while (search_offset < path.size()); + + return true; +} + +// Return directory name from given path or empty string +// "abc/file" => "abc" +// "abc/" => "abc" +// "abc" => "" +// "abc///" => "abc//" +filename_t dir_name(const filename_t &path) { + auto pos = path.find_last_of(folder_seps_filename); + return pos != filename_t::npos ? path.substr(0, pos) : filename_t{}; +} + +std::string getenv(const char *field) { +#if defined(_MSC_VER) + #if defined(__cplusplus_winrt) + return std::string{}; // not supported under uwp + #else + size_t len = 0; + char buf[128]; + bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0; + return ok ? buf : std::string{}; + #endif +#else // revert to getenv + char *buf = ::getenv(field); + return buf != nullptr ? buf : std::string{}; +#endif +} + +// Do fsync by FILE handlerpointer +// Return true on success +bool fsync(FILE *fp) { return FlushFileBuffers(reinterpret_cast(_get_osfhandle(_fileno(fp)))) != 0; } + +} // namespace os +} // namespace details +} // namespace spdlog