xref: /reactos/sdk/lib/ucrt/locale/setlocale.cpp (revision 7a764cf6)
104e0dc4aSTimo Kreuzer /***
204e0dc4aSTimo Kreuzer *setlocal.c - Contains the setlocale function
304e0dc4aSTimo Kreuzer *
404e0dc4aSTimo Kreuzer *       Copyright (c) Microsoft Corporation. All rights reserved.
504e0dc4aSTimo Kreuzer *
604e0dc4aSTimo Kreuzer *Purpose:
704e0dc4aSTimo Kreuzer *       Contains the _wsetlocale() function.
804e0dc4aSTimo Kreuzer *
904e0dc4aSTimo Kreuzer *******************************************************************************/
1004e0dc4aSTimo Kreuzer 
1104e0dc4aSTimo Kreuzer #include <corecrt_internal.h>
1204e0dc4aSTimo Kreuzer #include <locale.h>
1304e0dc4aSTimo Kreuzer #include <stdlib.h>
1404e0dc4aSTimo Kreuzer 
call_wsetlocale(int const category,char const * const narrow_locale)15*7a764cf6STimo Kreuzer static wchar_t* __cdecl call_wsetlocale(int const category, char const* const narrow_locale)
1604e0dc4aSTimo Kreuzer {
1704e0dc4aSTimo Kreuzer     if (narrow_locale == nullptr)
1804e0dc4aSTimo Kreuzer         return _wsetlocale(category, nullptr);
1904e0dc4aSTimo Kreuzer 
2004e0dc4aSTimo Kreuzer     size_t size;
2104e0dc4aSTimo Kreuzer     _ERRCHECK_EINVAL_ERANGE(mbstowcs_s(&size, nullptr, 0, narrow_locale, INT_MAX));
2204e0dc4aSTimo Kreuzer 
2304e0dc4aSTimo Kreuzer     __crt_unique_heap_ptr<wchar_t> wide_locale(_calloc_crt_t(wchar_t, size));
2404e0dc4aSTimo Kreuzer     if (wide_locale.get() == nullptr)
2504e0dc4aSTimo Kreuzer         return nullptr;
2604e0dc4aSTimo Kreuzer 
2704e0dc4aSTimo Kreuzer     if (_ERRCHECK_EINVAL_ERANGE(mbstowcs_s(nullptr, wide_locale.get(), size, narrow_locale, _TRUNCATE)) != 0)
2804e0dc4aSTimo Kreuzer         return nullptr;
2904e0dc4aSTimo Kreuzer 
3004e0dc4aSTimo Kreuzer     return _wsetlocale(category, wide_locale.get());
3104e0dc4aSTimo Kreuzer }
3204e0dc4aSTimo Kreuzer 
setlocale(int _category,char const * _locale)3304e0dc4aSTimo Kreuzer extern "C" char* __cdecl setlocale(int _category, char const* _locale)
3404e0dc4aSTimo Kreuzer {
3504e0dc4aSTimo Kreuzer     // Deadlock Avoidance:  See the comment in _wsetlocale on usage of this function.
3604e0dc4aSTimo Kreuzer     __acrt_eagerly_load_locale_apis();
3704e0dc4aSTimo Kreuzer 
3804e0dc4aSTimo Kreuzer     return __acrt_lock_and_call(__acrt_locale_lock, [&]() -> char*
3904e0dc4aSTimo Kreuzer     {
4004e0dc4aSTimo Kreuzer         // Convert ASCII string into a wide character string:
4104e0dc4aSTimo Kreuzer         wchar_t* const outwlocale = call_wsetlocale(_category, _locale);
4204e0dc4aSTimo Kreuzer         if (outwlocale == nullptr)
4304e0dc4aSTimo Kreuzer         {
4404e0dc4aSTimo Kreuzer             return nullptr;
4504e0dc4aSTimo Kreuzer         }
4604e0dc4aSTimo Kreuzer 
4704e0dc4aSTimo Kreuzer         __acrt_ptd* const ptd = __acrt_getptd();
4804e0dc4aSTimo Kreuzer 
4904e0dc4aSTimo Kreuzer         __crt_locale_pointers locale;
5004e0dc4aSTimo Kreuzer         locale.locinfo = ptd->_locale_info;
5104e0dc4aSTimo Kreuzer         locale.mbcinfo = ptd->_multibyte_info;
5204e0dc4aSTimo Kreuzer 
5304e0dc4aSTimo Kreuzer         // We now have a locale string, but the global locale can be changed by
5404e0dc4aSTimo Kreuzer         // another thread.  If we allow this thread's locale to be updated before
5504e0dc4aSTimo Kreuzer         // we're done with this string, it might be freed from under us.  We call
5604e0dc4aSTimo Kreuzer         // the versions of the wide-to-multibyte conversions that do not update the
5704e0dc4aSTimo Kreuzer         // current thread's locale.
5804e0dc4aSTimo Kreuzer         size_t size = 0;
5904e0dc4aSTimo Kreuzer         if (_ERRCHECK_EINVAL_ERANGE(_wcstombs_s_l(&size, nullptr, 0, outwlocale, 0, &locale)) != 0)
6004e0dc4aSTimo Kreuzer         {
6104e0dc4aSTimo Kreuzer             return nullptr;
6204e0dc4aSTimo Kreuzer         }
6304e0dc4aSTimo Kreuzer 
6404e0dc4aSTimo Kreuzer         long* const refcount = static_cast<long*>(_malloc_crt(size + sizeof(long)));
6504e0dc4aSTimo Kreuzer         if (refcount == nullptr)
6604e0dc4aSTimo Kreuzer         {
6704e0dc4aSTimo Kreuzer             return nullptr;
6804e0dc4aSTimo Kreuzer         }
6904e0dc4aSTimo Kreuzer 
7004e0dc4aSTimo Kreuzer         char* outlocale = reinterpret_cast<char*>(&refcount[1]);
7104e0dc4aSTimo Kreuzer 
7204e0dc4aSTimo Kreuzer         /* convert return value to ASCII */
7304e0dc4aSTimo Kreuzer         if (_ERRCHECK_EINVAL_ERANGE(_wcstombs_s_l(nullptr, outlocale, size, outwlocale, _TRUNCATE, &locale)) != 0)
7404e0dc4aSTimo Kreuzer         {
7504e0dc4aSTimo Kreuzer             _free_crt(refcount);
7604e0dc4aSTimo Kreuzer             return nullptr;
7704e0dc4aSTimo Kreuzer         }
7804e0dc4aSTimo Kreuzer 
7904e0dc4aSTimo Kreuzer         __crt_locale_data* ptloci = locale.locinfo;
8004e0dc4aSTimo Kreuzer 
8104e0dc4aSTimo Kreuzer         _ASSERTE((ptloci->lc_category[_category].locale != nullptr && ptloci->lc_category[_category].refcount != nullptr) ||
8204e0dc4aSTimo Kreuzer                  (ptloci->lc_category[_category].locale == nullptr && ptloci->lc_category[_category].refcount == nullptr));
8304e0dc4aSTimo Kreuzer 
8404e0dc4aSTimo Kreuzer         if (ptloci->lc_category[_category].refcount != nullptr &&
8504e0dc4aSTimo Kreuzer             _InterlockedDecrement(ptloci->lc_category[_category].refcount) == 0)
8604e0dc4aSTimo Kreuzer         {
8704e0dc4aSTimo Kreuzer             _free_crt(ptloci->lc_category[_category].refcount);
8804e0dc4aSTimo Kreuzer             ptloci->lc_category[_category].refcount = nullptr;
8904e0dc4aSTimo Kreuzer         }
9004e0dc4aSTimo Kreuzer 
9104e0dc4aSTimo Kreuzer         if (__acrt_should_sync_with_global_locale(ptd))
9204e0dc4aSTimo Kreuzer         {
9304e0dc4aSTimo Kreuzer             if (ptloci->lc_category[_category].refcount != nullptr &&
9404e0dc4aSTimo Kreuzer                 _InterlockedDecrement(ptloci->lc_category[_category].refcount) == 0)
9504e0dc4aSTimo Kreuzer             {
9604e0dc4aSTimo Kreuzer                 _free_crt(ptloci->lc_category[_category].refcount);
9704e0dc4aSTimo Kreuzer                 ptloci->lc_category[_category].refcount = nullptr;
9804e0dc4aSTimo Kreuzer             }
9904e0dc4aSTimo Kreuzer         }
10004e0dc4aSTimo Kreuzer 
10104e0dc4aSTimo Kreuzer         /*
10204e0dc4aSTimo Kreuzer         * Note that we are using a risky trick here.  We are adding this
10304e0dc4aSTimo Kreuzer         * locale to an existing __crt_locale_data struct, and thus starting
10404e0dc4aSTimo Kreuzer         * the locale's refcount with the same value as the whole struct.
10504e0dc4aSTimo Kreuzer         * That means all code which modifies both __crt_locale_data::refcount
10604e0dc4aSTimo Kreuzer         * and __crt_locale_data::lc_category[]::refcount in structs that are
10704e0dc4aSTimo Kreuzer         * potentially shared across threads must make those modifications
10804e0dc4aSTimo Kreuzer         * under the locale lock.  Otherwise, there's a race condition
10904e0dc4aSTimo Kreuzer         * for some other thread modifying __crt_locale_data::refcount after
11004e0dc4aSTimo Kreuzer         * we load it but before we store it to refcount.
11104e0dc4aSTimo Kreuzer         */
11204e0dc4aSTimo Kreuzer         *refcount = ptloci->refcount;
11304e0dc4aSTimo Kreuzer         ptloci->lc_category[_category].refcount = refcount;
11404e0dc4aSTimo Kreuzer         ptloci->lc_category[_category].locale = outlocale;
11504e0dc4aSTimo Kreuzer 
11604e0dc4aSTimo Kreuzer         return outlocale;
11704e0dc4aSTimo Kreuzer     });
11804e0dc4aSTimo Kreuzer }
119