// This file Copyright © Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif #include #include #include #include "libtransmission/string-utils.h" #include "libtransmission/tr-assert.h" /* User-level routine. returns whether or not 'text' and 'pattern' matched */ bool tr_wildmat(char const* text, char const* pattern) { // TODO(ckerr): replace wildmat with base/strings/pattern.cc // wildmat wants these to be zero-terminated. return (pattern[0] == '*' && pattern[1] == '\0') || DoMatch(text, pattern) > 0; } char const* tr_strerror(int errnum) { if (char const* const ret = strerror(errnum); ret != nullptr) { return ret; } return "Unknown Error"; } // --- std::string_view tr_strv_strip(std::string_view str) { auto constexpr Test = [](auto ch) { return isspace(static_cast(ch)); }; auto const it = std::ranges::find_if_not(str, Test); str.remove_prefix(std::ranges::distance(std::ranges::begin(str), it)); auto const rit = std::ranges::find_if_not(std::ranges::rbegin(str), std::ranges::rend(str), Test); str.remove_suffix(std::ranges::distance(std::ranges::rbegin(str), rit)); return str; } // --- #if !(defined(__APPLE__) && defined(__clang__)) std::string tr_strv_to_utf8_string(std::string_view sv) { return tr_strv_replace_invalid(sv); } #endif std::string tr_strv_replace_invalid(std::string_view sv, uint32_t replacement) { // stripping characters after first \0 if (auto first_null = sv.find('\0'); first_null != std::string::npos) { sv = { std::data(sv), first_null }; } auto out = std::string{}; out.reserve(std::size(sv)); utf8::unchecked::replace_invalid(std::data(sv), std::data(sv) + std::size(sv), std::back_inserter(out), replacement); return out; } #ifdef _WIN32 std::string tr_win32_native_to_utf8(std::wstring_view in) { auto out = std::string{}; out.resize(WideCharToMultiByte(CP_UTF8, 0, std::data(in), std::size(in), nullptr, 0, nullptr, nullptr)); [[maybe_unused]] auto len = WideCharToMultiByte(CP_UTF8, 0, std::data(in), std::size(in), std::data(out), std::size(out), nullptr, nullptr); TR_ASSERT(len == std::size(out)); return out; } std::wstring tr_win32_utf8_to_native(std::string_view in) { auto out = std::wstring{}; out.resize(MultiByteToWideChar(CP_UTF8, 0, std::data(in), std::size(in), nullptr, 0)); [[maybe_unused]] auto len = MultiByteToWideChar(CP_UTF8, 0, std::data(in), std::size(in), std::data(out), std::size(out)); TR_ASSERT(len == std::size(out)); return out; } std::string tr_win32_format_message(uint32_t code) { wchar_t* wide_text = nullptr; auto const wide_size = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, code, 0, reinterpret_cast(&wide_text), 0, nullptr); if (wide_size == 0) { return fmt::format("Unknown error ({:#08x})", code); } auto text = std::string{}; if (wide_size != 0 && wide_text != nullptr) { text = tr_win32_native_to_utf8({ wide_text, wide_size }); } LocalFree(wide_text); // Most (all?) messages contain "\r\n" in the end, chop it while (!std::empty(text) && isspace(text.back()) != 0) { text.resize(text.size() - 1); } return text; } #endif std::string_view::size_type tr_strv_find_invalid_utf8(std::string_view const sv) { return utf8::find_invalid(sv); } std::u8string_view::size_type tr_strv_find_invalid_utf8(std::u8string_view const sv) { auto const invalid = utf8::find_invalid(sv.begin(), sv.end()); return invalid == sv.end() ? std::u8string_view::npos : static_cast(invalid - sv.begin()); } std::u8string tr_strv_to_u8string(std::string_view const sv) { auto u8str = tr_strv_to_utf8_string(sv); auto const view = std::views::transform(u8str, [](char c) -> char8_t { return c; }); return { view.begin(), view.end() }; }