xref: /reactos/sdk/lib/ucrt/locale/setlocale.cpp (revision 7a764cf6)
1 /***
2 *setlocal.c - Contains the setlocale function
3 *
4 *       Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 *       Contains the _wsetlocale() function.
8 *
9 *******************************************************************************/
10 
11 #include <corecrt_internal.h>
12 #include <locale.h>
13 #include <stdlib.h>
14 
call_wsetlocale(int const category,char const * const narrow_locale)15 static wchar_t* __cdecl call_wsetlocale(int const category, char const* const narrow_locale)
16 {
17     if (narrow_locale == nullptr)
18         return _wsetlocale(category, nullptr);
19 
20     size_t size;
21     _ERRCHECK_EINVAL_ERANGE(mbstowcs_s(&size, nullptr, 0, narrow_locale, INT_MAX));
22 
23     __crt_unique_heap_ptr<wchar_t> wide_locale(_calloc_crt_t(wchar_t, size));
24     if (wide_locale.get() == nullptr)
25         return nullptr;
26 
27     if (_ERRCHECK_EINVAL_ERANGE(mbstowcs_s(nullptr, wide_locale.get(), size, narrow_locale, _TRUNCATE)) != 0)
28         return nullptr;
29 
30     return _wsetlocale(category, wide_locale.get());
31 }
32 
setlocale(int _category,char const * _locale)33 extern "C" char* __cdecl setlocale(int _category, char const* _locale)
34 {
35     // Deadlock Avoidance:  See the comment in _wsetlocale on usage of this function.
36     __acrt_eagerly_load_locale_apis();
37 
38     return __acrt_lock_and_call(__acrt_locale_lock, [&]() -> char*
39     {
40         // Convert ASCII string into a wide character string:
41         wchar_t* const outwlocale = call_wsetlocale(_category, _locale);
42         if (outwlocale == nullptr)
43         {
44             return nullptr;
45         }
46 
47         __acrt_ptd* const ptd = __acrt_getptd();
48 
49         __crt_locale_pointers locale;
50         locale.locinfo = ptd->_locale_info;
51         locale.mbcinfo = ptd->_multibyte_info;
52 
53         // We now have a locale string, but the global locale can be changed by
54         // another thread.  If we allow this thread's locale to be updated before
55         // we're done with this string, it might be freed from under us.  We call
56         // the versions of the wide-to-multibyte conversions that do not update the
57         // current thread's locale.
58         size_t size = 0;
59         if (_ERRCHECK_EINVAL_ERANGE(_wcstombs_s_l(&size, nullptr, 0, outwlocale, 0, &locale)) != 0)
60         {
61             return nullptr;
62         }
63 
64         long* const refcount = static_cast<long*>(_malloc_crt(size + sizeof(long)));
65         if (refcount == nullptr)
66         {
67             return nullptr;
68         }
69 
70         char* outlocale = reinterpret_cast<char*>(&refcount[1]);
71 
72         /* convert return value to ASCII */
73         if (_ERRCHECK_EINVAL_ERANGE(_wcstombs_s_l(nullptr, outlocale, size, outwlocale, _TRUNCATE, &locale)) != 0)
74         {
75             _free_crt(refcount);
76             return nullptr;
77         }
78 
79         __crt_locale_data* ptloci = locale.locinfo;
80 
81         _ASSERTE((ptloci->lc_category[_category].locale != nullptr && ptloci->lc_category[_category].refcount != nullptr) ||
82                  (ptloci->lc_category[_category].locale == nullptr && ptloci->lc_category[_category].refcount == nullptr));
83 
84         if (ptloci->lc_category[_category].refcount != nullptr &&
85             _InterlockedDecrement(ptloci->lc_category[_category].refcount) == 0)
86         {
87             _free_crt(ptloci->lc_category[_category].refcount);
88             ptloci->lc_category[_category].refcount = nullptr;
89         }
90 
91         if (__acrt_should_sync_with_global_locale(ptd))
92         {
93             if (ptloci->lc_category[_category].refcount != nullptr &&
94                 _InterlockedDecrement(ptloci->lc_category[_category].refcount) == 0)
95             {
96                 _free_crt(ptloci->lc_category[_category].refcount);
97                 ptloci->lc_category[_category].refcount = nullptr;
98             }
99         }
100 
101         /*
102         * Note that we are using a risky trick here.  We are adding this
103         * locale to an existing __crt_locale_data struct, and thus starting
104         * the locale's refcount with the same value as the whole struct.
105         * That means all code which modifies both __crt_locale_data::refcount
106         * and __crt_locale_data::lc_category[]::refcount in structs that are
107         * potentially shared across threads must make those modifications
108         * under the locale lock.  Otherwise, there's a race condition
109         * for some other thread modifying __crt_locale_data::refcount after
110         * we load it but before we store it to refcount.
111         */
112         *refcount = ptloci->refcount;
113         ptloci->lc_category[_category].refcount = refcount;
114         ptloci->lc_category[_category].locale = outlocale;
115 
116         return outlocale;
117     });
118 }
119