fix: use user-preferred locale (#5444)

* Use user-preferred locale

Previous fix adding `L` format specifier was correct but only fixed half
of the problem, as C++ locale is set up to be "C" by default. GTK client
used to call `setlocale(LC_ALL, "")` to set up user-preferred locale
which only affected C functions and `std::locale` (used by libfmt) was
unaware of those changes.

Apply the fix to all the binaries since they're all doing some sort of
output to the user and calling libtransmission helpers, as well as using
libfmt directly.

* Improve libtransmission's json-test

Set the locale C++ way to avoid any sort of inconsistencies, and also
restore it to the old one once finished testing.

* Improve transmission-show test runner script

Quote outputs to avoid CMake error about `message()` being called with
no arguments. Capture stderr to the same output file. Fallback to `git
diff` if `diff` wasn't found. A few other minor changes.
This commit is contained in:
Mike Gelfand 2023-04-27 15:10:33 +01:00 committed by Charles Kerr
parent 02b8535aa1
commit 411b66cf88
13 changed files with 78 additions and 14 deletions

View File

@ -197,6 +197,8 @@ static std::string getConfigDir(int argc, char const** argv)
int tr_main(int argc, char* argv[])
{
tr_locale_set_global("");
tr_variant settings;
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);

View File

@ -939,6 +939,8 @@ void tr_daemon::handle_error(tr_error* error) const
int tr_main(int argc, char* argv[])
{
tr_locale_set_global("");
int ret;
tr_daemon daemon;
bool foreground;

View File

@ -27,10 +27,8 @@
#include <fmt/core.h>
#include <clocale>
#include <cstdio>
#include <string>
#include <tuple>
namespace
{
@ -53,7 +51,7 @@ Glib::OptionEntry create_option_entry(Glib::ustring const& long_name, gchar shor
int main(int argc, char** argv)
{
/* init i18n */
std::ignore = std::setlocale(LC_ALL, "");
tr_locale_set_global("");
bindtextdomain(AppTranslationDomainName, TRANSMISSIONLOCALEDIR);
bind_textdomain_codeset(AppTranslationDomainName, "UTF-8");
textdomain(AppTranslationDomainName);

View File

@ -14,11 +14,14 @@
#include <cstdlib> // getenv()
#include <cstring> /* strerror() */
#include <ctime> // nanosleep()
#include <iostream>
#include <iterator> // for std::back_inserter
#include <locale>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
#ifdef _WIN32
@ -60,6 +63,23 @@ time_t libtransmission::detail::tr_time::current_time = {};
// ---
void tr_locale_set_global(char const* locale_name) noexcept
{
try
{
std::ignore = std::locale::global(std::locale{ locale_name });
std::ignore = std::cout.imbue(std::locale{});
std::ignore = std::cerr.imbue(std::locale{});
}
catch (std::exception const&)
{
// Ignore.
}
}
// ---
bool tr_loadFile(std::string_view filename, std::vector<char>& contents, tr_error** error)
{
auto const szfilename = tr_pathbuf{ filename };

View File

@ -56,6 +56,8 @@ struct tr_error;
#define tr_ngettext(singular, plural, count) ((count) == 1 ? (singular) : (plural))
#endif
void tr_locale_set_global(char const* locale_name) noexcept;
// ---
[[nodiscard]] std::string_view tr_get_mime_type_for_filename(std::string_view filename);

View File

@ -4,7 +4,13 @@
@import AppKit;
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h>
int main(int argc, char** argv)
{
tr_locale_set_global("");
return NSApplicationMain(argc, (char const**)argv);
}

View File

@ -667,6 +667,8 @@ FaviconCache& Application::faviconCache()
int tr_main(int argc, char** argv)
{
tr_locale_set_global("");
InteropHelper::initialize();
Application const app(argc, argv);

View File

@ -5,9 +5,11 @@
#define LIBTRANSMISSION_VARIANT_MODULE
#include <clocale> // setlocale()
#include <locale>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <libtransmission/transmission.h>
#include <libtransmission/variant.h>
@ -23,11 +25,26 @@ protected:
void SetUp() override
{
auto const* locale_str = GetParam();
if (setlocale(LC_NUMERIC, locale_str) == nullptr)
try
{
old_locale_ = std::locale::global(std::locale{ {}, new std::numpunct_byname<char>{ locale_str } });
}
catch (std::runtime_error const&)
{
GTEST_SKIP();
}
}
void TearDown() override
{
if (old_locale_)
{
std::ignore = std::locale::global(*old_locale_);
}
}
private:
std::optional<std::locale> old_locale_;
};
TEST_P(JSONTest, testElements)

View File

@ -4,7 +4,7 @@
if(CMAKE_VERSION VERSION_LESS 3.14)
# --ignore-eol was introduced in CMake 3.14
message(status "skipping transmission-show test; cmake version too old")
message(STATUS "skipping transmission-show test; cmake version too old")
else()
get_filename_component(torrent_basename "${torrent_file}" NAME)
set(output_file ${CMAKE_CURRENT_BINARY_DIR}/${torrent_basename}.out)
@ -23,7 +23,8 @@ else()
execute_process(
COMMAND ${transmission_show} ${torrent_file}
OUTPUT_FILE ${output_file})
OUTPUT_FILE ${output_file}
ERROR_FILE ${output_file})
execute_process(
COMMAND ${CMAKE_COMMAND} -E compare_files --ignore-eol ${reference_file} ${output_file}
@ -31,23 +32,29 @@ else()
if(STATUS AND NOT STATUS EQUAL 0)
file(READ ${reference_file} CONTENTS)
message("EXPECTED CONTENTS (${reference_file}):")
message(${CONTENTS})
message(STATUS "EXPECTED CONTENTS (${reference_file}):")
message("${CONTENTS}")
file(READ ${output_file} CONTENTS)
message("RECEIVED CONTENTS (${output_file}):")
message(${CONTENTS})
message(STATUS "RECEIVED CONTENTS (${output_file}):")
message("${CONTENTS}")
find_program(DIFF_EXEC diff)
if(NOT DIFF_EXEC)
find_program(GIT_EXEC git)
if(GIT_EXEC)
set(DIFF_EXEC "${GIT_EXEC}" diff --no-index)
endif()
endif()
if(DIFF_EXEC)
message("DIFF:")
execute_process(COMMAND ${DIFF_EXEC} -u ${output_file} ${reference_file})
message(STATUS "DIFF:")
execute_process(COMMAND ${DIFF_EXEC} --unified ${reference_file} ${output_file})
endif()
file(REMOVE ${output_file})
message(FATAL_ERROR "failed: files '${reference_file}' and '${output_file}' do not match")
else()
file(REMOVE ${output_file})
message("passed")
message(STATUS "passed")
endif()
endif()

View File

@ -143,6 +143,8 @@ std::string tr_getcwd()
int tr_main(int argc, char* argv[])
{
tr_locale_set_global("");
tr_logSetLevel(TR_LOG_ERROR);
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);
tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr);

View File

@ -322,6 +322,8 @@ static bool setSource(tr_variant* metainfo, char const* source_value)
int tr_main(int argc, char* argv[])
{
tr_locale_set_global("");
int changedCount = 0;
tr_logSetLevel(TR_LOG_ERROR);

View File

@ -3271,6 +3271,8 @@ static void getHostAndPortAndRpcUrl(int* argc, char** argv, std::string* host, i
int tr_main(int argc, char* argv[])
{
tr_locale_set_global("");
auto config = Config{};
auto port = DefaultPort;
auto host = std::string{};

View File

@ -399,6 +399,8 @@ void doScrape(tr_torrent_metainfo const& metainfo)
int tr_main(int argc, char* argv[])
{
tr_locale_set_global("");
tr_logSetQueueEnabled(false);
tr_logSetLevel(TR_LOG_ERROR);
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);