Use std filesystem (#3284)

* Use std::filesystem for path names and impl
This commit is contained in:
Gabi Melman
2024-12-05 19:14:25 +02:00
committed by GitHub
parent daf1b97b8f
commit 08c727e4f8
24 changed files with 300 additions and 318 deletions

View File

@@ -1,12 +1,13 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#include "spdlog/details/file_helper.h"
#include <cerrno>
#include <cstdio>
#include <utility>
#include <filesystem>
#include "spdlog/details/file_helper.h"
#include "spdlog/common.h"
#include "spdlog/details/os.h"
@@ -107,37 +108,6 @@ size_t file_helper::size() const {
const filename_t &file_helper::filename() const { return filename_; }
//
// return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname) {
auto ext_index = fname.rfind('.');
// no valid extension found - return whole path and empty string as
// extension
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) {
return std::make_tuple(fname, filename_t());
}
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
if (folder_index != filename_t::npos && folder_index >= ext_index - 1) {
return std::make_tuple(fname, filename_t());
}
// finally - return a valid base and extension tuple
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
}
} // namespace details
} // namespace spdlog

View File

@@ -0,0 +1,73 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#include <filesystem>
#include <string>
#include "spdlog/common.h"
#include "spdlog/details/os.h"
namespace spdlog {
namespace details {
namespace os {
bool remove(const filename_t &filename) {
return std::filesystem::remove(filename);
}
bool remove_if_exists(const filename_t &filename) {
if (path_exists(filename)) {
return os::remove(filename);
}
return false;
}
// Rename if regular file
bool rename(const filename_t &filename1, const filename_t &filename2) noexcept {
if (!std::filesystem::is_regular_file(filename1)) {
return false;
}
std::error_code ec;
std::filesystem::rename(filename1, filename2, ec);
return !ec;
}
// Return true if path exists (file or directory)
bool path_exists(const filename_t &filename) noexcept { return std::filesystem::exists(filename); }
// 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) { return path.parent_path(); }
// 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) {
std::error_code ec;
return std::filesystem::create_directories(path, ec) || !ec;
}
// Return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog", ".")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname) {
const auto ext = fname.extension();
auto without_ext = filename_t(fname).replace_extension();
return std::make_tuple(without_ext, ext);
}
} // namespace os
} // namespace details
} // namespace spdlog

View File

@@ -99,19 +99,6 @@ bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
return *fp == nullptr;
}
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 {
return std::rename(filename1.c_str(), filename2.c_str());
}
// Return true if path exists (file or directory)
bool path_exists(const filename_t &filename) noexcept {
struct stat buffer {};
return (::stat(filename.c_str(), &buffer) == 0);
}
// Return file size according to open FILE* object
size_t filesize(FILE *f) {
@@ -232,16 +219,22 @@ size_t _thread_id() noexcept {
// Return current thread id as size_t (from thread local storage)
size_t thread_id() noexcept {
// cache thread id in tls
#if defined(SPDLOG_NO_TLS)
return _thread_id();
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
void sleep_for_millis(unsigned int milliseconds) noexcept {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
std::string filename_to_str(const filename_t &filename) { return filename; }
std::string filename_to_str(const filename_t &filename) {
static_assert(std::is_same_v<filename_t::value_type, char>, "filename_t type must be char");
return filename;
}
int pid() noexcept { return static_cast<int>(::getpid()); }
@@ -274,49 +267,6 @@ bool is_color_terminal() noexcept {
// Source: https://github.com/agauniyal/rang/
bool in_terminal(FILE *file) noexcept { return ::isatty(fileno(file)) != 0; }
// return true on success
static bool mkdir_(const filename_t &path) { return ::mkdir(path.c_str(), static_cast<mode_t>(0755)) == 0; }
// 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) {
char *buf = ::getenv(field);
return buf != nullptr ? buf : std::string{};

View File

@@ -23,7 +23,6 @@
#include <cstring>
#include <ctime>
#include <string>
#include <thread>
#include "spdlog/common.h"
#include "spdlog/details/os.h"
@@ -63,7 +62,7 @@ std::tm gmtime() noexcept {
}
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#if defined(SPDLOG_PREVENT_CHILD_FD)
if (*fp != nullptr) {
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
@@ -76,19 +75,7 @@ bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
return *fp == nullptr;
}
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 {
return std::rename(filename1.c_str(), filename2.c_str());
}
// Return true if path exists (file or directory)
bool path_exists(const filename_t &filename) noexcept {
struct _stat buffer;
return (::_stat(filename.c_str(), &buffer) == 0);
}
#ifdef _MSC_VER
// avoid warning about unreachable statement at the end of filesize()
@@ -149,16 +136,31 @@ size_t _thread_id() noexcept { return static_cast<size_t>(::GetCurrentThreadId()
// Return current thread id as size_t (from thread local storage)
size_t thread_id() noexcept {
// cache thread id in tls
#if defined(SPDLOG_NO_TLS)
return _thread_id();
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
// 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); }
std::string filename_to_str(const filename_t &filename) { return filename; }
// Try tp convert wstring filename to string. Return "???" if failed
std::string filename_to_str(const filename_t &filename) {
static_assert(std::is_same_v<filename_t::value_type, wchar_t>, "filename_t type must be wchar_t");
try {
memory_buf_t buf;
wstr_to_utf8buf(filename.wstring(), buf);
return std::string(buf.data(), buf.size());
}
catch (...) {
return "???";
}
}
int pid() noexcept { return static_cast<int>(::GetCurrentProcessId()); }
@@ -193,7 +195,7 @@ void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
}
}
throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
throw spdlog_ex("WideCharToMultiByte failed");
}
void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
@@ -222,56 +224,6 @@ void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
}
// return true on success
static bool mkdir_(const filename_t &path) { return ::_mkdir(path.c_str()) == 0; }
// 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 is just a drive letter, add a slash e.g. "c:"=>"c:\",
// otherwise path_exists(subdir) returns false (issue #3079)
const bool is_drive = subdir.length() == 2 && subdir[1] == ':';
if (is_drive) {
subdir += '\\';
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)

View File

@@ -7,11 +7,11 @@
#include <mutex>
#include <string>
#include <tuple>
#include <sstream>
#include "spdlog/common.h"
#include "spdlog/details/file_helper.h"
#include "spdlog/details/os.h"
// #include "spdlog/fmt/fmt.h"
namespace spdlog {
namespace sinks {
@@ -51,8 +51,10 @@ filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename,
filename_t basename;
filename_t ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}.{}{}")), basename, index, ext);
std::tie(basename, ext) = details::os::split_by_extension(filename);
std::basic_ostringstream<filename_t::value_type> oss;
oss << basename.native() << '.' << index << ext.native();
return oss.str();
}
template <typename Mutex>
@@ -130,10 +132,8 @@ void rotating_file_sink<Mutex>::rotate_() {
// delete the target if exists, and rename the src file to target
// return true on success, false otherwise.
template <typename Mutex>
bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename) {
// try to delete the target file in case it already exists.
(void)details::os::remove(target_filename);
return details::os::rename(src_filename, target_filename) == 0;
bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename) noexcept{
return details::os::rename(src_filename, target_filename);
}
} // namespace sinks