From 58657edfb3f3705366f88cd0cfec8e1d08cee97d Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 14 Jul 2016 11:35:55 -0400 Subject: redesign PBD::LocaleGuard to work correctly (i.e. not break translations) --- libs/pbd/locale_guard.cc | 90 +++++++++++++++++++++++++++------------------ libs/pbd/pbd/locale_guard.h | 19 +++++----- 2 files changed, 64 insertions(+), 45 deletions(-) (limited to 'libs') diff --git a/libs/pbd/locale_guard.cc b/libs/pbd/locale_guard.cc index b4225d3875..f5f97f2042 100644 --- a/libs/pbd/locale_guard.cc +++ b/libs/pbd/locale_guard.cc @@ -29,54 +29,72 @@ using namespace PBD; -/* The initial C++ locale is "C" regardless of the user's preferred locale. - * The C locale from setlocale() matches the user's preferred locale +/* Neither C nor C++ pick up a user's preferred locale choice without the + * application actively taking steps to make this happen. + * + * For C: setlocale (LC_ALL, ""); + * For C++ (assuming that the C version was called): + * std::locale::global (std::locale (setlocale (LC_ALL, 0))); + * + * The application needs to make these calls, probably in main(). * * Setting the C++ locale will change the C locale, but not the other way 'round. * and some plugin may change either behind our back. */ LocaleGuard::LocaleGuard () - : old_c (0) + : old_c_locale (0) { - char* actual = setlocale (LC_NUMERIC, NULL); - if (strcmp ("C", actual)) { - DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: change locale from '%1' to 'C'\n", actual)); - /* purpose of LocaleGuard is to make sure we're using "C" for - the numeric locale during its lifetime, so make it so. - */ - old_c = strdup (actual); - /* this changes both C++ and C locale */ - std::locale::global (std::locale (std::locale::classic(), "C", std::locale::numeric)); - } - if (old_cpp != std::locale::classic ()) { - PBD::error << "LocaleGuard: initial C++ locale is not 'C'. Expect non-portable session files.\n"; - DEBUG_TRACE (DEBUG::Locale, "LG: initial C++ locale is not 'C'. Expect non-portable session files.\n"); + /* A LocaleGuard object ensures that the + * LC_NUMERIC/std::locale::numeric aspect of the C and C++ locales are + * set to "C" during its lifetime, so that printf/iostreams use a + * portable format for numeric output (i.e. 1234.5 is always 1234.5 and + * not sometimes 1234,5, as it would be in fr or de locales) + */ + + char const * const current_c_locale = setlocale (LC_NUMERIC, 0); + + if (strcmp ("C", current_c_locale) != 0) { + + old_c_locale = strdup (current_c_locale); + + /* set the C++ global/default locale to whatever we are using + * now, but with "C" numeric handling. + * + * this also sets the C locale, so no call to setlocale() is required. + */ + + std::locale::global (std::locale (old_cpp_locale, "C", std::locale::numeric)); + pre_cpp_locale = std::locale(); + + DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: change locale from '%1' => %2\n", old_cpp_locale.name(), pre_cpp_locale.name())); } } LocaleGuard::~LocaleGuard () { - char* actual = setlocale (LC_NUMERIC, NULL); - std::locale current; + char const * current_c_locale = setlocale (LC_NUMERIC, 0); + std::locale current_cpp_locale; - if (current != old_cpp) { - /* the C++ locale should always be "C", that's the default - * at application start, and ardour never changes it to - * anything but "C". - * - * if it's not: some plugin meddled with it. - */ - if (old_cpp != std::locale::classic ()) { - PBD::error << "LocaleGuard: someone (a plugin) changed the C++ locale, expect non-portable session files.\n"; - DEBUG_TRACE (DEBUG::Locale, "LG: someone (a plugin) changed the C++ locale, expect non-portable session files.\n"); - } - std::locale::global (old_cpp); - DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C++ locale: '%1'\n", old_cpp.name ())); - } - if (old_c && strcmp (old_c, actual)) { - setlocale (LC_NUMERIC, old_c); - DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C locale: '%1'\n", old_c)); + if (current_cpp_locale != pre_cpp_locale) { + + PBD::error << string_compose ("LocaleGuard: someone (a plugin) changed the C++ locale from\n\t%1\nto\n\t%2\n, expect non-portable session files. Decimal OK ? %2", + old_cpp_locale.name(), current_cpp_locale.name(), + (std::use_facet >(std::locale()).decimal_point() == '.')) + << endmsg; + DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: someone (a plugin) changed the C++ locale (Decimal OK ? '%1'); expect non-portable session files.\n", + (std::use_facet >(std::locale()).decimal_point() == '.'))); + + /* this resets C & C++ locales */ + std::locale::global (old_cpp_locale); + DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C & C++ locale: '%1'\n", std::locale().name())); + + } else if (old_c_locale && (strcmp (current_c_locale, old_c_locale) != 0)) { + + /* reset only the C locale */ + setlocale (LC_NUMERIC, old_c_locale); + DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C locale from %1 to\n'%2'\n(C++ is '%3')\n", current_c_locale, old_c_locale, std::locale().name())); } - free (old_c); + + free (const_cast (old_c_locale)); } diff --git a/libs/pbd/pbd/locale_guard.h b/libs/pbd/pbd/locale_guard.h index 3342603f20..b10e4d3ee3 100644 --- a/libs/pbd/pbd/locale_guard.h +++ b/libs/pbd/pbd/locale_guard.h @@ -26,15 +26,16 @@ namespace PBD { - struct LIBPBD_API LocaleGuard { - public: - LocaleGuard (); - ~LocaleGuard (); - - private: - std::locale old_cpp; - char* old_c; - }; +struct LIBPBD_API LocaleGuard { + public: + LocaleGuard (); + ~LocaleGuard (); + + private: + std::locale old_cpp_locale; + std::locale pre_cpp_locale; + char const * old_c_locale; +}; } #endif /* __pbd_locale_guard__ */ -- cgit v1.2.3