// Copyright (c) 2016, Novartis Institutes for BioMedical Research Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Novartis Institutes for BioMedical Research Inc. // nor the names of its contributors may be used to endorse or promote // products derived from this software without specific prior written // permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #if defined(__CYGWIN__) && !defined(_GNU_SOURCE) // -std=c++11 turns off recent POSIX features! #define _GNU_SOURCE #endif #include "LocaleSwitcher.h" #include // LocaleSwitcher Dependencies #include #ifdef __APPLE__ #include #endif #ifdef __CYGWIN__ #include #endif #ifdef RDK_THREADSAFE_SSS #include #endif namespace RDKit { namespace Utils { namespace detail { // Implementation of LocaleSwitcher // The locale switcher has state to indicate how many times // it has been used (in a thread if thread safe RDKIT) const static int CurrentState = 0; // return current state const static int SwitchLocale = 1; // indicate we are now switched to "C" const static int ResetLocale = -1; // return to previous locale (not "C") // Returns how many LocaleSwitches we have gone down. // Such as // void foo() { LocaleSwitcher switch; } // void bar() { LocaleSwitcher switch; foo(); } // // Implementations are free to not switch the locale if the locale // is already in "C", but this is implementation defined. // When Recurse(CurrentState) == 0 we are in the "global" locale, // or at least at the top locale that isn't "C". // static int recurseLocale(int state) { #ifndef RDK_THREADSAFE_SSS static int recursion = 0; if (state == SwitchLocale) recursion++; else if (state == ResetLocale) recursion--; return recursion; #else static thread_local int recursion = 0; if (state == SwitchLocale) { recursion++; } else if (state == ResetLocale) { recursion--; } return recursion; #endif } // allows an RAII-like approach to ensuring the locale is temporarily "C" // instead of whatever we started in. class LocaleSwitcherImpl { public: #ifdef _WIN32 LocaleSwitcherImpl() { if (!recurseLocale(CurrentState)) { #ifdef RDK_THREADSAFE_SSS _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); old_locale = ::setlocale(LC_ALL, nullptr); ::setlocale(LC_ALL, "C"); // thread safe on windows #else std::setlocale(LC_ALL, "C"); #endif // RDK_THREADSAFE_SSS recurseLocale(SwitchLocale); switched = true; } } ~LocaleSwitcherImpl() { if (switched) { #ifdef RDK_THREADSAFE_SSS ::setlocale(LC_ALL, old_locale.c_str()); #else // back to last (global?) locale std::setlocale(LC_ALL, old_locale.c_str()); #endif // RDK_THREADSAFE_SSS recurseLocale(ResetLocale); } } public: bool switched = false; std::string old_locale; #else // _WIN32 locale_t loc; // current "C" locale locale_t old_loc; // locale we came from LocaleSwitcherImpl() : old_locale(setlocale(LC_ALL, nullptr)) { // set locale for this thread if (!recurseLocale(CurrentState) && old_locale != "C") { recurseLocale(SwitchLocale); old_loc = uselocale(nullptr); loc = newlocale(LC_ALL_MASK, "C", (locale_t) nullptr); uselocale(loc); // Don't free "C" or "GLOBAL" Locales } else { old_locale = "C"; // prevents recursion } } ~LocaleSwitcherImpl() { if (old_locale != "C") { uselocale(old_loc); freelocale(loc); recurseLocale(ResetLocale); } } public: std::string old_locale; #endif // _WIN32 }; } // namespace detail // allows an RAII-like approach to ensuring the locale is temporarily "C" // instead of whatever we started in. LocaleSwitcher::LocaleSwitcher() : impl(new detail::LocaleSwitcherImpl) {} LocaleSwitcher::~LocaleSwitcher() { delete impl; } } // namespace Utils } // namespace RDKit