xref: /reactos/sdk/lib/ucrt/locale/wsetlocale.cpp (revision e98e9000)
104e0dc4aSTimo Kreuzer /***
204e0dc4aSTimo Kreuzer *wsetlocale.cpp - Contains the wsetlocale 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 
1404e0dc4aSTimo Kreuzer extern "C" {
1504e0dc4aSTimo Kreuzer 
1604e0dc4aSTimo Kreuzer static const char _first_127char[] = {
1704e0dc4aSTimo Kreuzer         1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17,
1804e0dc4aSTimo Kreuzer         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
1904e0dc4aSTimo Kreuzer         35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
2004e0dc4aSTimo Kreuzer         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
2104e0dc4aSTimo Kreuzer         69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
2204e0dc4aSTimo Kreuzer         86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,101,102,
2304e0dc4aSTimo Kreuzer         103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,
2404e0dc4aSTimo Kreuzer         120,121,122,123,124,125,126,127
2504e0dc4aSTimo Kreuzer };
2604e0dc4aSTimo Kreuzer 
2704e0dc4aSTimo Kreuzer extern __crt_locale_data __acrt_initial_locale_data;
2804e0dc4aSTimo Kreuzer extern const unsigned short _wctype[];
2904e0dc4aSTimo Kreuzer static const unsigned short *_ctype_loc_style = _wctype + 2;
3004e0dc4aSTimo Kreuzer 
3104e0dc4aSTimo Kreuzer long __acrt_locale_changed_data = FALSE;
3204e0dc4aSTimo Kreuzer 
3304e0dc4aSTimo Kreuzer /* helper function prototypes */
3404e0dc4aSTimo Kreuzer _Success_(return != 0)
3504e0dc4aSTimo Kreuzer static wchar_t * _expandlocale(
3604e0dc4aSTimo Kreuzer     _In_z_                                wchar_t const * expr,
3704e0dc4aSTimo Kreuzer     _Out_writes_z_(sizeInChars)           wchar_t *       output,
3804e0dc4aSTimo Kreuzer     _In_                                  size_t          sizeInChars,
3904e0dc4aSTimo Kreuzer     _Out_writes_z_(localeNameSizeInChars) wchar_t *       localeNameOutput,
4004e0dc4aSTimo Kreuzer     _In_                                  size_t          localeNameSizeInChars,
4104e0dc4aSTimo Kreuzer     _Out_                                 UINT&           cp
4204e0dc4aSTimo Kreuzer     );
4304e0dc4aSTimo Kreuzer 
4404e0dc4aSTimo Kreuzer void _wcscats(_Inout_updates_z_(_Param_(2)) wchar_t *, size_t, int, ...);
4504e0dc4aSTimo Kreuzer void __lc_lctowcs(_Inout_updates_z_(_Param_(2)) wchar_t *, size_t, const __crt_locale_strings *);
4604e0dc4aSTimo Kreuzer int __lc_wcstolc(__crt_locale_strings *, const wchar_t *);
4704e0dc4aSTimo Kreuzer static wchar_t * __cdecl _wsetlocale_set_cat(__crt_locale_data*, int, const wchar_t *);
4804e0dc4aSTimo Kreuzer static wchar_t * __cdecl _wsetlocale_get_all(__crt_locale_data*);
4904e0dc4aSTimo Kreuzer static __crt_locale_data* __cdecl _updatetlocinfo_nolock(void);
5004e0dc4aSTimo Kreuzer static wchar_t * __cdecl _wsetlocale_nolock(__crt_locale_data*, int, const wchar_t *);
5104e0dc4aSTimo Kreuzer int __cdecl _setmbcp_nolock(int, __crt_multibyte_data*);
5204e0dc4aSTimo Kreuzer __crt_locale_data* __cdecl _updatetlocinfoEx_nolock(__crt_locale_data**, __crt_locale_data*);
5304e0dc4aSTimo Kreuzer 
__acrt_set_locale_changed()5404e0dc4aSTimo Kreuzer void __cdecl __acrt_set_locale_changed()
5504e0dc4aSTimo Kreuzer {
5604e0dc4aSTimo Kreuzer     _InterlockedExchange(&__acrt_locale_changed_data, TRUE);
5704e0dc4aSTimo Kreuzer }
5804e0dc4aSTimo Kreuzer 
5904e0dc4aSTimo Kreuzer /***
6004e0dc4aSTimo Kreuzer * _configthreadlocale(int i)
6104e0dc4aSTimo Kreuzer *
6204e0dc4aSTimo Kreuzer * Purpose:
6304e0dc4aSTimo Kreuzer *       To set _own_locale flag on threadinfo sturct. If this flag is set, this thread
6404e0dc4aSTimo Kreuzer *       is going own it's threadlocinfo struct. Setlocale call on other thread will have
6504e0dc4aSTimo Kreuzer *       no effect on this thread's locale. If 0 is passed then nothing is changed, but
6604e0dc4aSTimo Kreuzer *       current status is returned.
6704e0dc4aSTimo Kreuzer * Exit   :
6804e0dc4aSTimo Kreuzer *       Returns the current status - i.e. per thread locale is enabled or not.
6904e0dc4aSTimo Kreuzer *
7004e0dc4aSTimo Kreuzer *******************************************************************************/
_configthreadlocale(int i)7104e0dc4aSTimo Kreuzer int __cdecl _configthreadlocale(int i)
7204e0dc4aSTimo Kreuzer {
7304e0dc4aSTimo Kreuzer     /*
7404e0dc4aSTimo Kreuzer      * ownlocale flag struct:
7504e0dc4aSTimo Kreuzer      * bits: 000000000000000000000000 000W 00P1
7604e0dc4aSTimo Kreuzer      * P is set when _ENABLE_PER_THREAD_LOCALE is called for this thread
7704e0dc4aSTimo Kreuzer      * or _ENABLE_PER_THREAD_LOCALE_NEW was set when this thread was created.
7804e0dc4aSTimo Kreuzer      * W is set when _WSETLOCALE_AVOID_SYNC_LOCALE_BIT is set by _wsetlocale.
7904e0dc4aSTimo Kreuzer      * It is used to disable global-ptd resynchronization during a call to _wsetlocale.
8004e0dc4aSTimo Kreuzer      *
8104e0dc4aSTimo Kreuzer      * __globallocalestatus structure:
8204e0dc4aSTimo Kreuzer      * bits: 111111111111111111111111 1111 1N1G
8304e0dc4aSTimo Kreuzer      * G is set if _ENABLE_PER_THREAD_LOCALE_GLOBAL is set.
8404e0dc4aSTimo Kreuzer      * G is 0 if _ENABLE_PER_THREAD_LOCALE_GLOBAL is not set.
8504e0dc4aSTimo Kreuzer      * N is set if _ENABLE_PER_THREAD_LOCALE_NEW is set.
8604e0dc4aSTimo Kreuzer      * N is 0 if _ENABLE_PER_THREAD_LOCALE_NEW is not set.
8704e0dc4aSTimo Kreuzer      */
8804e0dc4aSTimo Kreuzer     __acrt_ptd* const ptd = __acrt_getptd();
8904e0dc4aSTimo Kreuzer     int retval = (ptd->_own_locale & _PER_THREAD_LOCALE_BIT)==0 ? _DISABLE_PER_THREAD_LOCALE:_ENABLE_PER_THREAD_LOCALE;
9004e0dc4aSTimo Kreuzer 
9104e0dc4aSTimo Kreuzer     switch(i)
9204e0dc4aSTimo Kreuzer     {
9304e0dc4aSTimo Kreuzer         case _ENABLE_PER_THREAD_LOCALE:
9404e0dc4aSTimo Kreuzer             // same behavior as __acrt_disable_global_locale_sync()
9504e0dc4aSTimo Kreuzer             ptd->_own_locale = ptd->_own_locale | _PER_THREAD_LOCALE_BIT;
9604e0dc4aSTimo Kreuzer             break;
9704e0dc4aSTimo Kreuzer 
9804e0dc4aSTimo Kreuzer         case _DISABLE_PER_THREAD_LOCALE:
9904e0dc4aSTimo Kreuzer             // same behavior as __acrt_enable_global_locale_sync()
10004e0dc4aSTimo Kreuzer             ptd->_own_locale = ptd->_own_locale & ~_PER_THREAD_LOCALE_BIT;
10104e0dc4aSTimo Kreuzer             break;
10204e0dc4aSTimo Kreuzer 
10304e0dc4aSTimo Kreuzer         case 0:
10404e0dc4aSTimo Kreuzer             break;
10504e0dc4aSTimo Kreuzer 
10604e0dc4aSTimo Kreuzer         /* used only during dll startup for linkopt */
10704e0dc4aSTimo Kreuzer         case -1:
10804e0dc4aSTimo Kreuzer             __globallocalestatus = -1;
10904e0dc4aSTimo Kreuzer             break;
11004e0dc4aSTimo Kreuzer 
11104e0dc4aSTimo Kreuzer         default:
11204e0dc4aSTimo Kreuzer             _VALIDATE_RETURN(("Invalid parameter for _configthreadlocale",0),EINVAL,-1);
11304e0dc4aSTimo Kreuzer             break;
11404e0dc4aSTimo Kreuzer     }
11504e0dc4aSTimo Kreuzer 
11604e0dc4aSTimo Kreuzer     return retval;
11704e0dc4aSTimo Kreuzer }
11804e0dc4aSTimo Kreuzer 
__acrt_uninitialize_locale()11904e0dc4aSTimo Kreuzer extern "C" void __cdecl __acrt_uninitialize_locale()
12004e0dc4aSTimo Kreuzer {
12104e0dc4aSTimo Kreuzer     __acrt_lock_and_call(__acrt_locale_lock, [&]
12204e0dc4aSTimo Kreuzer     {
12304e0dc4aSTimo Kreuzer         __acrt_current_locale_data.uninitialize([](__crt_locale_data*& locale)
12404e0dc4aSTimo Kreuzer         {
12504e0dc4aSTimo Kreuzer             if (locale == &__acrt_initial_locale_data)
12604e0dc4aSTimo Kreuzer             {
12704e0dc4aSTimo Kreuzer                 return;
12804e0dc4aSTimo Kreuzer             }
12904e0dc4aSTimo Kreuzer 
13004e0dc4aSTimo Kreuzer             locale = _updatetlocinfoEx_nolock(&locale, &__acrt_initial_locale_data);
13104e0dc4aSTimo Kreuzer         });
13204e0dc4aSTimo Kreuzer     });
13304e0dc4aSTimo Kreuzer }
13404e0dc4aSTimo Kreuzer 
13504e0dc4aSTimo Kreuzer /***
13604e0dc4aSTimo Kreuzer *_free_locale() - free threadlocinfo
13704e0dc4aSTimo Kreuzer *
13804e0dc4aSTimo Kreuzer *Purpose:
13904e0dc4aSTimo Kreuzer *       Free up the per-thread locale info structure specified by the passed
14004e0dc4aSTimo Kreuzer *       pointer.
14104e0dc4aSTimo Kreuzer *
14204e0dc4aSTimo Kreuzer *Entry:
14304e0dc4aSTimo Kreuzer *       __crt_locale_data* ptloci
14404e0dc4aSTimo Kreuzer *
14504e0dc4aSTimo Kreuzer *Exit:
14604e0dc4aSTimo Kreuzer *
14704e0dc4aSTimo Kreuzer *Exceptions:
14804e0dc4aSTimo Kreuzer *
14904e0dc4aSTimo Kreuzer *******************************************************************************/
15004e0dc4aSTimo Kreuzer 
_free_locale(_locale_t plocinfo)15104e0dc4aSTimo Kreuzer void __cdecl _free_locale(
15204e0dc4aSTimo Kreuzer         _locale_t plocinfo
15304e0dc4aSTimo Kreuzer         )
15404e0dc4aSTimo Kreuzer {
15504e0dc4aSTimo Kreuzer     if (plocinfo != nullptr)
15604e0dc4aSTimo Kreuzer     {
15704e0dc4aSTimo Kreuzer         __acrt_lock(__acrt_multibyte_cp_lock);
15804e0dc4aSTimo Kreuzer         __try
15904e0dc4aSTimo Kreuzer         {
16004e0dc4aSTimo Kreuzer             if (plocinfo->mbcinfo != nullptr &&
16104e0dc4aSTimo Kreuzer                     InterlockedDecrement(&(plocinfo->mbcinfo->refcount)) == 0 &&
16204e0dc4aSTimo Kreuzer                     plocinfo->mbcinfo != &__acrt_initial_multibyte_data )
16304e0dc4aSTimo Kreuzer             {
16404e0dc4aSTimo Kreuzer                 _free_crt(plocinfo->mbcinfo);
16504e0dc4aSTimo Kreuzer             }
16604e0dc4aSTimo Kreuzer         }
16704e0dc4aSTimo Kreuzer         __finally
16804e0dc4aSTimo Kreuzer         {
16904e0dc4aSTimo Kreuzer             __acrt_unlock(__acrt_multibyte_cp_lock);
17004e0dc4aSTimo Kreuzer         }
171*e98e9000STimo Kreuzer         __endtry
17204e0dc4aSTimo Kreuzer 
17304e0dc4aSTimo Kreuzer         if (plocinfo->locinfo != nullptr)
17404e0dc4aSTimo Kreuzer         {
17504e0dc4aSTimo Kreuzer             /*
17604e0dc4aSTimo Kreuzer              * this portion has to be in locale lock as there may be case when
17704e0dc4aSTimo Kreuzer              * not this thread but some other thread is still holding to this
17804e0dc4aSTimo Kreuzer              * locale and is also trying to free this locale. In this case
17904e0dc4aSTimo Kreuzer              * we may end up leaking memory.
18004e0dc4aSTimo Kreuzer              */
18104e0dc4aSTimo Kreuzer 
18204e0dc4aSTimo Kreuzer             __acrt_lock(__acrt_locale_lock);
18304e0dc4aSTimo Kreuzer             __try
18404e0dc4aSTimo Kreuzer             {
18504e0dc4aSTimo Kreuzer                 __acrt_release_locale_ref(plocinfo->locinfo);
18604e0dc4aSTimo Kreuzer                 if ( (plocinfo->locinfo != nullptr) &&
18704e0dc4aSTimo Kreuzer                      (plocinfo->locinfo->refcount == 0) &&
18804e0dc4aSTimo Kreuzer                      (plocinfo->locinfo != &__acrt_initial_locale_data) )
18904e0dc4aSTimo Kreuzer                     __acrt_free_locale(plocinfo->locinfo);
19004e0dc4aSTimo Kreuzer             }
19104e0dc4aSTimo Kreuzer             __finally
19204e0dc4aSTimo Kreuzer             {
19304e0dc4aSTimo Kreuzer                 __acrt_unlock(__acrt_locale_lock);
19404e0dc4aSTimo Kreuzer             }
195*e98e9000STimo Kreuzer             __endtry
19604e0dc4aSTimo Kreuzer         }
19704e0dc4aSTimo Kreuzer 
19804e0dc4aSTimo Kreuzer         _free_crt(plocinfo);
19904e0dc4aSTimo Kreuzer     }
20004e0dc4aSTimo Kreuzer }
20104e0dc4aSTimo Kreuzer 
20204e0dc4aSTimo Kreuzer /*
20304e0dc4aSTimo Kreuzer * _locale_t _get_current_locale() -
20404e0dc4aSTimo Kreuzer *    Gets the current locale setting.
20504e0dc4aSTimo Kreuzer *
20604e0dc4aSTimo Kreuzer * Purpose:
20704e0dc4aSTimo Kreuzer *       Gets the current locale setting for this thread. Returns locale
20804e0dc4aSTimo Kreuzer *       in form of _locale_t, which then can be used with other locale
20904e0dc4aSTimo Kreuzer *       aware string functions.
21004e0dc4aSTimo Kreuzer *
21104e0dc4aSTimo Kreuzer */
21204e0dc4aSTimo Kreuzer 
_get_current_locale(void)21304e0dc4aSTimo Kreuzer _locale_t __cdecl _get_current_locale(void)
21404e0dc4aSTimo Kreuzer {
21504e0dc4aSTimo Kreuzer     __acrt_ptd* const ptd = __acrt_getptd();
21604e0dc4aSTimo Kreuzer 
21704e0dc4aSTimo Kreuzer     auto locale_copy(_calloc_crt_t(__crt_locale_pointers, 1));
21804e0dc4aSTimo Kreuzer     if (!locale_copy)
21904e0dc4aSTimo Kreuzer         return nullptr; // calloc will set errno
22004e0dc4aSTimo Kreuzer 
22104e0dc4aSTimo Kreuzer     __acrt_update_thread_locale_data();
22204e0dc4aSTimo Kreuzer     __acrt_update_thread_multibyte_data();
22304e0dc4aSTimo Kreuzer 
22404e0dc4aSTimo Kreuzer     // No one can free the data pointed to by ptlocinfo while we're copying
22504e0dc4aSTimo Kreuzer     // it, since we're copying this thread's ptlocinfo, which won't be updated
22604e0dc4aSTimo Kreuzer     // during the copy.  So there are no worries about it being freed from
22704e0dc4aSTimo Kreuzer     // under us.  We still need a lock while adding a reference for the new
22804e0dc4aSTimo Kreuzer     // copy, though, because of the race condition found in _wsetlocale.
22904e0dc4aSTimo Kreuzer     locale_copy.get()->locinfo = ptd->_locale_info;
23004e0dc4aSTimo Kreuzer     locale_copy.get()->mbcinfo = ptd->_multibyte_info;
23104e0dc4aSTimo Kreuzer     __acrt_lock_and_call(__acrt_locale_lock, [&]
23204e0dc4aSTimo Kreuzer     {
23304e0dc4aSTimo Kreuzer         __acrt_add_locale_ref(locale_copy.get()->locinfo);
23404e0dc4aSTimo Kreuzer     });
23504e0dc4aSTimo Kreuzer 
23604e0dc4aSTimo Kreuzer     __acrt_lock_and_call(__acrt_multibyte_cp_lock, [&]
23704e0dc4aSTimo Kreuzer     {
23804e0dc4aSTimo Kreuzer         InterlockedIncrement(&locale_copy.get()->mbcinfo->refcount);
23904e0dc4aSTimo Kreuzer     });
24004e0dc4aSTimo Kreuzer 
24104e0dc4aSTimo Kreuzer     return locale_copy.detach();
24204e0dc4aSTimo Kreuzer }
24304e0dc4aSTimo Kreuzer 
24404e0dc4aSTimo Kreuzer // Enclaves do not support dynamic locales, but they support reading the default one.
24504e0dc4aSTimo Kreuzer #ifndef _UCRT_ENCLAVE_BUILD
24604e0dc4aSTimo Kreuzer 
24704e0dc4aSTimo Kreuzer // This is used as the initializer if a category does not need initialization.
no_op_initialize(__crt_locale_data *)24804e0dc4aSTimo Kreuzer static int __cdecl no_op_initialize(__crt_locale_data*)
24904e0dc4aSTimo Kreuzer {
25004e0dc4aSTimo Kreuzer     return 0;
25104e0dc4aSTimo Kreuzer }
25204e0dc4aSTimo Kreuzer 
25304e0dc4aSTimo Kreuzer void __cdecl __acrt_free_locale(__crt_locale_data*);
25404e0dc4aSTimo Kreuzer 
25504e0dc4aSTimo Kreuzer _CRT_LINKER_FORCE_INCLUDE(__acrt_locale_terminator);
25604e0dc4aSTimo Kreuzer 
25704e0dc4aSTimo Kreuzer extern __declspec(selectany) struct {
25804e0dc4aSTimo Kreuzer         const wchar_t * catname;
25904e0dc4aSTimo Kreuzer         wchar_t * locale;
26004e0dc4aSTimo Kreuzer         int (* init)(__crt_locale_data*);
26104e0dc4aSTimo Kreuzer } const __lc_category[LC_MAX-LC_MIN+1] = {
26204e0dc4aSTimo Kreuzer         /* code assumes locale initialization is "__wclocalestr" */
26304e0dc4aSTimo Kreuzer         { L"LC_ALL",     nullptr,                        no_op_initialize                  },
26404e0dc4aSTimo Kreuzer         { L"LC_COLLATE", __acrt_wide_c_locale_string,    no_op_initialize                  },
26504e0dc4aSTimo Kreuzer         { L"LC_CTYPE",   __acrt_wide_c_locale_string,    __acrt_locale_initialize_ctype    },
26604e0dc4aSTimo Kreuzer         { L"LC_MONETARY",__acrt_wide_c_locale_string,    __acrt_locale_initialize_monetary },
26704e0dc4aSTimo Kreuzer         { L"LC_NUMERIC", __acrt_wide_c_locale_string,    __acrt_locale_initialize_numeric  },
26804e0dc4aSTimo Kreuzer         { L"LC_TIME",    __acrt_wide_c_locale_string,    __acrt_locale_initialize_time     }
26904e0dc4aSTimo Kreuzer };
27004e0dc4aSTimo Kreuzer 
27104e0dc4aSTimo Kreuzer /***
27204e0dc4aSTimo Kreuzer *
27304e0dc4aSTimo Kreuzer * _copytlocinfo_nolock(__crt_locale_data* ptlocid, __crt_locale_data* ptlocis)
27404e0dc4aSTimo Kreuzer *
27504e0dc4aSTimo Kreuzer * Purpose:
27604e0dc4aSTimo Kreuzer *       Copy the contents of ptlocis to ptlocid and increase the refcount of all the
27704e0dc4aSTimo Kreuzer *       elements in ptlocid after copy.
27804e0dc4aSTimo Kreuzer *
27904e0dc4aSTimo Kreuzer ******************************************************************************/
_copytlocinfo_nolock(__crt_locale_data * ptlocid,__crt_locale_data * ptlocis)28004e0dc4aSTimo Kreuzer static void __cdecl _copytlocinfo_nolock(
28104e0dc4aSTimo Kreuzer     __crt_locale_data* ptlocid,
28204e0dc4aSTimo Kreuzer     __crt_locale_data* ptlocis)
28304e0dc4aSTimo Kreuzer {
28404e0dc4aSTimo Kreuzer     if (ptlocis != nullptr && ptlocid != nullptr && ptlocid != ptlocis) {
28504e0dc4aSTimo Kreuzer         *ptlocid = *ptlocis;
28604e0dc4aSTimo Kreuzer         ptlocid->refcount = 0;
28704e0dc4aSTimo Kreuzer         __acrt_add_locale_ref(ptlocid);
28804e0dc4aSTimo Kreuzer     }
28904e0dc4aSTimo Kreuzer }
29004e0dc4aSTimo Kreuzer 
29104e0dc4aSTimo Kreuzer /***
29204e0dc4aSTimo Kreuzer * void sync_legacy_variables_lk()
29304e0dc4aSTimo Kreuzer *
29404e0dc4aSTimo Kreuzer * Purpose:
29504e0dc4aSTimo Kreuzer *   Syncs all the legacy locale specific variables to the global locale.
29604e0dc4aSTimo Kreuzer *
29704e0dc4aSTimo Kreuzer *******************************************************************************/
sync_legacy_variables_lk()29804e0dc4aSTimo Kreuzer static __inline void sync_legacy_variables_lk()
29904e0dc4aSTimo Kreuzer {
30004e0dc4aSTimo Kreuzer     __acrt_lconv = __acrt_current_locale_data.value()->lconv;
30104e0dc4aSTimo Kreuzer     _pctype = __acrt_current_locale_data.value()->_public._locale_pctype;
30204e0dc4aSTimo Kreuzer     __mb_cur_max = __acrt_current_locale_data.value()->_public._locale_mb_cur_max;
30304e0dc4aSTimo Kreuzer }
30404e0dc4aSTimo Kreuzer 
30504e0dc4aSTimo Kreuzer /***
30604e0dc4aSTimo Kreuzer * _locale_t _wcreate_locale(int category, char *wlocale) -
30704e0dc4aSTimo Kreuzer *    Set one or all locale categories of global locale structure
30804e0dc4aSTimo Kreuzer *
30904e0dc4aSTimo Kreuzer * Purpose:
31004e0dc4aSTimo Kreuzer *       The _wcreate_locale() routine allows the user to create a _locale_t
31104e0dc4aSTimo Kreuzer *       object that can be used with other locale functions.
31204e0dc4aSTimo Kreuzer *
31304e0dc4aSTimo Kreuzer * Entry:
31404e0dc4aSTimo Kreuzer *       int category = One of the locale categories defined in locale.h
31504e0dc4aSTimo Kreuzer *       char *locale = String identifying a specific locale.
31604e0dc4aSTimo Kreuzer *
31704e0dc4aSTimo Kreuzer * Exit:
31804e0dc4aSTimo Kreuzer *       If supplied locale pointer != nullptr:
31904e0dc4aSTimo Kreuzer *
32004e0dc4aSTimo Kreuzer *           If locale string is '\0', set locale to default.
32104e0dc4aSTimo Kreuzer *
32204e0dc4aSTimo Kreuzer *           If desired setting can be honored, return a pointer to the
32304e0dc4aSTimo Kreuzer *           locale string for the appropriate category.
32404e0dc4aSTimo Kreuzer *
32504e0dc4aSTimo Kreuzer *           If desired setting can NOT be honored, return nullptr.
32604e0dc4aSTimo Kreuzer *
32704e0dc4aSTimo Kreuzer * Exceptions:
32804e0dc4aSTimo Kreuzer *       Compound locale strings of the form "LC_COLLATE=xxx;LC_CTYPE=xxx;..."
32904e0dc4aSTimo Kreuzer *       are allowed for the LC_ALL category.  This is to support the ability
33004e0dc4aSTimo Kreuzer *       to restore locales with the returned string, as specified by ANSI.
33104e0dc4aSTimo Kreuzer *       Setting the locale with a compound locale string will succeed unless
33204e0dc4aSTimo Kreuzer *       *all* categories failed.  The returned string will reflect the current
33304e0dc4aSTimo Kreuzer *       locale.  For example, if LC_CTYPE fails in the above string, setlocale
33404e0dc4aSTimo Kreuzer *       will return "LC_COLLATE=xxx;LC_CTYPE=yyy;..." where yyy is the
33504e0dc4aSTimo Kreuzer *       previous locale (or the C locale if restoring the previous locale
33604e0dc4aSTimo Kreuzer *       also failed).  Unrecognized LC_* categories are ignored.
33704e0dc4aSTimo Kreuzer *
33804e0dc4aSTimo Kreuzer *******************************************************************************/
33904e0dc4aSTimo Kreuzer 
_wcreate_locale(int _category,const wchar_t * locale)34004e0dc4aSTimo Kreuzer _locale_t __cdecl _wcreate_locale(
34104e0dc4aSTimo Kreuzer         int _category,
34204e0dc4aSTimo Kreuzer         const wchar_t *locale
34304e0dc4aSTimo Kreuzer         )
34404e0dc4aSTimo Kreuzer {
34504e0dc4aSTimo Kreuzer     if (_category < LC_MIN || _category > LC_MAX || locale == nullptr)
34604e0dc4aSTimo Kreuzer         return nullptr;
34704e0dc4aSTimo Kreuzer 
34804e0dc4aSTimo Kreuzer     auto result = _calloc_crt_t(__crt_locale_pointers, 1);
34904e0dc4aSTimo Kreuzer     _VALIDATE_RETURN_NOEXC(result, ENOMEM, nullptr);
35004e0dc4aSTimo Kreuzer 
35104e0dc4aSTimo Kreuzer     auto locale_data = _calloc_crt_t(__crt_locale_data, 1);
35204e0dc4aSTimo Kreuzer     _VALIDATE_RETURN_NOEXC(locale_data, ENOMEM, nullptr);
35304e0dc4aSTimo Kreuzer 
35404e0dc4aSTimo Kreuzer     auto multibyte_data = _calloc_crt_t(__crt_multibyte_data, 1);
35504e0dc4aSTimo Kreuzer     _VALIDATE_RETURN_NOEXC(multibyte_data, ENOMEM, nullptr);
35604e0dc4aSTimo Kreuzer 
35704e0dc4aSTimo Kreuzer     _copytlocinfo_nolock(locale_data.get(), &__acrt_initial_locale_data);
35804e0dc4aSTimo Kreuzer 
35904e0dc4aSTimo Kreuzer     if (_wsetlocale_nolock(locale_data.get(), _category, locale) == nullptr ||
36004e0dc4aSTimo Kreuzer         _setmbcp_nolock(locale_data.get()->_public._locale_lc_codepage, multibyte_data.get()) != 0)
36104e0dc4aSTimo Kreuzer     {
36204e0dc4aSTimo Kreuzer         __acrt_release_locale_ref(locale_data.get());
36304e0dc4aSTimo Kreuzer         __acrt_free_locale(locale_data.detach());
36404e0dc4aSTimo Kreuzer         return nullptr;
36504e0dc4aSTimo Kreuzer     }
36604e0dc4aSTimo Kreuzer 
36704e0dc4aSTimo Kreuzer     multibyte_data.get()->refcount = 1;
36804e0dc4aSTimo Kreuzer 
36904e0dc4aSTimo Kreuzer     result.get()->locinfo = locale_data.detach();
37004e0dc4aSTimo Kreuzer     result.get()->mbcinfo = multibyte_data.detach();
37104e0dc4aSTimo Kreuzer     return result.detach();
37204e0dc4aSTimo Kreuzer }
37304e0dc4aSTimo Kreuzer 
37404e0dc4aSTimo Kreuzer /***
37504e0dc4aSTimo Kreuzer * _locale_t _create_locale(int category, char *locale) -
37604e0dc4aSTimo Kreuzer *    Set one or all locale categories of global locale structure
37704e0dc4aSTimo Kreuzer ****/
_create_locale(int _category,const char * _locale)37804e0dc4aSTimo Kreuzer _locale_t __cdecl _create_locale(
37904e0dc4aSTimo Kreuzer         int _category,
38004e0dc4aSTimo Kreuzer         const char *_locale
38104e0dc4aSTimo Kreuzer         )
38204e0dc4aSTimo Kreuzer {
38304e0dc4aSTimo Kreuzer     wchar_t _wlocale[MAX_LC_LEN];
38404e0dc4aSTimo Kreuzer 
38504e0dc4aSTimo Kreuzer     /* Early input validation */
38604e0dc4aSTimo Kreuzer     if ( (_category < LC_MIN) || (_category > LC_MAX) || _locale == nullptr)
38704e0dc4aSTimo Kreuzer         return nullptr;
38804e0dc4aSTimo Kreuzer 
38904e0dc4aSTimo Kreuzer     if ( __acrt_MultiByteToWideChar(CP_ACP, 0, _locale, -1, _wlocale, _countof(_wlocale)) == 0 )
39004e0dc4aSTimo Kreuzer     { // conversion to wide char failed
39104e0dc4aSTimo Kreuzer         return nullptr;
39204e0dc4aSTimo Kreuzer     }
39304e0dc4aSTimo Kreuzer 
39404e0dc4aSTimo Kreuzer     return _wcreate_locale(_category, _wlocale);
39504e0dc4aSTimo Kreuzer }
39604e0dc4aSTimo Kreuzer 
39704e0dc4aSTimo Kreuzer /*
39804e0dc4aSTimo Kreuzer *char * setlocale(int category, char *locale) - Set one or all locale categories
39904e0dc4aSTimo Kreuzer *
40004e0dc4aSTimo Kreuzer *Purpose:
40104e0dc4aSTimo Kreuzer *       The setlocale() routine allows the user to set one or more of
40204e0dc4aSTimo Kreuzer *       the locale categories to the specific locale selected by the
40304e0dc4aSTimo Kreuzer *       user.  [ANSI]
40404e0dc4aSTimo Kreuzer *
40504e0dc4aSTimo Kreuzer *       NOTE: Under !_INTL, the C libraries only support the "C" locale.
40604e0dc4aSTimo Kreuzer *       Attempts to change the locale will fail.
40704e0dc4aSTimo Kreuzer *
40804e0dc4aSTimo Kreuzer *Entry:
40904e0dc4aSTimo Kreuzer *       int category = One of the locale categories defined in locale.h
41004e0dc4aSTimo Kreuzer *       char *locale = String identifying a specific locale or nullptr to
41104e0dc4aSTimo Kreuzer *                  query the current locale.
41204e0dc4aSTimo Kreuzer *
41304e0dc4aSTimo Kreuzer *Exit:
41404e0dc4aSTimo Kreuzer *       If supplied locale pointer == nullptr:
41504e0dc4aSTimo Kreuzer *
41604e0dc4aSTimo Kreuzer *           Return pointer to current locale string and do NOT change
41704e0dc4aSTimo Kreuzer *           the current locale.
41804e0dc4aSTimo Kreuzer *
41904e0dc4aSTimo Kreuzer *       If supplied locale pointer != nullptr:
42004e0dc4aSTimo Kreuzer *
42104e0dc4aSTimo Kreuzer *           If locale string is '\0', set locale to default.
42204e0dc4aSTimo Kreuzer *
42304e0dc4aSTimo Kreuzer *           If desired setting can be honored, return a pointer to the
42404e0dc4aSTimo Kreuzer *           locale string for the appropriate category.
42504e0dc4aSTimo Kreuzer *
42604e0dc4aSTimo Kreuzer *           If desired setting can NOT be honored, return nullptr.
42704e0dc4aSTimo Kreuzer *
42804e0dc4aSTimo Kreuzer *Exceptions:
42904e0dc4aSTimo Kreuzer *       Compound locale strings of the form "LC_COLLATE=xxx;LC_CTYPE=xxx;..."
43004e0dc4aSTimo Kreuzer *       are allowed for the LC_ALL category.  This is to support the ability
43104e0dc4aSTimo Kreuzer *       to restore locales with the returned string, as specified by ANSI.
43204e0dc4aSTimo Kreuzer *       Setting the locale with a compound locale string will succeed unless
43304e0dc4aSTimo Kreuzer *       *all* categories failed.  The returned string will reflect the current
43404e0dc4aSTimo Kreuzer *       locale.  For example, if LC_CTYPE fails in the above string, setlocale
43504e0dc4aSTimo Kreuzer *       will return "LC_COLLATE=xxx;LC_CTYPE=yyy;..." where yyy is the
43604e0dc4aSTimo Kreuzer *       previous locale (or the C locale if restoring the previous locale
43704e0dc4aSTimo Kreuzer *       also failed).  Unrecognized LC_* categories are ignored.
43804e0dc4aSTimo Kreuzer *
43904e0dc4aSTimo Kreuzer */
44004e0dc4aSTimo Kreuzer 
_wsetlocale(int _category,const wchar_t * _wlocale)44104e0dc4aSTimo Kreuzer wchar_t * __cdecl _wsetlocale (
44204e0dc4aSTimo Kreuzer         int _category,
44304e0dc4aSTimo Kreuzer         const wchar_t *_wlocale
44404e0dc4aSTimo Kreuzer         )
44504e0dc4aSTimo Kreuzer {
44604e0dc4aSTimo Kreuzer     wchar_t * retval=nullptr;
44704e0dc4aSTimo Kreuzer     __crt_locale_data* ptloci = nullptr;
44804e0dc4aSTimo Kreuzer 
44904e0dc4aSTimo Kreuzer     /* Validate category */
45004e0dc4aSTimo Kreuzer     _VALIDATE_RETURN(LC_MIN <= _category && _category <= LC_MAX, EINVAL, nullptr);
45104e0dc4aSTimo Kreuzer 
45204e0dc4aSTimo Kreuzer     __acrt_ptd* const ptd = __acrt_getptd();
45304e0dc4aSTimo Kreuzer 
45404e0dc4aSTimo Kreuzer     // Deadlock Avoidance:  When a new thread is created in the process, we
45504e0dc4aSTimo Kreuzer     // create a new PTD for the thread.  The PTD initialization function is
45604e0dc4aSTimo Kreuzer     // called under the loader lock.  This initialization function will also
45704e0dc4aSTimo Kreuzer     // acquire the locale lock in order to acquire a reference to the current
45804e0dc4aSTimo Kreuzer     // global locale for the new thread.
45904e0dc4aSTimo Kreuzer     //
46004e0dc4aSTimo Kreuzer     // Some of the locale APIs are not available on all supported target OSes.
46104e0dc4aSTimo Kreuzer     // We dynamically obtain these libraries via LoadLibrary/GetProcAddress.
46204e0dc4aSTimo Kreuzer     // We must ensure that no call to LoadLibrary is made while we hold the
46304e0dc4aSTimo Kreuzer     // locale lock, lest we deadlock due to lock order inversion between the
46404e0dc4aSTimo Kreuzer     // loader lock and the locale lock.
46504e0dc4aSTimo Kreuzer     //
46604e0dc4aSTimo Kreuzer     // This function call here will ensure that any required modules are loaded
46704e0dc4aSTimo Kreuzer     // before we acquire the locale lock.
46804e0dc4aSTimo Kreuzer     __acrt_eagerly_load_locale_apis();
46904e0dc4aSTimo Kreuzer 
47004e0dc4aSTimo Kreuzer     __acrt_update_thread_locale_data();
47104e0dc4aSTimo Kreuzer 
47204e0dc4aSTimo Kreuzer     // Prevent global locale resynchronization - we call things like stricmp() further down
47304e0dc4aSTimo Kreuzer     // without passing our _locale_t which would trigger a resynchronization.
47404e0dc4aSTimo Kreuzer     // Use _WSETLOCALE_AVOID_SYNC_LOCALE_BIT to avoid interfering with other locale settings
47504e0dc4aSTimo Kreuzer     // (see _configthreadlocale() for details).
47604e0dc4aSTimo Kreuzer 
47704e0dc4aSTimo Kreuzer     // This may not be necessary anymore and may be a hold-over from when _wsetlocale called
47804e0dc4aSTimo Kreuzer     // setlocale instead of the other way around.
47904e0dc4aSTimo Kreuzer     // (MSFT:20394962 - Investigate whether _WSETLOCALE_AVOID_SYNC_LOCALE_BIT is needed)
48004e0dc4aSTimo Kreuzer     __acrt_disable_global_locale_sync(ptd, _WSETLOCALE_AVOID_SYNC_LOCALE_BIT);
48104e0dc4aSTimo Kreuzer 
48204e0dc4aSTimo Kreuzer     __crt_call_and_cleanup([&]
48304e0dc4aSTimo Kreuzer     {
48404e0dc4aSTimo Kreuzer         if ((ptloci = _calloc_crt_t(__crt_locale_data, 1).detach()) != nullptr)
48504e0dc4aSTimo Kreuzer         {
48604e0dc4aSTimo Kreuzer             __acrt_lock_and_call(__acrt_locale_lock, [&]
48704e0dc4aSTimo Kreuzer             {
48804e0dc4aSTimo Kreuzer                 _copytlocinfo_nolock(ptloci, ptd->_locale_info);
48904e0dc4aSTimo Kreuzer 
49004e0dc4aSTimo Kreuzer                 if ((retval = _wsetlocale_nolock(ptloci, _category, _wlocale)) != 0)
49104e0dc4aSTimo Kreuzer                 {
49204e0dc4aSTimo Kreuzer                     // If no call has been made to setlocale to change locale from "C" locale
49304e0dc4aSTimo Kreuzer                     // to some other locale, we keep locale_changed = 0. Other functions that
49404e0dc4aSTimo Kreuzer                     // depend on locale use this variable to optimize performance for C locale
49504e0dc4aSTimo Kreuzer                     // which is normally the case in applications.
49604e0dc4aSTimo Kreuzer                     if (_wlocale != nullptr)
49704e0dc4aSTimo Kreuzer                     {
49804e0dc4aSTimo Kreuzer                         if (wcscmp(_wlocale, __acrt_wide_c_locale_string) != 0)
49904e0dc4aSTimo Kreuzer                         {
50004e0dc4aSTimo Kreuzer                             __acrt_set_locale_changed();
50104e0dc4aSTimo Kreuzer                         }
50204e0dc4aSTimo Kreuzer                         // If it is again set to the C locale, could we not retain that state?
50304e0dc4aSTimo Kreuzer                         // else
50404e0dc4aSTimo Kreuzer                         // {
50504e0dc4aSTimo Kreuzer                         //    __acrt_set_locale_unchanged();
50604e0dc4aSTimo Kreuzer                         // }
50704e0dc4aSTimo Kreuzer                     }
50804e0dc4aSTimo Kreuzer 
50904e0dc4aSTimo Kreuzer                     (void)_updatetlocinfoEx_nolock(&ptd->_locale_info, ptloci);
51004e0dc4aSTimo Kreuzer                     __acrt_release_locale_ref(ptloci);
51104e0dc4aSTimo Kreuzer                     // Note that after incrementing _own_locale, if this thread doesn't
51204e0dc4aSTimo Kreuzer                     // have its own locale, _own_locale variable should be 1.
51304e0dc4aSTimo Kreuzer 
51404e0dc4aSTimo Kreuzer                     // Do not use __acrt_should_sync_with_global_locale() because
51504e0dc4aSTimo Kreuzer                     // _WSETLOCALE_AVOID_SYNC_LOCALE_BIT will interfere.
51604e0dc4aSTimo Kreuzer                     if (!(ptd->_own_locale & _PER_THREAD_LOCALE_BIT) &&
51704e0dc4aSTimo Kreuzer                         !(__globallocalestatus & _GLOBAL_LOCALE_BIT))
51804e0dc4aSTimo Kreuzer                     {
51904e0dc4aSTimo Kreuzer                         (void)_updatetlocinfoEx_nolock(&__acrt_current_locale_data.value(), ptd->_locale_info);
52004e0dc4aSTimo Kreuzer                         sync_legacy_variables_lk();
52104e0dc4aSTimo Kreuzer                     }
52204e0dc4aSTimo Kreuzer                 }
52304e0dc4aSTimo Kreuzer                 else
52404e0dc4aSTimo Kreuzer                 {
52504e0dc4aSTimo Kreuzer                     __acrt_release_locale_ref(ptloci);
52604e0dc4aSTimo Kreuzer                     __acrt_free_locale(ptloci);
52704e0dc4aSTimo Kreuzer                 }
52804e0dc4aSTimo Kreuzer             });
52904e0dc4aSTimo Kreuzer         }
53004e0dc4aSTimo Kreuzer     },
53104e0dc4aSTimo Kreuzer     [&]{ __acrt_enable_global_locale_sync(ptd, _WSETLOCALE_AVOID_SYNC_LOCALE_BIT); });
53204e0dc4aSTimo Kreuzer 
53304e0dc4aSTimo Kreuzer     return retval;
53404e0dc4aSTimo Kreuzer }
53504e0dc4aSTimo Kreuzer 
_wsetlocale_nolock(__crt_locale_data * ploci,int _category,const wchar_t * _wlocale)53604e0dc4aSTimo Kreuzer static wchar_t * __cdecl _wsetlocale_nolock(
53704e0dc4aSTimo Kreuzer         __crt_locale_data* ploci,
53804e0dc4aSTimo Kreuzer         int _category,
53904e0dc4aSTimo Kreuzer         const wchar_t *_wlocale
54004e0dc4aSTimo Kreuzer         )
54104e0dc4aSTimo Kreuzer {
54204e0dc4aSTimo Kreuzer     wchar_t * retval;
54304e0dc4aSTimo Kreuzer     /* Interpret locale */
54404e0dc4aSTimo Kreuzer 
54504e0dc4aSTimo Kreuzer     if (_category != LC_ALL)
54604e0dc4aSTimo Kreuzer     {
54704e0dc4aSTimo Kreuzer         retval = (_wlocale) ? _wsetlocale_set_cat(ploci, _category,_wlocale) :
54804e0dc4aSTimo Kreuzer             ploci->lc_category[_category].wlocale;
54904e0dc4aSTimo Kreuzer 
55004e0dc4aSTimo Kreuzer     } else { /* LC_ALL */
55104e0dc4aSTimo Kreuzer         wchar_t lctemp[MAX_LC_LEN];
55204e0dc4aSTimo Kreuzer         int i;
55304e0dc4aSTimo Kreuzer         int same = 1;
55404e0dc4aSTimo Kreuzer         int fLocaleSet = 0; /* flag to indicate if anything successfully set */
55504e0dc4aSTimo Kreuzer 
55604e0dc4aSTimo Kreuzer         if (_wlocale != nullptr)
55704e0dc4aSTimo Kreuzer         {
55804e0dc4aSTimo Kreuzer             if ( (_wlocale[0]==L'L') && (_wlocale[1]==L'C') && (_wlocale[2]==L'_') )
55904e0dc4aSTimo Kreuzer             {
56004e0dc4aSTimo Kreuzer                 /* parse compound locale string */
56104e0dc4aSTimo Kreuzer                 size_t len;
56204e0dc4aSTimo Kreuzer                 const wchar_t * p = _wlocale;  /* start of string to parse */
56304e0dc4aSTimo Kreuzer                 const wchar_t * s;
56404e0dc4aSTimo Kreuzer 
56504e0dc4aSTimo Kreuzer                 do {
56604e0dc4aSTimo Kreuzer                     s = wcspbrk(p, L"=;");
56704e0dc4aSTimo Kreuzer 
56804e0dc4aSTimo Kreuzer                     if (s == nullptr || (len=(size_t)(s - p)) == 0 || (*s == L';'))
56904e0dc4aSTimo Kreuzer                         return nullptr;  /* syntax error */
57004e0dc4aSTimo Kreuzer 
57104e0dc4aSTimo Kreuzer                     /* match with known LC_ strings, if possible, else ignore */
57204e0dc4aSTimo Kreuzer                     for (i=LC_ALL+1; i<=LC_MAX; i++)
57304e0dc4aSTimo Kreuzer                     {
57404e0dc4aSTimo Kreuzer                         if ((!wcsncmp(__lc_category[i].catname,p,len))
57504e0dc4aSTimo Kreuzer                             && (len==wcslen(__lc_category[i].catname)))
57604e0dc4aSTimo Kreuzer                         {
57704e0dc4aSTimo Kreuzer                             break;  /* matched i */
57804e0dc4aSTimo Kreuzer                         }
57904e0dc4aSTimo Kreuzer                     } /* no match if (i>LC_MAX) -- just ignore */
58004e0dc4aSTimo Kreuzer 
58104e0dc4aSTimo Kreuzer                     if ((len = wcscspn(++s, L";")) == 0 && *s != L';')
58204e0dc4aSTimo Kreuzer                         return nullptr;  /* syntax error */
58304e0dc4aSTimo Kreuzer 
58404e0dc4aSTimo Kreuzer                     if (i<=LC_MAX)
58504e0dc4aSTimo Kreuzer                     {
58604e0dc4aSTimo Kreuzer                         _ERRCHECK(wcsncpy_s(lctemp, _countof(lctemp), s, len));
58704e0dc4aSTimo Kreuzer                         lctemp[len]=L'\0';   /* null terminate string */
58804e0dc4aSTimo Kreuzer 
58904e0dc4aSTimo Kreuzer                         /* don't fail unless all categories fail */
59004e0dc4aSTimo Kreuzer                         if (_wsetlocale_set_cat(ploci, i,lctemp))
59104e0dc4aSTimo Kreuzer                             fLocaleSet++;       /* record a success */
59204e0dc4aSTimo Kreuzer                     }
59304e0dc4aSTimo Kreuzer                     if (*(p = s+len)!=L'\0')
59404e0dc4aSTimo Kreuzer                         p++;  /* skip ';', if present */
59504e0dc4aSTimo Kreuzer 
59604e0dc4aSTimo Kreuzer                 } while (*p);
59704e0dc4aSTimo Kreuzer 
59804e0dc4aSTimo Kreuzer                 retval = (fLocaleSet) ? _wsetlocale_get_all(ploci) : nullptr;
59904e0dc4aSTimo Kreuzer 
60004e0dc4aSTimo Kreuzer             } else { /* simple LC_ALL locale string */
60104e0dc4aSTimo Kreuzer 
60204e0dc4aSTimo Kreuzer                 UINT code_page;
60304e0dc4aSTimo Kreuzer                 wchar_t localeNameTemp[LOCALE_NAME_MAX_LENGTH];
60404e0dc4aSTimo Kreuzer                 /* confirm locale is supported, get expanded locale */
60504e0dc4aSTimo Kreuzer                 retval = _expandlocale(_wlocale, lctemp, _countof(lctemp), localeNameTemp, _countof(localeNameTemp), code_page);
60604e0dc4aSTimo Kreuzer                 if (retval != 0)
60704e0dc4aSTimo Kreuzer                 {
60804e0dc4aSTimo Kreuzer                     for (i=LC_MIN; i<=LC_MAX; i++)
60904e0dc4aSTimo Kreuzer                     {
61004e0dc4aSTimo Kreuzer                         if (i!=LC_ALL)
61104e0dc4aSTimo Kreuzer                         {
61204e0dc4aSTimo Kreuzer                             if (wcscmp(lctemp, ploci->lc_category[i].wlocale) != 0)
61304e0dc4aSTimo Kreuzer                             { // does not match the LC_ALL locale
61404e0dc4aSTimo Kreuzer                                 if (_wsetlocale_set_cat(ploci, i, lctemp))
61504e0dc4aSTimo Kreuzer                                 {
61604e0dc4aSTimo Kreuzer                                     fLocaleSet++;   /* record a success */
61704e0dc4aSTimo Kreuzer                                 }
61804e0dc4aSTimo Kreuzer                                 else
61904e0dc4aSTimo Kreuzer                                 {
62004e0dc4aSTimo Kreuzer                                     same = 0;       /* record a failure */
62104e0dc4aSTimo Kreuzer                                 }
62204e0dc4aSTimo Kreuzer                             }
62304e0dc4aSTimo Kreuzer                             else
62404e0dc4aSTimo Kreuzer                                 fLocaleSet++;   /* trivial succcess */
62504e0dc4aSTimo Kreuzer                         }
62604e0dc4aSTimo Kreuzer                     }
62704e0dc4aSTimo Kreuzer                     if (same) /* needn't call setlocale_get_all() if all the same */
62804e0dc4aSTimo Kreuzer                     {
62904e0dc4aSTimo Kreuzer                         retval = _wsetlocale_get_all(ploci);
63004e0dc4aSTimo Kreuzer                         /* retval set above */
63104e0dc4aSTimo Kreuzer                     }
63204e0dc4aSTimo Kreuzer                     else
63304e0dc4aSTimo Kreuzer                         retval = (fLocaleSet) ? _wsetlocale_get_all(ploci) : nullptr;
63404e0dc4aSTimo Kreuzer                 }
63504e0dc4aSTimo Kreuzer             }
63604e0dc4aSTimo Kreuzer         } else { /* LC_ALL & nullptr */
63704e0dc4aSTimo Kreuzer             retval = _wsetlocale_get_all (ploci);
63804e0dc4aSTimo Kreuzer         }
63904e0dc4aSTimo Kreuzer     }
64004e0dc4aSTimo Kreuzer 
64104e0dc4aSTimo Kreuzer     /* common exit point */
64204e0dc4aSTimo Kreuzer     return retval;
64304e0dc4aSTimo Kreuzer } /* setlocale */
64404e0dc4aSTimo Kreuzer 
64504e0dc4aSTimo Kreuzer 
_wsetlocale_set_cat(__crt_locale_data * ploci,int category,const wchar_t * wlocale)64604e0dc4aSTimo Kreuzer static wchar_t * __cdecl _wsetlocale_set_cat (
64704e0dc4aSTimo Kreuzer         __crt_locale_data* ploci,
64804e0dc4aSTimo Kreuzer         int category,
64904e0dc4aSTimo Kreuzer         const wchar_t * wlocale
65004e0dc4aSTimo Kreuzer         )
65104e0dc4aSTimo Kreuzer {
65204e0dc4aSTimo Kreuzer     wchar_t * oldlocale;
65304e0dc4aSTimo Kreuzer     wchar_t * oldlocalename;
65404e0dc4aSTimo Kreuzer     UINT oldcodepage;
65504e0dc4aSTimo Kreuzer 
65604e0dc4aSTimo Kreuzer     UINT cptemp;
65704e0dc4aSTimo Kreuzer     wchar_t lctemp[MAX_LC_LEN];
65804e0dc4aSTimo Kreuzer     wchar_t localeNameString[LOCALE_NAME_MAX_LENGTH];
65904e0dc4aSTimo Kreuzer     wchar_t * pch = nullptr;
66004e0dc4aSTimo Kreuzer     wchar_t * pch_cat_locale = nullptr;
66104e0dc4aSTimo Kreuzer     size_t cch = 0;
66204e0dc4aSTimo Kreuzer     unsigned short out[sizeof(_first_127char)];
66304e0dc4aSTimo Kreuzer     int i;
66404e0dc4aSTimo Kreuzer     __acrt_ptd* const ptd = __acrt_getptd();
66504e0dc4aSTimo Kreuzer     __crt_ctype_compatibility_data* _Loc_c = ptd->_setloc_data._Loc_c; // __setloc_data._Loc_c is array
66604e0dc4aSTimo Kreuzer     int _LOC_CCACHE = _countof(ptd->_setloc_data._Loc_c);
66704e0dc4aSTimo Kreuzer     __crt_ctype_compatibility_data buf1, buf2;
66804e0dc4aSTimo Kreuzer 
66904e0dc4aSTimo Kreuzer     if (!_expandlocale(wlocale, lctemp, _countof(lctemp), localeNameString, _countof(localeNameString), cptemp))
67004e0dc4aSTimo Kreuzer         return nullptr;
67104e0dc4aSTimo Kreuzer 
67204e0dc4aSTimo Kreuzer     // if this category's locale hadn't changed
67304e0dc4aSTimo Kreuzer     if (wcscmp(lctemp, ploci->lc_category[category].wlocale) == 0)
67404e0dc4aSTimo Kreuzer     {
67504e0dc4aSTimo Kreuzer         return ploci->lc_category[category].wlocale;
67604e0dc4aSTimo Kreuzer     }
67704e0dc4aSTimo Kreuzer 
67804e0dc4aSTimo Kreuzer     cch = wcslen(lctemp) + 1;
67904e0dc4aSTimo Kreuzer     if ((pch = static_cast<wchar_t*>(_malloc_crt(sizeof(int) + (cch * sizeof(wchar_t))))) == nullptr)
68004e0dc4aSTimo Kreuzer         return nullptr;  /* error if malloc fails */
68104e0dc4aSTimo Kreuzer 
68204e0dc4aSTimo Kreuzer     pch_cat_locale = pch + (sizeof(int) / sizeof(wchar_t));
68304e0dc4aSTimo Kreuzer 
68404e0dc4aSTimo Kreuzer      /* save for possible restore */
68504e0dc4aSTimo Kreuzer     oldlocale = ploci->lc_category[category].wlocale;
68604e0dc4aSTimo Kreuzer     oldlocalename = ploci->locale_name[category];
68704e0dc4aSTimo Kreuzer     oldcodepage = ploci->_public._locale_lc_codepage;
68804e0dc4aSTimo Kreuzer 
68904e0dc4aSTimo Kreuzer     /* update locale string */
69004e0dc4aSTimo Kreuzer     _ERRCHECK(wcscpy_s(pch_cat_locale, cch, lctemp));
69104e0dc4aSTimo Kreuzer     ploci->lc_category[category].wlocale = pch_cat_locale;
69204e0dc4aSTimo Kreuzer 
69304e0dc4aSTimo Kreuzer     /* Copy locale name */
69404e0dc4aSTimo Kreuzer     if (lctemp[0] == L'C' && lctemp[1] == L'\x0') // if "C" locale
69504e0dc4aSTimo Kreuzer         ploci->locale_name[category] = nullptr;
69604e0dc4aSTimo Kreuzer     else
69704e0dc4aSTimo Kreuzer         ploci->locale_name[category] = __acrt_copy_locale_name(localeNameString);
69804e0dc4aSTimo Kreuzer 
69904e0dc4aSTimo Kreuzer     /* To speedup locale based comparisions, we identify if the current
70004e0dc4aSTimo Kreuzer      * local has first 127 character set same as CLOCALE. If yes then
70104e0dc4aSTimo Kreuzer      * ploci->lc_clike = TRUE.
70204e0dc4aSTimo Kreuzer      */
70304e0dc4aSTimo Kreuzer 
70404e0dc4aSTimo Kreuzer     if (category==LC_CTYPE)
70504e0dc4aSTimo Kreuzer     {
70604e0dc4aSTimo Kreuzer         ploci->_public._locale_lc_codepage = cptemp;
70704e0dc4aSTimo Kreuzer         buf1 = _Loc_c[_LOC_CCACHE -1];
70804e0dc4aSTimo Kreuzer         /* brings the recently used codepage to the top. or else shifts
70904e0dc4aSTimo Kreuzer          * every thing down by one so that new _Loc_c can be placed at
71004e0dc4aSTimo Kreuzer          * the top.
71104e0dc4aSTimo Kreuzer          */
71204e0dc4aSTimo Kreuzer         for ( i = 0; i < _LOC_CCACHE; i++)
71304e0dc4aSTimo Kreuzer         {
71404e0dc4aSTimo Kreuzer             if (ploci->_public._locale_lc_codepage == _Loc_c[i].id)
71504e0dc4aSTimo Kreuzer             {
71604e0dc4aSTimo Kreuzer                 /* We don't really want to swap cache around in case what we are looking
71704e0dc4aSTimo Kreuzer                  *  for is the first element of the cache
71804e0dc4aSTimo Kreuzer                  */
71904e0dc4aSTimo Kreuzer                 if (i!=0)
72004e0dc4aSTimo Kreuzer                 {
72104e0dc4aSTimo Kreuzer                     _Loc_c[0] = _Loc_c[i];
72204e0dc4aSTimo Kreuzer                     _Loc_c[i] = buf1;
72304e0dc4aSTimo Kreuzer                 }
72404e0dc4aSTimo Kreuzer                 break;
72504e0dc4aSTimo Kreuzer             }
72604e0dc4aSTimo Kreuzer             else
72704e0dc4aSTimo Kreuzer             {
72804e0dc4aSTimo Kreuzer                 buf2 = _Loc_c[i];
72904e0dc4aSTimo Kreuzer                 _Loc_c[i] = buf1;
73004e0dc4aSTimo Kreuzer                 buf1 = buf2;
73104e0dc4aSTimo Kreuzer             }
73204e0dc4aSTimo Kreuzer         }
73304e0dc4aSTimo Kreuzer         if ( i == _LOC_CCACHE)
73404e0dc4aSTimo Kreuzer         {
73504e0dc4aSTimo Kreuzer             if ( __acrt_GetStringTypeA(nullptr, CT_CTYPE1,
73604e0dc4aSTimo Kreuzer                                        _first_127char,
73704e0dc4aSTimo Kreuzer                                        sizeof(_first_127char),
73804e0dc4aSTimo Kreuzer                                        out,
73904e0dc4aSTimo Kreuzer                                        ploci->_public._locale_lc_codepage,
74004e0dc4aSTimo Kreuzer                                        TRUE ))
74104e0dc4aSTimo Kreuzer             {
74204e0dc4aSTimo Kreuzer                 int j;
74304e0dc4aSTimo Kreuzer                 for ( j = 0; j < sizeof(_first_127char); j++)
74404e0dc4aSTimo Kreuzer                     out[j] = out[j]&
74504e0dc4aSTimo Kreuzer                             (_UPPER|_LOWER|_DIGIT|_SPACE|_PUNCT|_CONTROL|_BLANK|_HEX|_ALPHA);
74604e0dc4aSTimo Kreuzer                 if ( !memcmp(out, _ctype_loc_style, (sizeof(_first_127char)/sizeof(char))*sizeof(short)))
74704e0dc4aSTimo Kreuzer                 {
74804e0dc4aSTimo Kreuzer                     _Loc_c[0].is_clike = TRUE;
74904e0dc4aSTimo Kreuzer                 }
75004e0dc4aSTimo Kreuzer                 else
75104e0dc4aSTimo Kreuzer                 {
75204e0dc4aSTimo Kreuzer                     _Loc_c[0].is_clike = FALSE;
75304e0dc4aSTimo Kreuzer                 }
75404e0dc4aSTimo Kreuzer             }
75504e0dc4aSTimo Kreuzer             else
75604e0dc4aSTimo Kreuzer                 _Loc_c[0].is_clike = FALSE;
75704e0dc4aSTimo Kreuzer             _Loc_c[0].id = ploci->_public._locale_lc_codepage;
75804e0dc4aSTimo Kreuzer         }
75904e0dc4aSTimo Kreuzer         ploci->lc_clike = _Loc_c[0].is_clike;
76004e0dc4aSTimo Kreuzer     } /* category==LC_CTYPE */
76104e0dc4aSTimo Kreuzer     else if ( category == LC_COLLATE )
76204e0dc4aSTimo Kreuzer         ploci->lc_collate_cp = cptemp;
76304e0dc4aSTimo Kreuzer     else if ( category == LC_TIME )
76404e0dc4aSTimo Kreuzer         ploci->lc_time_cp = cptemp;
76504e0dc4aSTimo Kreuzer 
76604e0dc4aSTimo Kreuzer     if (__lc_category[category].init(ploci) != 0)
76704e0dc4aSTimo Kreuzer     {
76804e0dc4aSTimo Kreuzer         /* restore previous state */
76904e0dc4aSTimo Kreuzer         ploci->lc_category[category].wlocale = oldlocale;
77004e0dc4aSTimo Kreuzer         _free_crt(ploci->locale_name[category]);
77104e0dc4aSTimo Kreuzer         ploci->locale_name[category] = oldlocalename;
77204e0dc4aSTimo Kreuzer         _free_crt(pch);
77304e0dc4aSTimo Kreuzer         ploci->_public._locale_lc_codepage = oldcodepage;
77404e0dc4aSTimo Kreuzer 
77504e0dc4aSTimo Kreuzer         return nullptr; /* error if non-zero return */
77604e0dc4aSTimo Kreuzer     }
77704e0dc4aSTimo Kreuzer 
77804e0dc4aSTimo Kreuzer     /* locale set up successfully */
77904e0dc4aSTimo Kreuzer     /* Cleanup */
78004e0dc4aSTimo Kreuzer     if ((oldlocale != __acrt_wide_c_locale_string) &&
78104e0dc4aSTimo Kreuzer         (InterlockedDecrement(ploci->lc_category[category].wrefcount) == 0)
78204e0dc4aSTimo Kreuzer         )
78304e0dc4aSTimo Kreuzer     {
78404e0dc4aSTimo Kreuzer         _ASSERT(0);
78504e0dc4aSTimo Kreuzer         _free_crt(ploci->lc_category[category].wrefcount);
78604e0dc4aSTimo Kreuzer         _free_crt(ploci->lc_category[category].refcount);
78704e0dc4aSTimo Kreuzer         _free_crt(ploci->locale_name[category]);
78804e0dc4aSTimo Kreuzer         ploci->lc_category[category].wlocale = nullptr;
78904e0dc4aSTimo Kreuzer         ploci->locale_name[category] = nullptr;
79004e0dc4aSTimo Kreuzer     }
79104e0dc4aSTimo Kreuzer     if (pch)
79204e0dc4aSTimo Kreuzer     {
79304e0dc4aSTimo Kreuzer         reinterpret_cast<long&>(*pch) = 1;
79404e0dc4aSTimo Kreuzer     }
79504e0dc4aSTimo Kreuzer     ploci->lc_category[category].wrefcount = reinterpret_cast<long*>(pch);
79604e0dc4aSTimo Kreuzer 
79704e0dc4aSTimo Kreuzer     return ploci->lc_category[category].wlocale;
79804e0dc4aSTimo Kreuzer } /* _wsetlocale_set_cat */
79904e0dc4aSTimo Kreuzer 
80004e0dc4aSTimo Kreuzer 
80104e0dc4aSTimo Kreuzer 
_wsetlocale_get_all(__crt_locale_data * ploci)80204e0dc4aSTimo Kreuzer static wchar_t * __cdecl _wsetlocale_get_all ( __crt_locale_data* ploci)
80304e0dc4aSTimo Kreuzer {
80404e0dc4aSTimo Kreuzer     int i;
80504e0dc4aSTimo Kreuzer     int same = 1;
80604e0dc4aSTimo Kreuzer     wchar_t *pch = nullptr;
80704e0dc4aSTimo Kreuzer     size_t cch = 0;
80804e0dc4aSTimo Kreuzer     long *refcount = nullptr;
80904e0dc4aSTimo Kreuzer     size_t refcountSize = 0;
81004e0dc4aSTimo Kreuzer 
81104e0dc4aSTimo Kreuzer     /* allocate memory if necessary */
81204e0dc4aSTimo Kreuzer     refcountSize = sizeof(int) + (sizeof(wchar_t) * (MAX_LC_LEN+1) * (LC_MAX-LC_MIN+1)) + (sizeof(wchar_t) * CATNAMES_LEN);
81304e0dc4aSTimo Kreuzer     if ( (refcount = static_cast<long*>(_malloc_crt(refcountSize))) == nullptr)
81404e0dc4aSTimo Kreuzer         return nullptr;
81504e0dc4aSTimo Kreuzer 
81604e0dc4aSTimo Kreuzer     pch = ((wchar_t*)refcount) + (sizeof(int) / sizeof(wchar_t));
81704e0dc4aSTimo Kreuzer     cch = (refcountSize - sizeof(int)) / sizeof(wchar_t);
81804e0dc4aSTimo Kreuzer     *pch = L'\0';
81904e0dc4aSTimo Kreuzer     *refcount = 1;
82004e0dc4aSTimo Kreuzer     for (i=LC_MIN+1; ; i++)
82104e0dc4aSTimo Kreuzer     {
82204e0dc4aSTimo Kreuzer         _wcscats(pch, cch, 3, __lc_category[i].catname, L"=", ploci->lc_category[i].wlocale);
82304e0dc4aSTimo Kreuzer         if (i<LC_MAX)
82404e0dc4aSTimo Kreuzer         {
82504e0dc4aSTimo Kreuzer             _ERRCHECK(wcscat_s(pch, cch, L";"));
82604e0dc4aSTimo Kreuzer             if (wcscmp(ploci->lc_category[i].wlocale, ploci->lc_category[i+1].wlocale))
82704e0dc4aSTimo Kreuzer                 same=0;
82804e0dc4aSTimo Kreuzer         }
82904e0dc4aSTimo Kreuzer         else
83004e0dc4aSTimo Kreuzer         {
83104e0dc4aSTimo Kreuzer             if (!same) {
83204e0dc4aSTimo Kreuzer                 if (ploci->lc_category[LC_ALL].wrefcount != nullptr &&
83304e0dc4aSTimo Kreuzer                     InterlockedDecrement(ploci->lc_category[LC_ALL].wrefcount) == 0) {
83404e0dc4aSTimo Kreuzer                     _ASSERT(0);
83504e0dc4aSTimo Kreuzer                     _free_crt(ploci->lc_category[LC_ALL].wrefcount);
83604e0dc4aSTimo Kreuzer                 }
83704e0dc4aSTimo Kreuzer                 if (ploci->lc_category[LC_ALL].refcount != nullptr &&
83804e0dc4aSTimo Kreuzer                     InterlockedDecrement(ploci->lc_category[LC_ALL].refcount) == 0) {
83904e0dc4aSTimo Kreuzer                     _ASSERT(0);
84004e0dc4aSTimo Kreuzer                     _free_crt(ploci->lc_category[LC_ALL].refcount);
84104e0dc4aSTimo Kreuzer                 }
84204e0dc4aSTimo Kreuzer                 ploci->lc_category[LC_ALL].refcount = nullptr;
84304e0dc4aSTimo Kreuzer                 ploci->lc_category[LC_ALL].locale = nullptr;
84404e0dc4aSTimo Kreuzer                 ploci->lc_category[LC_ALL].wrefcount = refcount;
84504e0dc4aSTimo Kreuzer                 return ploci->lc_category[LC_ALL].wlocale = pch;
84604e0dc4aSTimo Kreuzer             } else {
84704e0dc4aSTimo Kreuzer                 _free_crt(refcount);
84804e0dc4aSTimo Kreuzer                 if (ploci->lc_category[LC_ALL].wrefcount != nullptr &&
84904e0dc4aSTimo Kreuzer                     InterlockedDecrement(ploci->lc_category[LC_ALL].wrefcount) == 0) {
85004e0dc4aSTimo Kreuzer                     _ASSERT(0);
85104e0dc4aSTimo Kreuzer                     _free_crt(ploci->lc_category[LC_ALL].wrefcount);
85204e0dc4aSTimo Kreuzer                 }
85304e0dc4aSTimo Kreuzer                 if (ploci->lc_category[LC_ALL].refcount != nullptr &&
85404e0dc4aSTimo Kreuzer                     InterlockedDecrement(ploci->lc_category[LC_ALL].refcount) == 0) {
85504e0dc4aSTimo Kreuzer                     _ASSERT(0);
85604e0dc4aSTimo Kreuzer                     _free_crt(ploci->lc_category[LC_ALL].refcount);
85704e0dc4aSTimo Kreuzer                 }
85804e0dc4aSTimo Kreuzer                 ploci->lc_category[LC_ALL].refcount = nullptr;
85904e0dc4aSTimo Kreuzer                 ploci->lc_category[LC_ALL].locale = nullptr;
86004e0dc4aSTimo Kreuzer                 ploci->lc_category[LC_ALL].wrefcount = nullptr;
86104e0dc4aSTimo Kreuzer                 ploci->lc_category[LC_ALL].wlocale = nullptr;
86204e0dc4aSTimo Kreuzer                 return ploci->lc_category[LC_CTYPE].wlocale;
86304e0dc4aSTimo Kreuzer             }
86404e0dc4aSTimo Kreuzer         }
86504e0dc4aSTimo Kreuzer     }
86604e0dc4aSTimo Kreuzer } /* _wsetlocale_get_all */
86704e0dc4aSTimo Kreuzer 
86804e0dc4aSTimo Kreuzer // --- BCP-47 tag parsing for _expandlocale() ---
86904e0dc4aSTimo Kreuzer //
87004e0dc4aSTimo Kreuzer // Provides parse_bcp47() which parses a given locale expression and updates a __crt_locale_strings structure accordingly.
87104e0dc4aSTimo Kreuzer // Unsupported:
87204e0dc4aSTimo Kreuzer // * Extension and private tags
87304e0dc4aSTimo Kreuzer // * 4-8 character language subtags
87404e0dc4aSTimo Kreuzer // * Grandfathered tags
87504e0dc4aSTimo Kreuzer // * Lone code page specifiers (POSIX extension for BCP-47)
87604e0dc4aSTimo Kreuzer 
87704e0dc4aSTimo Kreuzer enum class _bcp47_section_delimiter
87804e0dc4aSTimo Kreuzer {
87904e0dc4aSTimo Kreuzer     normal,
88004e0dc4aSTimo Kreuzer     end_of_string,
88104e0dc4aSTimo Kreuzer     code_page
88204e0dc4aSTimo Kreuzer };
88304e0dc4aSTimo Kreuzer 
88404e0dc4aSTimo Kreuzer struct _bcp47_section
88504e0dc4aSTimo Kreuzer {
88604e0dc4aSTimo Kreuzer     wchar_t const *          ptr;
88704e0dc4aSTimo Kreuzer     size_t                   length;
88804e0dc4aSTimo Kreuzer     _bcp47_section_delimiter delimiter;
88904e0dc4aSTimo Kreuzer };
89004e0dc4aSTimo Kreuzer 
categorize_delimiter(wchar_t const wc)89104e0dc4aSTimo Kreuzer static _bcp47_section_delimiter categorize_delimiter(wchar_t const wc)
89204e0dc4aSTimo Kreuzer {
89304e0dc4aSTimo Kreuzer     switch (wc)
89404e0dc4aSTimo Kreuzer     {
89504e0dc4aSTimo Kreuzer         case '-':
89604e0dc4aSTimo Kreuzer         case '_':
89704e0dc4aSTimo Kreuzer             return _bcp47_section_delimiter::normal;
89804e0dc4aSTimo Kreuzer         case '.':
89904e0dc4aSTimo Kreuzer             return _bcp47_section_delimiter::code_page;
90004e0dc4aSTimo Kreuzer         case '\0':
90104e0dc4aSTimo Kreuzer         default:
90204e0dc4aSTimo Kreuzer             return _bcp47_section_delimiter::end_of_string;
90304e0dc4aSTimo Kreuzer     }
90404e0dc4aSTimo Kreuzer }
90504e0dc4aSTimo Kreuzer 
string_is_alpha(wchar_t const * const str,size_t const len)90604e0dc4aSTimo Kreuzer static bool string_is_alpha(wchar_t const * const str, size_t const len)
90704e0dc4aSTimo Kreuzer {
90804e0dc4aSTimo Kreuzer     for (size_t i = 0; i < len; ++i)
90904e0dc4aSTimo Kreuzer     {
91004e0dc4aSTimo Kreuzer         if (!__ascii_isalpha(str[i]))
91104e0dc4aSTimo Kreuzer         {
91204e0dc4aSTimo Kreuzer             return false;
91304e0dc4aSTimo Kreuzer         }
91404e0dc4aSTimo Kreuzer     }
91504e0dc4aSTimo Kreuzer     return true;
91604e0dc4aSTimo Kreuzer }
91704e0dc4aSTimo Kreuzer 
string_is_digit(wchar_t const * const str,size_t const len)91804e0dc4aSTimo Kreuzer static bool string_is_digit(wchar_t const * const str, size_t const len)
91904e0dc4aSTimo Kreuzer {
92004e0dc4aSTimo Kreuzer     for (size_t i = 0; i < len; ++i)
92104e0dc4aSTimo Kreuzer     {
92204e0dc4aSTimo Kreuzer         if (!__ascii_isdigit(str[i]))
92304e0dc4aSTimo Kreuzer         {
92404e0dc4aSTimo Kreuzer             return false;
92504e0dc4aSTimo Kreuzer         }
92604e0dc4aSTimo Kreuzer     }
92704e0dc4aSTimo Kreuzer     return true;
92804e0dc4aSTimo Kreuzer }
92904e0dc4aSTimo Kreuzer 
parse_bcp47_language(__crt_locale_strings * const names,_bcp47_section const & section)93004e0dc4aSTimo Kreuzer static bool parse_bcp47_language(__crt_locale_strings * const names, _bcp47_section const& section)
93104e0dc4aSTimo Kreuzer {
93204e0dc4aSTimo Kreuzer     if (section.delimiter != _bcp47_section_delimiter::normal)
93304e0dc4aSTimo Kreuzer     {
93404e0dc4aSTimo Kreuzer         return false;
93504e0dc4aSTimo Kreuzer     }
93604e0dc4aSTimo Kreuzer 
93704e0dc4aSTimo Kreuzer     if (section.length < 2 || section.length > 3)
93804e0dc4aSTimo Kreuzer     {
93904e0dc4aSTimo Kreuzer         return false; // Failure, only 2-3 letter language codes permitted.
94004e0dc4aSTimo Kreuzer     }
94104e0dc4aSTimo Kreuzer 
94204e0dc4aSTimo Kreuzer     if (!string_is_alpha(section.ptr, section.length))
94304e0dc4aSTimo Kreuzer     {
94404e0dc4aSTimo Kreuzer         return false;
94504e0dc4aSTimo Kreuzer     }
94604e0dc4aSTimo Kreuzer 
94704e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncpy_s(names->szLanguage, _countof(names->szLanguage), section.ptr, section.length));
94804e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncpy_s(names->szLocaleName, _countof(names->szLocaleName), section.ptr, section.length));
94904e0dc4aSTimo Kreuzer     return true;
95004e0dc4aSTimo Kreuzer }
95104e0dc4aSTimo Kreuzer 
parse_bcp47_script(__crt_locale_strings * const names,_bcp47_section const & section)95204e0dc4aSTimo Kreuzer static bool parse_bcp47_script(__crt_locale_strings * const names, _bcp47_section const& section)
95304e0dc4aSTimo Kreuzer {
95404e0dc4aSTimo Kreuzer     if (section.delimiter != _bcp47_section_delimiter::normal)
95504e0dc4aSTimo Kreuzer     {
95604e0dc4aSTimo Kreuzer         return false;
95704e0dc4aSTimo Kreuzer     }
95804e0dc4aSTimo Kreuzer 
95904e0dc4aSTimo Kreuzer     if (section.length != 4) {
96004e0dc4aSTimo Kreuzer         return false; // Failure, only 4 letter script codes permitted.
96104e0dc4aSTimo Kreuzer     }
96204e0dc4aSTimo Kreuzer 
96304e0dc4aSTimo Kreuzer     if (!string_is_alpha(section.ptr, section.length))
96404e0dc4aSTimo Kreuzer     {
96504e0dc4aSTimo Kreuzer         return false;
96604e0dc4aSTimo Kreuzer     }
96704e0dc4aSTimo Kreuzer 
96804e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncat_s(names->szLocaleName, _countof(names->szLocaleName), L"-", 1));
96904e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncat_s(names->szLocaleName, _countof(names->szLocaleName), section.ptr, section.length));
97004e0dc4aSTimo Kreuzer     return true;
97104e0dc4aSTimo Kreuzer }
97204e0dc4aSTimo Kreuzer 
parse_bcp47_region(__crt_locale_strings * const names,_bcp47_section const & section)97304e0dc4aSTimo Kreuzer static bool parse_bcp47_region(__crt_locale_strings * const names, _bcp47_section const& section)
97404e0dc4aSTimo Kreuzer {
97504e0dc4aSTimo Kreuzer     if (section.delimiter != _bcp47_section_delimiter::normal)
97604e0dc4aSTimo Kreuzer     {
97704e0dc4aSTimo Kreuzer         return false;
97804e0dc4aSTimo Kreuzer     }
97904e0dc4aSTimo Kreuzer 
98004e0dc4aSTimo Kreuzer     if (   !(section.length == 2 && string_is_alpha(section.ptr, section.length))  // if not 2 letters
98104e0dc4aSTimo Kreuzer         && !(section.length == 3 && string_is_digit(section.ptr, section.length))) // and not 3 digits
98204e0dc4aSTimo Kreuzer     {
98304e0dc4aSTimo Kreuzer         return false;
98404e0dc4aSTimo Kreuzer     }
98504e0dc4aSTimo Kreuzer 
98604e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncpy_s(names->szCountry, _countof(names->szCountry), section.ptr, section.length));
98704e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncat_s(names->szLocaleName, _countof(names->szLocaleName), L"-", 1));
98804e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncat_s(names->szLocaleName, _countof(names->szLocaleName), section.ptr, section.length));
98904e0dc4aSTimo Kreuzer     return true;
99004e0dc4aSTimo Kreuzer }
99104e0dc4aSTimo Kreuzer 
parse_bcp47_code_page(__crt_locale_strings * const names,_bcp47_section const & section)99204e0dc4aSTimo Kreuzer static bool parse_bcp47_code_page(__crt_locale_strings * const names, _bcp47_section const& section)
99304e0dc4aSTimo Kreuzer {
99404e0dc4aSTimo Kreuzer     if (section.delimiter != _bcp47_section_delimiter::code_page)
99504e0dc4aSTimo Kreuzer     {
99604e0dc4aSTimo Kreuzer         return false;
99704e0dc4aSTimo Kreuzer     }
99804e0dc4aSTimo Kreuzer 
99904e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncpy_s(names->szCodePage, _countof(names->szCodePage), section.ptr, section.length));
100004e0dc4aSTimo Kreuzer     return true; // Success
100104e0dc4aSTimo Kreuzer }
100204e0dc4aSTimo Kreuzer 
parse_bcp47(__crt_locale_strings * const names,const wchar_t * const expr)100304e0dc4aSTimo Kreuzer static bool parse_bcp47(__crt_locale_strings * const names, const wchar_t * const expr)
100404e0dc4aSTimo Kreuzer {
100504e0dc4aSTimo Kreuzer     wchar_t const * p = expr;
100604e0dc4aSTimo Kreuzer     wchar_t const * const delimiters = L"-_.";
100704e0dc4aSTimo Kreuzer 
100804e0dc4aSTimo Kreuzer     memset(names, 0, sizeof(__crt_locale_strings));
100904e0dc4aSTimo Kreuzer 
101004e0dc4aSTimo Kreuzer     size_t const max_sections = 4;
101104e0dc4aSTimo Kreuzer     _bcp47_section sections[max_sections];
101204e0dc4aSTimo Kreuzer     size_t num_sections = 0;
101304e0dc4aSTimo Kreuzer 
101404e0dc4aSTimo Kreuzer     for (auto last_delimiter = _bcp47_section_delimiter::normal;
101504e0dc4aSTimo Kreuzer          last_delimiter != _bcp47_section_delimiter::end_of_string;
101604e0dc4aSTimo Kreuzer          last_delimiter = categorize_delimiter(*p++)
101704e0dc4aSTimo Kreuzer         )
101804e0dc4aSTimo Kreuzer     {
101904e0dc4aSTimo Kreuzer         if (num_sections >= max_sections)
102004e0dc4aSTimo Kreuzer         {
102104e0dc4aSTimo Kreuzer             // Didn't reach end of string before running out of sections to parse.
102204e0dc4aSTimo Kreuzer             return false;
102304e0dc4aSTimo Kreuzer         }
102404e0dc4aSTimo Kreuzer 
102504e0dc4aSTimo Kreuzer         size_t const section_length = (last_delimiter != _bcp47_section_delimiter::code_page)
102604e0dc4aSTimo Kreuzer             ? wcscspn(p, delimiters) : wcslen(p);
102704e0dc4aSTimo Kreuzer 
102804e0dc4aSTimo Kreuzer         sections[num_sections].ptr = p;
102904e0dc4aSTimo Kreuzer         sections[num_sections].length = section_length;
103004e0dc4aSTimo Kreuzer         sections[num_sections].delimiter = last_delimiter;
103104e0dc4aSTimo Kreuzer 
103204e0dc4aSTimo Kreuzer         p += section_length;
103304e0dc4aSTimo Kreuzer         ++num_sections;
103404e0dc4aSTimo Kreuzer     }
103504e0dc4aSTimo Kreuzer 
103604e0dc4aSTimo Kreuzer     switch (num_sections)
103704e0dc4aSTimo Kreuzer     {
103804e0dc4aSTimo Kreuzer         case 1:
103904e0dc4aSTimo Kreuzer             // en
104004e0dc4aSTimo Kreuzer             return parse_bcp47_language(names, sections[0]);
104104e0dc4aSTimo Kreuzer         case 2:
104204e0dc4aSTimo Kreuzer             // en-US
104304e0dc4aSTimo Kreuzer             // zh-Hans
104404e0dc4aSTimo Kreuzer             // en.utf-8
104504e0dc4aSTimo Kreuzer             // .utf-8
104604e0dc4aSTimo Kreuzer             return parse_bcp47_language(names, sections[0])
104704e0dc4aSTimo Kreuzer                    && (   parse_bcp47_script(names, sections[1])
104804e0dc4aSTimo Kreuzer                        || parse_bcp47_region(names, sections[1])
104904e0dc4aSTimo Kreuzer                        || parse_bcp47_code_page(names, sections[1])
105004e0dc4aSTimo Kreuzer                       );
105104e0dc4aSTimo Kreuzer         case 3:
105204e0dc4aSTimo Kreuzer             // en-US.utf-8
105304e0dc4aSTimo Kreuzer             // zh-Hans-HK
105404e0dc4aSTimo Kreuzer             return parse_bcp47_language(names, sections[0])
105504e0dc4aSTimo Kreuzer                    && (    parse_bcp47_script(names, sections[1]) && (parse_bcp47_region(names, sections[2]) || parse_bcp47_code_page(names, sections[2]))
105604e0dc4aSTimo Kreuzer                        || (parse_bcp47_region(names, sections[1]) &&  parse_bcp47_code_page(names, sections[2]))
105704e0dc4aSTimo Kreuzer                       );
105804e0dc4aSTimo Kreuzer         case 4:
105904e0dc4aSTimo Kreuzer             // zh-Hans-HK.utf-8
106004e0dc4aSTimo Kreuzer             return parse_bcp47_language(names, sections[0])
106104e0dc4aSTimo Kreuzer                 && parse_bcp47_script(names, sections[1])
106204e0dc4aSTimo Kreuzer                 && parse_bcp47_region(names, sections[2])
106304e0dc4aSTimo Kreuzer                 && parse_bcp47_code_page(names, sections[3]);
106404e0dc4aSTimo Kreuzer         default:
106504e0dc4aSTimo Kreuzer             return false;
106604e0dc4aSTimo Kreuzer     }
106704e0dc4aSTimo Kreuzer }
106804e0dc4aSTimo Kreuzer 
106904e0dc4aSTimo Kreuzer // Utility functions/structs for _expandlocale().
107004e0dc4aSTimo Kreuzer 
get_default_code_page(wchar_t const * const valid_windows_locale_name)107104e0dc4aSTimo Kreuzer static int get_default_code_page(wchar_t const * const valid_windows_locale_name)
107204e0dc4aSTimo Kreuzer {
107304e0dc4aSTimo Kreuzer     int code_page{};
107404e0dc4aSTimo Kreuzer 
107504e0dc4aSTimo Kreuzer     int const size_written = __acrt_GetLocaleInfoEx(
107604e0dc4aSTimo Kreuzer         valid_windows_locale_name,
107704e0dc4aSTimo Kreuzer         LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
107804e0dc4aSTimo Kreuzer         reinterpret_cast<LPWSTR>(&code_page),
107904e0dc4aSTimo Kreuzer         sizeof(int) / sizeof(wchar_t)
108004e0dc4aSTimo Kreuzer         );
108104e0dc4aSTimo Kreuzer 
108204e0dc4aSTimo Kreuzer     if (size_written == 0 || code_page == 0)
108304e0dc4aSTimo Kreuzer     {   // Default to UTF-8 if could not find or no default code page.
108404e0dc4aSTimo Kreuzer         return CP_UTF8;
108504e0dc4aSTimo Kreuzer     }
108604e0dc4aSTimo Kreuzer 
108704e0dc4aSTimo Kreuzer     return code_page;
108804e0dc4aSTimo Kreuzer }
108904e0dc4aSTimo Kreuzer 
109004e0dc4aSTimo Kreuzer class _expandlocale_locale_name_cache
109104e0dc4aSTimo Kreuzer {
109204e0dc4aSTimo Kreuzer     // Scope guard to keep invariants in _expandlocale for the localeNameOutput and _cacheLocaleName variables.
109304e0dc4aSTimo Kreuzer     // The cacheLocaleName/localeNameOutput is the only cache where work is done progressively in the cache value,
109404e0dc4aSTimo Kreuzer     // (inside __acrt_get_qualified_locale and __acrt_get_qualified_locale_downlevel)
109504e0dc4aSTimo Kreuzer     // so we must save/restore at function start and invalidate the cache once we have stuff to commit to both.
109604e0dc4aSTimo Kreuzer public:
_expandlocale_locale_name_cache(wchar_t * const localeNameOutput,size_t const localeNameSizeInChars,__crt_qualified_locale_data * const psetloc_data)109704e0dc4aSTimo Kreuzer     _expandlocale_locale_name_cache(
109804e0dc4aSTimo Kreuzer         wchar_t *                     const localeNameOutput,
109904e0dc4aSTimo Kreuzer         size_t                        const localeNameSizeInChars,
110004e0dc4aSTimo Kreuzer         __crt_qualified_locale_data * const psetloc_data
110104e0dc4aSTimo Kreuzer     ) : _localeNameOutput(localeNameOutput),
110204e0dc4aSTimo Kreuzer         _localeNameSizeInChars(localeNameSizeInChars),
110304e0dc4aSTimo Kreuzer         _psetloc_data(psetloc_data),
110404e0dc4aSTimo Kreuzer         _committed(false)
110504e0dc4aSTimo Kreuzer     {
110604e0dc4aSTimo Kreuzer         _ERRCHECK(wcsncpy_s(localeNameOutput, localeNameSizeInChars, psetloc_data->_cacheLocaleName, _countof(psetloc_data->_cacheLocaleName)));
110704e0dc4aSTimo Kreuzer     }
110804e0dc4aSTimo Kreuzer 
~_expandlocale_locale_name_cache()110904e0dc4aSTimo Kreuzer     ~_expandlocale_locale_name_cache()
111004e0dc4aSTimo Kreuzer     {
111104e0dc4aSTimo Kreuzer         if (!_committed)
111204e0dc4aSTimo Kreuzer         {
111304e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(_psetloc_data->_cacheLocaleName, _countof(_psetloc_data->_cacheLocaleName), _localeNameOutput, _localeNameSizeInChars));
111404e0dc4aSTimo Kreuzer         }
111504e0dc4aSTimo Kreuzer     }
111604e0dc4aSTimo Kreuzer 
111704e0dc4aSTimo Kreuzer     _expandlocale_locale_name_cache(_expandlocale_locale_name_cache const&) = delete;
111804e0dc4aSTimo Kreuzer     _expandlocale_locale_name_cache& operator=(_expandlocale_locale_name_cache const&) = delete;
111904e0dc4aSTimo Kreuzer 
commit_locale_name(wchar_t const * const new_locale_name,size_t const new_locale_name_length)112004e0dc4aSTimo Kreuzer     void commit_locale_name(wchar_t const * const new_locale_name, size_t const new_locale_name_length)
112104e0dc4aSTimo Kreuzer     {
112204e0dc4aSTimo Kreuzer         _ERRCHECK(wcsncpy_s(_psetloc_data->_cacheLocaleName, _countof(_psetloc_data->_cacheLocaleName), new_locale_name, new_locale_name_length));
112304e0dc4aSTimo Kreuzer         commit_locale_name_cache_already_updated(new_locale_name, new_locale_name_length);
112404e0dc4aSTimo Kreuzer     }
112504e0dc4aSTimo Kreuzer 
commit_locale_name_cache_already_updated(wchar_t const * const new_locale_name,size_t const new_locale_name_length)112604e0dc4aSTimo Kreuzer     void commit_locale_name_cache_already_updated(wchar_t const * const new_locale_name, size_t const new_locale_name_length)
112704e0dc4aSTimo Kreuzer     {
112804e0dc4aSTimo Kreuzer         _ERRCHECK(wcsncpy_s(_localeNameOutput, _localeNameSizeInChars, new_locale_name, new_locale_name_length));
112904e0dc4aSTimo Kreuzer         _committed = true;
113004e0dc4aSTimo Kreuzer     }
113104e0dc4aSTimo Kreuzer 
113204e0dc4aSTimo Kreuzer private:
113304e0dc4aSTimo Kreuzer     wchar_t *                     _localeNameOutput;
113404e0dc4aSTimo Kreuzer     size_t                        _localeNameSizeInChars;
113504e0dc4aSTimo Kreuzer     __crt_qualified_locale_data * _psetloc_data;
113604e0dc4aSTimo Kreuzer     bool                          _committed;
113704e0dc4aSTimo Kreuzer };
113804e0dc4aSTimo Kreuzer 
_expandlocale(wchar_t const * const expr,wchar_t * const output,size_t const sizeInChars,wchar_t * const localeNameOutput,size_t const localeNameSizeInChars,UINT & output_code_page)113904e0dc4aSTimo Kreuzer wchar_t * _expandlocale(
114004e0dc4aSTimo Kreuzer     wchar_t const * const expr,
114104e0dc4aSTimo Kreuzer     wchar_t *       const output,
114204e0dc4aSTimo Kreuzer     size_t          const sizeInChars,
114304e0dc4aSTimo Kreuzer     wchar_t *       const localeNameOutput,
114404e0dc4aSTimo Kreuzer     size_t          const localeNameSizeInChars,
114504e0dc4aSTimo Kreuzer     UINT&                 output_code_page
114604e0dc4aSTimo Kreuzer     )
114704e0dc4aSTimo Kreuzer {
114804e0dc4aSTimo Kreuzer     // Returns: locale name to return to user (lifetime bound to PTD variable _cacheout)
114904e0dc4aSTimo Kreuzer     // Out Parameters:
115004e0dc4aSTimo Kreuzer     //  * output: locale name to return to user (lifetime bound to supplied buffer)
115104e0dc4aSTimo Kreuzer     //  * localeNameOutput: normalized locale name to be used internally (lifetime bound to supplied buffer)
115204e0dc4aSTimo Kreuzer     //  * output_code_page: code page used
115304e0dc4aSTimo Kreuzer     //
115404e0dc4aSTimo Kreuzer     // Effects:
115504e0dc4aSTimo Kreuzer     //  Parses an input locale string and returns the string that will replicate the effects just taken (output), the string to be used
115604e0dc4aSTimo Kreuzer     //  for Win32 calls (localeNameOutput), and the code page.
115704e0dc4aSTimo Kreuzer     //  Note that there are three modes here:
115804e0dc4aSTimo Kreuzer     //  * Legacy and Windows Locale Names:
115904e0dc4aSTimo Kreuzer     //      The output locale string and internally used locale string become normalized for Win32 APIs.
116004e0dc4aSTimo Kreuzer     //      Neither necessarily match the user input locale.
116104e0dc4aSTimo Kreuzer     //      Uses ACP as default if Windows does not know the code page.
116204e0dc4aSTimo Kreuzer     //  * Permissive Windows locale names:
116304e0dc4aSTimo Kreuzer     //      Ask Windows whether full given user string is a known locale name.
116404e0dc4aSTimo Kreuzer     //      In that case, the output locale string, internally used locale string, and user input locale all match.
116504e0dc4aSTimo Kreuzer     //      Uses UTF-8 as default if Windows does not know the code page.
116604e0dc4aSTimo Kreuzer     //  * BCP-47 Locale Names:
116704e0dc4aSTimo Kreuzer     //      Windows doesn't recognize BCP-47 + code page specifiers, but we must support them for proper UTF-8 support.
116804e0dc4aSTimo Kreuzer     //      The output locale name must include the code page, but internally we modify the format so Windows understands it.
116904e0dc4aSTimo Kreuzer     //      Uses UTF-8 as default if Windows does not know the code page.
117004e0dc4aSTimo Kreuzer     //
117104e0dc4aSTimo Kreuzer     //  Also populates _psetloc_data structure from the PTD:
117204e0dc4aSTimo Kreuzer     //  Caching Internals:
117304e0dc4aSTimo Kreuzer     //    * _cachein: Caches last successful 'expr'.
117404e0dc4aSTimo Kreuzer     //    * _cacheout: Caches last successful 'output' (which is also the return value).
117504e0dc4aSTimo Kreuzer     //    * _cachecp: Caches last successful 'output_code_page'.
117604e0dc4aSTimo Kreuzer     //    * _cacheLocaleName: Caches last successful 'localeNameOutput'.
117704e0dc4aSTimo Kreuzer     //  Legacy Internals (used by __acrt_get_qualified_locale and __acrt_get_qualified_locale_downlevel only):
117804e0dc4aSTimo Kreuzer     //    * pchLanguage: Pointer to start of language name, ex: "English"
117904e0dc4aSTimo Kreuzer     //    * pchCountry: Pointer to start of country/region name, ex: "United States"
118004e0dc4aSTimo Kreuzer     //    * iLocState: Used to record the match degree for locales checked (i.e. language and region match vs just language match)
118104e0dc4aSTimo Kreuzer     //    * bAbbrevLanguage: Whether language name is a three-letter short-form, ex: "ENU"
118204e0dc4aSTimo Kreuzer     //    * bAbbrevCountry: Whether country/region name is a three letter short-form, ex: "USA"
118304e0dc4aSTimo Kreuzer     //    * iPrimaryLen: Length of pchLanguage portion of locale string
118404e0dc4aSTimo Kreuzer     //  Also in _psetloc_data, but not used in _expandlocale: _Loc_c
118504e0dc4aSTimo Kreuzer 
118604e0dc4aSTimo Kreuzer     if (!expr)
118704e0dc4aSTimo Kreuzer     {
118804e0dc4aSTimo Kreuzer         return nullptr; // error if no input
118904e0dc4aSTimo Kreuzer     }
119004e0dc4aSTimo Kreuzer 
119104e0dc4aSTimo Kreuzer     // C locale
119204e0dc4aSTimo Kreuzer     // Callers know that LocaleNameOutput has not been updated and check for "C" locale before using it.
119304e0dc4aSTimo Kreuzer     if (expr[0] == L'C' && expr[1] == L'\0')
119404e0dc4aSTimo Kreuzer     {
119504e0dc4aSTimo Kreuzer         _ERRCHECK(wcscpy_s(output, sizeInChars, L"C"));
119604e0dc4aSTimo Kreuzer         output_code_page = CP_ACP;
119704e0dc4aSTimo Kreuzer         return output;
119804e0dc4aSTimo Kreuzer     }
119904e0dc4aSTimo Kreuzer 
120004e0dc4aSTimo Kreuzer     __crt_qualified_locale_data * const _psetloc_data    = &__acrt_getptd()->_setloc_data;
120104e0dc4aSTimo Kreuzer     UINT *                        const pcachecp         = &_psetloc_data->_cachecp;
120204e0dc4aSTimo Kreuzer     wchar_t *                     const cachein          = _psetloc_data->_cachein;
120304e0dc4aSTimo Kreuzer     size_t                        const cacheinLen       = _countof(_psetloc_data->_cachein);
120404e0dc4aSTimo Kreuzer     wchar_t *                     const cacheout         = _psetloc_data->_cacheout;
120504e0dc4aSTimo Kreuzer     size_t                        const cacheoutLen      = _countof(_psetloc_data->_cacheout);
120604e0dc4aSTimo Kreuzer     size_t                              charactersInExpression = 0;
120704e0dc4aSTimo Kreuzer     int                                 iCodePage = 0;
120804e0dc4aSTimo Kreuzer 
120904e0dc4aSTimo Kreuzer     // This guard owns access to localeNameOutput. It expresses the invariants that localeNameOutput and _cacheLocaleName have.
121004e0dc4aSTimo Kreuzer     _expandlocale_locale_name_cache locale_name_guard(localeNameOutput, localeNameSizeInChars, _psetloc_data);
121104e0dc4aSTimo Kreuzer 
121204e0dc4aSTimo Kreuzer     // First, make sure we didn't just do this one
121304e0dc4aSTimo Kreuzer     charactersInExpression = wcslen(expr);
121404e0dc4aSTimo Kreuzer     if (charactersInExpression >= MAX_LC_LEN ||       // we would never have cached this
121504e0dc4aSTimo Kreuzer         (wcscmp(cacheout, expr) && wcscmp(cachein, expr)))
121604e0dc4aSTimo Kreuzer     {
121704e0dc4aSTimo Kreuzer         __crt_locale_strings names;
121804e0dc4aSTimo Kreuzer         BOOL getqloc_results = FALSE;
121904e0dc4aSTimo Kreuzer         BOOL const isDownlevel = !__acrt_can_use_vista_locale_apis();
122004e0dc4aSTimo Kreuzer 
122104e0dc4aSTimo Kreuzer         // Check if can parse as Legacy/Windows locale name
122204e0dc4aSTimo Kreuzer         if (__lc_wcstolc(&names, expr) == 0)
122304e0dc4aSTimo Kreuzer         {
122404e0dc4aSTimo Kreuzer             if (isDownlevel)
122504e0dc4aSTimo Kreuzer             {
122604e0dc4aSTimo Kreuzer                 getqloc_results = __acrt_get_qualified_locale_downlevel(&names, pcachecp, &names);
122704e0dc4aSTimo Kreuzer             }
122804e0dc4aSTimo Kreuzer             else
122904e0dc4aSTimo Kreuzer             {
123004e0dc4aSTimo Kreuzer                 getqloc_results = __acrt_get_qualified_locale(&names, pcachecp, &names);
123104e0dc4aSTimo Kreuzer             }
123204e0dc4aSTimo Kreuzer         }
123304e0dc4aSTimo Kreuzer 
123404e0dc4aSTimo Kreuzer         // If successfully parsed as Legacy/Windows name
123504e0dc4aSTimo Kreuzer         if (getqloc_results)
123604e0dc4aSTimo Kreuzer         {
123704e0dc4aSTimo Kreuzer             // Legacy/Windows name resolution returns applies normalization to both returned and internally used locale names.
123804e0dc4aSTimo Kreuzer             __lc_lctowcs(cacheout, cacheoutLen, &names);
123904e0dc4aSTimo Kreuzer 
124004e0dc4aSTimo Kreuzer             // __acrt_get_qualified_locale and __acrt_get_qualified_locale_downlevel update _cacheLocaleName - if successful commit locale changes
124104e0dc4aSTimo Kreuzer             locale_name_guard.commit_locale_name_cache_already_updated(names.szLocaleName, wcslen(names.szLocaleName) + 1);
124204e0dc4aSTimo Kreuzer         }
124304e0dc4aSTimo Kreuzer         else if (__acrt_IsValidLocaleName(expr))
124404e0dc4aSTimo Kreuzer         {
124504e0dc4aSTimo Kreuzer             // Windows recognizes the locale expression - only work is to grab the code page to be used.
124604e0dc4aSTimo Kreuzer             iCodePage = get_default_code_page(expr);
124704e0dc4aSTimo Kreuzer 
124804e0dc4aSTimo Kreuzer             // Commit code page
124904e0dc4aSTimo Kreuzer             *pcachecp = static_cast<WORD>(iCodePage);
125004e0dc4aSTimo Kreuzer 
125104e0dc4aSTimo Kreuzer             // Commit locale name that we will return to the user (same as input locale string).
125204e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(cacheout, cacheoutLen, expr, charactersInExpression + 1));
125304e0dc4aSTimo Kreuzer 
125404e0dc4aSTimo Kreuzer             // Commit locale name for internal use (same as input locale string).
125504e0dc4aSTimo Kreuzer             locale_name_guard.commit_locale_name(expr, charactersInExpression + 1);
125604e0dc4aSTimo Kreuzer         }
125704e0dc4aSTimo Kreuzer         else if (parse_bcp47(&names, expr) && __acrt_IsValidLocaleName(names.szLocaleName))
125804e0dc4aSTimo Kreuzer         {
125904e0dc4aSTimo Kreuzer             // Parsed as Windows-recognized BCP-47 tag
126004e0dc4aSTimo Kreuzer             wchar_t const * const normalized_locale_name = names.szLocaleName;
126104e0dc4aSTimo Kreuzer 
126204e0dc4aSTimo Kreuzer             if (names.szCodePage[0])
126304e0dc4aSTimo Kreuzer             {
126404e0dc4aSTimo Kreuzer                 wchar_t const * const cp = names.szCodePage;
126504e0dc4aSTimo Kreuzer 
126604e0dc4aSTimo Kreuzer                 // Allow .utf8/.utf-8 for BCP-47 tags.
126704e0dc4aSTimo Kreuzer                 if (   __ascii_towlower(cp[0]) == L'u'
126804e0dc4aSTimo Kreuzer                     && __ascii_towlower(cp[1]) == L't'
126904e0dc4aSTimo Kreuzer                     && __ascii_towlower(cp[2]) == L'f'
127004e0dc4aSTimo Kreuzer                     &&     (cp[3] == L'8' && cp[4] == L'\0')
127104e0dc4aSTimo Kreuzer                         || (cp[3] == L'-' && cp[4] == L'8' && cp[5] == L'\0'))
127204e0dc4aSTimo Kreuzer                 {
127304e0dc4aSTimo Kreuzer                     iCodePage = CP_UTF8;
127404e0dc4aSTimo Kreuzer                 }
127504e0dc4aSTimo Kreuzer                 else
127604e0dc4aSTimo Kreuzer                 {
127704e0dc4aSTimo Kreuzer                     // Other codepage tokens (fr-FR.1252, etc.) aren't supported for BCP-47 tags (eg: Use Unicode!)
127804e0dc4aSTimo Kreuzer                     return nullptr;
127904e0dc4aSTimo Kreuzer                 }
128004e0dc4aSTimo Kreuzer             }
128104e0dc4aSTimo Kreuzer             else
128204e0dc4aSTimo Kreuzer             {
128304e0dc4aSTimo Kreuzer                 iCodePage = get_default_code_page(normalized_locale_name);
128404e0dc4aSTimo Kreuzer             }
128504e0dc4aSTimo Kreuzer 
128604e0dc4aSTimo Kreuzer             // Commit code page
128704e0dc4aSTimo Kreuzer             *pcachecp = static_cast<WORD>(iCodePage);
128804e0dc4aSTimo Kreuzer 
128904e0dc4aSTimo Kreuzer             // Commit locale name that we will return to the user (same as input locale string).
129004e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(cacheout, cacheoutLen, expr, charactersInExpression + 1));
129104e0dc4aSTimo Kreuzer 
129204e0dc4aSTimo Kreuzer             // Commit normalized name for internal use.
129304e0dc4aSTimo Kreuzer             locale_name_guard.commit_locale_name(normalized_locale_name, wcslen(normalized_locale_name) + 1);
129404e0dc4aSTimo Kreuzer         }
129504e0dc4aSTimo Kreuzer         else
129604e0dc4aSTimo Kreuzer         {
129704e0dc4aSTimo Kreuzer             return nullptr;  // input unrecognized as locale name
129804e0dc4aSTimo Kreuzer         }
129904e0dc4aSTimo Kreuzer 
130004e0dc4aSTimo Kreuzer         // Operation succeeded - commit input cache.
130104e0dc4aSTimo Kreuzer         if (*expr && charactersInExpression < MAX_LC_LEN)
130204e0dc4aSTimo Kreuzer         {
130304e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(cachein, cacheinLen, expr, charactersInExpression + 1));
130404e0dc4aSTimo Kreuzer         }
130504e0dc4aSTimo Kreuzer         else
130604e0dc4aSTimo Kreuzer         {
130704e0dc4aSTimo Kreuzer             *cachein = L'\x0';
130804e0dc4aSTimo Kreuzer         }
130904e0dc4aSTimo Kreuzer     }
131004e0dc4aSTimo Kreuzer 
131104e0dc4aSTimo Kreuzer     output_code_page = *pcachecp; // Update code page
131204e0dc4aSTimo Kreuzer 
131304e0dc4aSTimo Kreuzer     _ERRCHECK(wcscpy_s(output, sizeInChars, cacheout));
131404e0dc4aSTimo Kreuzer     return cacheout; // Return locale name to be given back to user, with lifetime bound in PTD
131504e0dc4aSTimo Kreuzer }
131604e0dc4aSTimo Kreuzer 
131704e0dc4aSTimo Kreuzer /* helpers */
131804e0dc4aSTimo Kreuzer 
_wcscats(wchar_t * outstr,size_t numberOfElements,int n,...)131904e0dc4aSTimo Kreuzer void _wcscats ( wchar_t *outstr, size_t numberOfElements, int n, ...)
132004e0dc4aSTimo Kreuzer {
132104e0dc4aSTimo Kreuzer     int i;
132204e0dc4aSTimo Kreuzer     va_list substr;
132304e0dc4aSTimo Kreuzer 
132404e0dc4aSTimo Kreuzer     va_start (substr, n);
132504e0dc4aSTimo Kreuzer 
132604e0dc4aSTimo Kreuzer     for (i =0; i<n; i++)
132704e0dc4aSTimo Kreuzer     {
132804e0dc4aSTimo Kreuzer         _ERRCHECK(wcscat_s(outstr, numberOfElements, va_arg(substr, wchar_t *)));
132904e0dc4aSTimo Kreuzer     }
133004e0dc4aSTimo Kreuzer     va_end(substr);
133104e0dc4aSTimo Kreuzer }
133204e0dc4aSTimo Kreuzer 
133304e0dc4aSTimo Kreuzer // Parse the wlocale string to find the array of language/region/codepage strings (if available)
133404e0dc4aSTimo Kreuzer // in the form .CodePage, Language, Language_Region, Language_Region.Codepage, or Language.CodePage
133504e0dc4aSTimo Kreuzer // If the input is a BCP-47 tag, then that tag is returned in the names language.
__lc_wcstolc(__crt_locale_strings * names,const wchar_t * wlocale)133604e0dc4aSTimo Kreuzer int __lc_wcstolc ( __crt_locale_strings *names, const wchar_t *wlocale)
133704e0dc4aSTimo Kreuzer {
133804e0dc4aSTimo Kreuzer     int i;
133904e0dc4aSTimo Kreuzer     size_t len;
134004e0dc4aSTimo Kreuzer     wchar_t wch;
134104e0dc4aSTimo Kreuzer 
134204e0dc4aSTimo Kreuzer     memset((void *)names, '\0', sizeof(__crt_locale_strings));  /* clear out result */
134304e0dc4aSTimo Kreuzer 
134404e0dc4aSTimo Kreuzer     if (*wlocale==L'\0')
134504e0dc4aSTimo Kreuzer         return 0; /* trivial case */
134604e0dc4aSTimo Kreuzer 
134704e0dc4aSTimo Kreuzer     /* only code page is given */
134804e0dc4aSTimo Kreuzer     if (wlocale[0] == L'.' && wlocale[1] != L'\0')
134904e0dc4aSTimo Kreuzer     {
135004e0dc4aSTimo Kreuzer         _ERRCHECK(wcsncpy_s(names->szCodePage, _countof(names->szCodePage), &wlocale[1], MAX_CP_LEN-1));
135104e0dc4aSTimo Kreuzer         /* Make sure to null terminate the string in case wlocale is > MAX_CP_LEN */
135204e0dc4aSTimo Kreuzer         names->szCodePage[ MAX_CP_LEN -1] = 0;
135304e0dc4aSTimo Kreuzer         return 0;
135404e0dc4aSTimo Kreuzer     }
135504e0dc4aSTimo Kreuzer 
135604e0dc4aSTimo Kreuzer     // Looks like Language_Country/Region.Codepage
135704e0dc4aSTimo Kreuzer     for (i=0; ; i++)
135804e0dc4aSTimo Kreuzer     {
135904e0dc4aSTimo Kreuzer         // _ language country/region separator, . is before codepage, either , or \0 are end of string.
136004e0dc4aSTimo Kreuzer         len = wcscspn(wlocale, L"_.,");
136104e0dc4aSTimo Kreuzer         if (len == 0)
136204e0dc4aSTimo Kreuzer             return -1;  /* syntax error, can't start with a separator */
136304e0dc4aSTimo Kreuzer 
136404e0dc4aSTimo Kreuzer         wch = wlocale[len];
136504e0dc4aSTimo Kreuzer 
136604e0dc4aSTimo Kreuzer         if ((i == 0) && (len < MAX_LANG_LEN))
136704e0dc4aSTimo Kreuzer         {
136804e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(names->szLanguage, _countof(names->szLanguage), wlocale, len));
136904e0dc4aSTimo Kreuzer             if (wch == L'.')
137004e0dc4aSTimo Kreuzer             {
137104e0dc4aSTimo Kreuzer                 // '.' is a delimiter before codepage, so codepage is expected next (skip country/region)
137204e0dc4aSTimo Kreuzer                 i++;
137304e0dc4aSTimo Kreuzer             }
137404e0dc4aSTimo Kreuzer         }
137504e0dc4aSTimo Kreuzer 
137604e0dc4aSTimo Kreuzer         else if ((i==1) && (len<MAX_CTRY_LEN) && (wch!=L'_'))
137704e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(names->szCountry, _countof(names->szCountry), wlocale, len));
137804e0dc4aSTimo Kreuzer 
137904e0dc4aSTimo Kreuzer         else if ((i==2) && (len<MAX_CP_LEN) && (wch==L'\0' || wch==L','))
138004e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(names->szCodePage, _countof(names->szCodePage), wlocale, len));
138104e0dc4aSTimo Kreuzer 
138204e0dc4aSTimo Kreuzer         else
138304e0dc4aSTimo Kreuzer             return -1;  /* error parsing wlocale string */
138404e0dc4aSTimo Kreuzer 
138504e0dc4aSTimo Kreuzer         if (wch==L',')
138604e0dc4aSTimo Kreuzer         {
138704e0dc4aSTimo Kreuzer             /* modifier not used in current implementation, but it
138804e0dc4aSTimo Kreuzer                must be parsed to for POSIX/XOpen conformance */
138904e0dc4aSTimo Kreuzer             /*  wcsncpy(names->szModifier, wlocale, MAX_MODIFIER_LEN-1); */
139004e0dc4aSTimo Kreuzer             break;
139104e0dc4aSTimo Kreuzer         }
139204e0dc4aSTimo Kreuzer 
139304e0dc4aSTimo Kreuzer         if (!wch)
139404e0dc4aSTimo Kreuzer             break;
139504e0dc4aSTimo Kreuzer         wlocale+=(len+1);
139604e0dc4aSTimo Kreuzer     }
139704e0dc4aSTimo Kreuzer     return 0;
139804e0dc4aSTimo Kreuzer }
139904e0dc4aSTimo Kreuzer // Append locale name pieces together in the form of "Language_country/region.CodePage"
140004e0dc4aSTimo Kreuzer // Note that the preferred form is the BCP-47 tag, but we don't want to change legacy names
__lc_lctowcs(wchar_t * locale,size_t numberOfElements,const __crt_locale_strings * names)140104e0dc4aSTimo Kreuzer void __lc_lctowcs ( wchar_t *locale, size_t numberOfElements, const __crt_locale_strings *names)
140204e0dc4aSTimo Kreuzer {
140304e0dc4aSTimo Kreuzer     _ERRCHECK(wcscpy_s(locale, numberOfElements, names->szLanguage));
140404e0dc4aSTimo Kreuzer     if (*(names->szCountry))
140504e0dc4aSTimo Kreuzer         _wcscats(locale, numberOfElements, 2, L"_", names->szCountry);
140604e0dc4aSTimo Kreuzer     if (*(names->szCodePage))
140704e0dc4aSTimo Kreuzer         _wcscats(locale, numberOfElements, 2, L".", names->szCodePage);
140804e0dc4aSTimo Kreuzer }
140904e0dc4aSTimo Kreuzer 
141004e0dc4aSTimo Kreuzer // Create a copy of a locale name string
__acrt_copy_locale_name(LPCWSTR localeName)141104e0dc4aSTimo Kreuzer LPWSTR __cdecl __acrt_copy_locale_name(LPCWSTR localeName)
141204e0dc4aSTimo Kreuzer {
141304e0dc4aSTimo Kreuzer     size_t cch;
141404e0dc4aSTimo Kreuzer     wchar_t* localeNameCopy;
141504e0dc4aSTimo Kreuzer 
141604e0dc4aSTimo Kreuzer     if (!localeName) // Input cannot be nullptr
141704e0dc4aSTimo Kreuzer         return nullptr;
141804e0dc4aSTimo Kreuzer 
141904e0dc4aSTimo Kreuzer     cch = wcsnlen(localeName, LOCALE_NAME_MAX_LENGTH);
142004e0dc4aSTimo Kreuzer     if (cch >= LOCALE_NAME_MAX_LENGTH) // locale name length must be <= LOCALE_NAME_MAX_LENGTH
142104e0dc4aSTimo Kreuzer         return nullptr;
142204e0dc4aSTimo Kreuzer 
142304e0dc4aSTimo Kreuzer     if ((localeNameCopy = (wchar_t *) _malloc_crt((cch + 1) * sizeof(wchar_t))) == nullptr)
142404e0dc4aSTimo Kreuzer         return nullptr;
142504e0dc4aSTimo Kreuzer 
142604e0dc4aSTimo Kreuzer     _ERRCHECK(wcsncpy_s(localeNameCopy, cch+1, localeName, cch+1));
142704e0dc4aSTimo Kreuzer     return localeNameCopy;
142804e0dc4aSTimo Kreuzer }
142904e0dc4aSTimo Kreuzer 
143004e0dc4aSTimo Kreuzer #endif /* _UCRT_ENCLAVE_BUILD */
143104e0dc4aSTimo Kreuzer 
143204e0dc4aSTimo Kreuzer } // extern "C"
1433