mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 10:41:12 +00:00
2186 lines
71 KiB
C
2186 lines
71 KiB
C
/*
|
|
* msvcrt.dll locale functions
|
|
*
|
|
* Copyright 2000 Jon Griffiths
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <locale.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <mbctype.h>
|
|
#include <wctype.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winuser.h"
|
|
#include "winnls.h"
|
|
|
|
#include "msvcrt.h"
|
|
#include "mtdll.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
|
|
|
|
#define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
|
|
#define MAX_LOCALE_LENGTH 256
|
|
_locale_t MSVCRT_locale = NULL;
|
|
unsigned short *MSVCRT__pctype = NULL;
|
|
unsigned int MSVCRT___lc_codepage = 0;
|
|
int MSVCRT___lc_collate_cp = 0;
|
|
LCID MSVCRT___lc_handle[LC_MAX - LC_MIN + 1] = { 0 };
|
|
int MSVCRT___mb_cur_max = 1;
|
|
BOOL initial_locale = TRUE;
|
|
|
|
#define MSVCRT_LEADBYTE 0x8000
|
|
#define MSVCRT_C1_DEFINED 0x200
|
|
|
|
#if _MSVCR_VER >= 110
|
|
#define LCID_CONVERSION_FLAGS LOCALE_ALLOW_NEUTRAL_NAMES
|
|
#else
|
|
#define LCID_CONVERSION_FLAGS 0
|
|
#endif
|
|
|
|
__lc_time_data cloc_time_data =
|
|
{
|
|
{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
|
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
|
"January", "February", "March", "April", "May", "June", "July",
|
|
"August", "September", "October", "November", "December",
|
|
"AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"}},
|
|
#if _MSVCR_VER < 110
|
|
MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
|
|
#endif
|
|
1, -1,
|
|
#if _MSVCR_VER == 0 || _MSVCR_VER >= 100
|
|
{{L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat",
|
|
L"Sunday", L"Monday", L"Tuesday", L"Wednesday", L"Thursday", L"Friday", L"Saturday",
|
|
L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec",
|
|
L"January", L"February", L"March", L"April", L"May", L"June", L"July",
|
|
L"August", L"September", L"October", L"November", L"December",
|
|
L"AM", L"PM", L"MM/dd/yy", L"dddd, MMMM dd, yyyy", L"HH:mm:ss"}},
|
|
#endif
|
|
#if _MSVCR_VER >= 110
|
|
L"en-US",
|
|
#endif
|
|
};
|
|
|
|
static const unsigned char cloc_clmap[256] =
|
|
{
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
|
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
|
0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
|
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
|
|
0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
|
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
|
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
|
|
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
|
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
|
|
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
|
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
|
|
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
|
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
|
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
|
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
|
};
|
|
|
|
static const unsigned char cloc_cumap[256] =
|
|
{
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
|
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
|
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
|
0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
|
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
|
0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
|
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
|
|
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
|
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
|
|
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
|
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
|
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
|
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
|
};
|
|
|
|
static char empty[] = "";
|
|
static char cloc_dec_point[] = ".";
|
|
#if _MSVCR_VER >= 100
|
|
static wchar_t emptyW[] = L"";
|
|
static wchar_t cloc_dec_pointW[] = L".";
|
|
#endif
|
|
static struct lconv cloc_lconv =
|
|
{
|
|
cloc_dec_point, empty, empty, empty, empty, empty, empty, empty, empty, empty,
|
|
CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX,
|
|
#if _MSVCR_VER >= 100
|
|
cloc_dec_pointW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW
|
|
#endif
|
|
};
|
|
|
|
/* Friendly country strings & language names abbreviations. */
|
|
static const char * const _country_synonyms[] =
|
|
{
|
|
"american", "en",
|
|
"american english", "en-US",
|
|
"american-english", "en-US",
|
|
"english-american", "en-US",
|
|
"english-us", "en-US",
|
|
"english-usa", "en-US",
|
|
"us", "en-US",
|
|
"usa", "en-US",
|
|
"australian", "en-AU",
|
|
"english-aus", "en-AU",
|
|
"belgian", "nl-BE",
|
|
"french-belgian", "fr-BE",
|
|
"canadian", "en-CA",
|
|
"english-can", "en-CA",
|
|
"french-canadian", "fr-CA",
|
|
#if _MSVCR_VER >= 110
|
|
"chinese", "zh",
|
|
"chinese-simplified", "zh",
|
|
"chinese-traditional", "zh-HK",
|
|
"chs", "zh",
|
|
"cht", "zh-HK",
|
|
#else
|
|
"chinese", "zh-CN",
|
|
"chinese-simplified", "zh-CN",
|
|
"chinese-traditional", "zh-TW",
|
|
"chs", "zh-CN",
|
|
"cht", "zh-TW",
|
|
#endif
|
|
"dutch-belgian", "nl-BE",
|
|
"english-nz", "en-NZ",
|
|
"uk", "en-GB",
|
|
"english-uk", "en-GB",
|
|
"french-swiss", "fr-CH",
|
|
"swiss", "de-CH",
|
|
"german-swiss", "de-CH",
|
|
"italian-swiss", "it-CH",
|
|
"german-austrian", "de-AT",
|
|
"portuguese", "pt-BR",
|
|
"portuguese-brazil", "pt-BR",
|
|
"spanish-mexican", "es-MX",
|
|
"norwegian-bokmal", "nb",
|
|
"norwegian-nynorsk", "nn-NO",
|
|
"spanish-modern", "es-ES"
|
|
};
|
|
|
|
|
|
/* INTERNAL: Map a synonym to an ISO code */
|
|
static BOOL remap_synonym(char *name)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < ARRAY_SIZE(_country_synonyms); i += 2)
|
|
{
|
|
if (!_stricmp(_country_synonyms[i],name))
|
|
{
|
|
TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
|
|
strcpy(name, _country_synonyms[i+1]);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Note: Flags are weighted in order of matching importance */
|
|
#define FOUND_SNAME 0x4
|
|
#define FOUND_LANGUAGE 0x2
|
|
#define FOUND_COUNTRY 0x1
|
|
|
|
typedef struct {
|
|
WCHAR search_language[MAX_ELEM_LEN];
|
|
WCHAR search_country[MAX_ELEM_LEN];
|
|
WCHAR found_lang_sname[LOCALE_NAME_MAX_LENGTH];
|
|
unsigned int match_flags;
|
|
BOOL allow_sname;
|
|
} locale_search_t;
|
|
|
|
#define CONTINUE_LOOKING TRUE
|
|
#define STOP_LOOKING FALSE
|
|
|
|
/* INTERNAL: Get and compare locale info with a given string */
|
|
static int compare_info(WCHAR *name, DWORD flags, WCHAR *buff, const WCHAR *cmp, BOOL exact)
|
|
{
|
|
int len;
|
|
|
|
if(!cmp[0])
|
|
return 0;
|
|
|
|
buff[0] = 0;
|
|
GetLocaleInfoEx(name, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN);
|
|
if (!buff[0])
|
|
return 0;
|
|
|
|
/* Partial matches are only allowed on language/country names */
|
|
len = wcslen(cmp);
|
|
|
|
if(exact || len<=3)
|
|
return !_wcsicmp(cmp, buff);
|
|
else
|
|
return !_wcsnicmp(cmp, buff, len);
|
|
}
|
|
|
|
static BOOL CALLBACK
|
|
find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam )
|
|
{
|
|
locale_search_t *res = (locale_search_t *)lParam;
|
|
WCHAR buff[MAX_ELEM_LEN];
|
|
unsigned int flags = 0;
|
|
|
|
if (res->allow_sname && compare_info(name,LOCALE_SNAME,buff,res->search_language, TRUE))
|
|
{
|
|
TRACE(":Found locale: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff));
|
|
res->match_flags = FOUND_SNAME;
|
|
wcscpy(res->found_lang_sname, name);
|
|
return STOP_LOOKING;
|
|
}
|
|
|
|
/* Check Language */
|
|
if (compare_info(name,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) ||
|
|
compare_info(name,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) ||
|
|
compare_info(name,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE))
|
|
{
|
|
TRACE(":Found language: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff));
|
|
flags |= FOUND_LANGUAGE;
|
|
}
|
|
else if (res->match_flags & FOUND_LANGUAGE)
|
|
{
|
|
return CONTINUE_LOOKING;
|
|
}
|
|
|
|
/* Check Country */
|
|
if (compare_info(name,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) ||
|
|
compare_info(name,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) ||
|
|
compare_info(name,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE))
|
|
{
|
|
TRACE("Found country:%s->%s\n", wine_dbgstr_w(res->search_country), wine_dbgstr_w(buff));
|
|
flags |= FOUND_COUNTRY;
|
|
}
|
|
else if (!flags && (res->match_flags & FOUND_COUNTRY))
|
|
{
|
|
return CONTINUE_LOOKING;
|
|
}
|
|
|
|
if (flags > res->match_flags)
|
|
{
|
|
/* Found a better match than previously */
|
|
res->match_flags = flags;
|
|
wcscpy(res->found_lang_sname, name);
|
|
}
|
|
if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY)) ==
|
|
(FOUND_LANGUAGE | FOUND_COUNTRY))
|
|
{
|
|
TRACE(":found exact locale match\n");
|
|
return STOP_LOOKING;
|
|
}
|
|
return CONTINUE_LOOKING;
|
|
}
|
|
|
|
/* Internal: Find the sname for a locale specification.
|
|
* sname must be at least LOCALE_NAME_MAX_LENGTH characters long
|
|
*/
|
|
BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_match, WCHAR *sname)
|
|
{
|
|
thread_data_t *data = msvcrt_get_thread_data();
|
|
const char *cp, *region;
|
|
BOOL is_sname = FALSE;
|
|
DWORD locale_cp;
|
|
|
|
if (!strcmp(locale, data->cached_locale)) {
|
|
if (codepage)
|
|
*codepage = data->cached_cp;
|
|
if (sname_match)
|
|
*sname_match = data->cached_sname_match;
|
|
wcscpy(sname, data->cached_sname);
|
|
return TRUE;
|
|
}
|
|
|
|
cp = strchr(locale, '.');
|
|
region = strchr(locale, '_');
|
|
|
|
if(!locale[0] || (cp == locale && !region)) {
|
|
GetUserDefaultLocaleName(sname, LOCALE_NAME_MAX_LENGTH);
|
|
} else {
|
|
char search_language_buf[MAX_ELEM_LEN] = { 0 }, search_country_buf[MAX_ELEM_LEN] = { 0 };
|
|
locale_search_t search;
|
|
BOOL remapped = FALSE;
|
|
|
|
memset(&search, 0, sizeof(locale_search_t));
|
|
lstrcpynA(search_language_buf, locale, MAX_ELEM_LEN);
|
|
if(region) {
|
|
lstrcpynA(search_country_buf, region+1, MAX_ELEM_LEN);
|
|
if(region-locale < MAX_ELEM_LEN)
|
|
search_language_buf[region-locale] = '\0';
|
|
} else
|
|
search_country_buf[0] = '\0';
|
|
|
|
if(cp) {
|
|
if(region && cp-region-1<MAX_ELEM_LEN)
|
|
search_country_buf[cp-region-1] = '\0';
|
|
if(cp-locale < MAX_ELEM_LEN)
|
|
search_language_buf[cp-locale] = '\0';
|
|
}
|
|
|
|
if ((remapped = remap_synonym(search_language_buf)))
|
|
{
|
|
search.allow_sname = TRUE;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
if(!cp && !region)
|
|
{
|
|
search.allow_sname = TRUE;
|
|
}
|
|
#endif
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, search_language_buf, -1, search.search_language, MAX_ELEM_LEN);
|
|
if (search.allow_sname && IsValidLocaleName(search.search_language))
|
|
{
|
|
search.match_flags = FOUND_SNAME;
|
|
wcscpy(sname, search.search_language);
|
|
}
|
|
else
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, search_country_buf, -1, search.search_country, MAX_ELEM_LEN);
|
|
EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL);
|
|
|
|
if (!search.match_flags)
|
|
return FALSE;
|
|
|
|
/* If we were given something that didn't match, fail */
|
|
if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE)))
|
|
return FALSE;
|
|
if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
|
|
return FALSE;
|
|
|
|
wcscpy(sname, search.found_lang_sname);
|
|
}
|
|
|
|
is_sname = !remapped && (search.match_flags & FOUND_SNAME) != 0;
|
|
}
|
|
|
|
/* Obtain code page */
|
|
if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) {
|
|
GetLocaleInfoEx(sname, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
|
|
(WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
|
|
if (!locale_cp)
|
|
locale_cp = GetACP();
|
|
} else if (!_strnicmp(cp, ".OCP", 4)) {
|
|
GetLocaleInfoEx(sname, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
|
|
(WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
|
|
#if _MSVCR_VER >= 140
|
|
} else if (!_strnicmp(cp, ".UTF-8", 6)
|
|
|| !_strnicmp(cp, ".UTF8", 5)) {
|
|
locale_cp = CP_UTF8;
|
|
#endif
|
|
} else {
|
|
locale_cp = atoi(cp + 1);
|
|
}
|
|
if (!IsValidCodePage(locale_cp))
|
|
return FALSE;
|
|
|
|
if (!locale_cp)
|
|
return FALSE;
|
|
|
|
if (codepage)
|
|
*codepage = locale_cp;
|
|
if (sname_match)
|
|
*sname_match = is_sname;
|
|
|
|
if (strlen(locale) < sizeof(data->cached_locale)) {
|
|
strcpy(data->cached_locale, locale);
|
|
data->cached_cp = locale_cp;
|
|
data->cached_sname_match = is_sname;
|
|
wcscpy(data->cached_sname, sname);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void copy_threadlocinfo_category(pthreadlocinfo locinfo,
|
|
const threadlocinfo *old_locinfo, int category)
|
|
{
|
|
locinfo->lc_handle[category] = old_locinfo->lc_handle[category];
|
|
locinfo->lc_id[category] = old_locinfo->lc_id[category];
|
|
if(!locinfo->lc_category[category].locale) {
|
|
locinfo->lc_category[category].locale = old_locinfo->lc_category[category].locale;
|
|
locinfo->lc_category[category].refcount = old_locinfo->lc_category[category].refcount;
|
|
InterlockedIncrement((LONG *)locinfo->lc_category[category].refcount);
|
|
}
|
|
#if _MSVCR_VER >= 110
|
|
locinfo->lc_name[category] = old_locinfo->lc_name[category];
|
|
locinfo->lc_category[category].wrefcount = old_locinfo->lc_category[category].wrefcount;
|
|
if(locinfo->lc_category[category].wrefcount)
|
|
InterlockedIncrement((LONG *)locinfo->lc_category[category].wrefcount);
|
|
#endif
|
|
}
|
|
|
|
static BOOL init_category_name(const char *name, int len,
|
|
pthreadlocinfo locinfo, int category)
|
|
{
|
|
locinfo->lc_category[category].locale = malloc(len+1);
|
|
locinfo->lc_category[category].refcount = malloc(sizeof(int));
|
|
if(!locinfo->lc_category[category].locale
|
|
|| !locinfo->lc_category[category].refcount) {
|
|
free(locinfo->lc_category[category].locale);
|
|
free(locinfo->lc_category[category].refcount);
|
|
locinfo->lc_category[category].locale = NULL;
|
|
locinfo->lc_category[category].refcount = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(locinfo->lc_category[category].locale, name, len);
|
|
locinfo->lc_category[category].locale[len] = 0;
|
|
*locinfo->lc_category[category].refcount = 1;
|
|
return TRUE;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname)
|
|
{
|
|
locinfo->lc_category[cat].wrefcount = malloc(sizeof(int));
|
|
if(!locinfo->lc_category[cat].wrefcount)
|
|
return FALSE;
|
|
*locinfo->lc_category[cat].wrefcount = 1;
|
|
|
|
if(!(locinfo->lc_name[cat] = wcsdup(sname)))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
#else
|
|
static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname)
|
|
{
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
/* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
|
|
static BOOL update_threadlocinfo_category(WCHAR *sname, unsigned short cp,
|
|
pthreadlocinfo locinfo, int category)
|
|
{
|
|
WCHAR wbuf[256], *p;
|
|
|
|
if(GetLocaleInfoEx(sname, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf))) {
|
|
p = wbuf;
|
|
|
|
locinfo->lc_id[category].wLanguage = 0;
|
|
while(*p) {
|
|
locinfo->lc_id[category].wLanguage *= 16;
|
|
|
|
if(*p <= '9')
|
|
locinfo->lc_id[category].wLanguage += *p-'0';
|
|
else
|
|
locinfo->lc_id[category].wLanguage += *p-'a'+10;
|
|
|
|
p++;
|
|
}
|
|
|
|
locinfo->lc_id[category].wCountry =
|
|
locinfo->lc_id[category].wLanguage;
|
|
}
|
|
|
|
locinfo->lc_id[category].wCodePage = cp;
|
|
|
|
locinfo->lc_handle[category] = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS);
|
|
|
|
set_lc_locale_name(locinfo, category, sname);
|
|
|
|
if(!locinfo->lc_category[category].locale) {
|
|
char buf[256];
|
|
int len = 0;
|
|
|
|
#if _MSVCR_VER < 110
|
|
if (LANGIDFROMLCID(locinfo->lc_handle[category]) == MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK))
|
|
{
|
|
/* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */
|
|
wcscpy( wbuf, L"Norwegian-Nynorsk" );
|
|
len = wcslen( wbuf ) + 1;
|
|
}
|
|
else
|
|
#endif
|
|
len += GetLocaleInfoEx(sname, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf));
|
|
wbuf[len-1] = '_';
|
|
len += GetLocaleInfoEx(sname, LOCALE_SENGCOUNTRY
|
|
|LOCALE_NOUSEROVERRIDE, &wbuf[len], ARRAY_SIZE(wbuf) - len);
|
|
wbuf[len-1] = '.';
|
|
swprintf(wbuf+len, ARRAY_SIZE(wbuf) - len,L"%d", cp);
|
|
len += wcslen(wbuf+len);
|
|
|
|
WideCharToMultiByte(cp, 0, wbuf, -1, buf, ARRAY_SIZE(buf), NULL, NULL);
|
|
|
|
return init_category_name(buf, len, locinfo, category);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _lock_locales (UCRTBASE.@)
|
|
*/
|
|
void CDECL _lock_locales(void)
|
|
{
|
|
_lock(_SETLOCALE_LOCK);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _unlock_locales (UCRTBASE.@)
|
|
*/
|
|
void CDECL _unlock_locales(void)
|
|
{
|
|
_unlock(_SETLOCALE_LOCK);
|
|
}
|
|
|
|
static void grab_locinfo(pthreadlocinfo locinfo)
|
|
{
|
|
int i;
|
|
|
|
InterlockedIncrement((LONG *)&locinfo->refcount);
|
|
for(i=LC_MIN+1; i<=LC_MAX; i++)
|
|
{
|
|
InterlockedIncrement((LONG *)locinfo->lc_category[i].refcount);
|
|
if(locinfo->lc_category[i].wrefcount)
|
|
InterlockedIncrement((LONG *)locinfo->lc_category[i].wrefcount);
|
|
}
|
|
if(locinfo->lconv_intl_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
|
|
if(locinfo->lconv_num_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
|
|
if(locinfo->lconv_mon_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
|
|
if(locinfo->ctype1_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
|
|
InterlockedIncrement(&locinfo->lc_time_curr->refcount);
|
|
}
|
|
|
|
static void update_thread_locale(thread_data_t *data)
|
|
{
|
|
if((data->locale_flags & LOCALE_FREE) && ((data->locale_flags & LOCALE_THREAD) ||
|
|
(data->locinfo == MSVCRT_locale->locinfo && data->mbcinfo == MSVCRT_locale->mbcinfo)))
|
|
return;
|
|
|
|
if(data->locale_flags & LOCALE_FREE)
|
|
{
|
|
free_locinfo(data->locinfo);
|
|
free_mbcinfo(data->mbcinfo);
|
|
}
|
|
|
|
_lock_locales();
|
|
data->locinfo = MSVCRT_locale->locinfo;
|
|
grab_locinfo(data->locinfo);
|
|
_unlock_locales();
|
|
|
|
_lock(_MB_CP_LOCK);
|
|
data->mbcinfo = MSVCRT_locale->mbcinfo;
|
|
InterlockedIncrement(&data->mbcinfo->refcount);
|
|
_unlock(_MB_CP_LOCK);
|
|
|
|
data->locale_flags |= LOCALE_FREE;
|
|
}
|
|
|
|
/* INTERNAL: returns threadlocinfo struct */
|
|
pthreadlocinfo CDECL get_locinfo(void) {
|
|
thread_data_t *data = msvcrt_get_thread_data();
|
|
update_thread_locale(data);
|
|
return data->locinfo;
|
|
}
|
|
|
|
/* INTERNAL: returns pthreadmbcinfo struct */
|
|
pthreadmbcinfo CDECL get_mbcinfo(void) {
|
|
thread_data_t *data = msvcrt_get_thread_data();
|
|
update_thread_locale(data);
|
|
return data->mbcinfo;
|
|
}
|
|
|
|
/* INTERNAL: constructs string returned by setlocale */
|
|
static inline char* construct_lc_all(pthreadlocinfo locinfo) {
|
|
static char current_lc_all[MAX_LOCALE_LENGTH];
|
|
|
|
int i;
|
|
|
|
for(i=LC_MIN+1; i<LC_MAX; i++) {
|
|
if(strcmp(locinfo->lc_category[i].locale,
|
|
locinfo->lc_category[i+1].locale))
|
|
break;
|
|
}
|
|
|
|
if(i==LC_MAX)
|
|
return locinfo->lc_category[LC_COLLATE].locale;
|
|
|
|
sprintf(current_lc_all,
|
|
"LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
|
|
locinfo->lc_category[LC_COLLATE].locale,
|
|
locinfo->lc_category[LC_CTYPE].locale,
|
|
locinfo->lc_category[LC_MONETARY].locale,
|
|
locinfo->lc_category[LC_NUMERIC].locale,
|
|
locinfo->lc_category[LC_TIME].locale);
|
|
|
|
return current_lc_all;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
* _Getdays (MSVCRT.@)
|
|
*/
|
|
char* CDECL _Getdays(void)
|
|
{
|
|
__lc_time_data *cur = get_locinfo()->lc_time_curr;
|
|
int i, len, size = 0;
|
|
char *out;
|
|
|
|
TRACE("\n");
|
|
|
|
for(i=0; i<7; i++) {
|
|
size += strlen(cur->str.names.short_wday[i]) + 1;
|
|
size += strlen(cur->str.names.wday[i]) + 1;
|
|
}
|
|
out = malloc(size+1);
|
|
if(!out)
|
|
return NULL;
|
|
|
|
size = 0;
|
|
for(i=0; i<7; i++) {
|
|
out[size++] = ':';
|
|
len = strlen(cur->str.names.short_wday[i]);
|
|
memcpy(&out[size], cur->str.names.short_wday[i], len);
|
|
size += len;
|
|
|
|
out[size++] = ':';
|
|
len = strlen(cur->str.names.wday[i]);
|
|
memcpy(&out[size], cur->str.names.wday[i], len);
|
|
size += len;
|
|
}
|
|
out[size] = '\0';
|
|
|
|
return out;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
/*********************************************************************
|
|
* _W_Getdays (MSVCR110.@)
|
|
*/
|
|
wchar_t* CDECL _W_Getdays(void)
|
|
{
|
|
__lc_time_data *cur = get_locinfo()->lc_time_curr;
|
|
wchar_t *out;
|
|
int i, len, size = 0;
|
|
|
|
TRACE("\n");
|
|
|
|
for(i=0; i<7; i++) {
|
|
size += wcslen(cur->wstr.names.short_wday[i]) + 1;
|
|
size += wcslen(cur->wstr.names.wday[i]) + 1;
|
|
}
|
|
out = malloc((size+1)*sizeof(*out));
|
|
if(!out)
|
|
return NULL;
|
|
|
|
size = 0;
|
|
for(i=0; i<7; i++) {
|
|
out[size++] = ':';
|
|
len = wcslen(cur->wstr.names.short_wday[i]);
|
|
memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
|
|
size += len;
|
|
|
|
out[size++] = ':';
|
|
len = wcslen(cur->wstr.names.wday[i]);
|
|
memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
|
|
size += len;
|
|
}
|
|
out[size] = '\0';
|
|
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* _Getmonths (MSVCRT.@)
|
|
*/
|
|
char* CDECL _Getmonths(void)
|
|
{
|
|
__lc_time_data *cur = get_locinfo()->lc_time_curr;
|
|
int i, len, size = 0;
|
|
char *out;
|
|
|
|
TRACE("\n");
|
|
|
|
for(i=0; i<12; i++) {
|
|
size += strlen(cur->str.names.short_mon[i]) + 1;
|
|
size += strlen(cur->str.names.mon[i]) + 1;
|
|
}
|
|
out = malloc(size+1);
|
|
if(!out)
|
|
return NULL;
|
|
|
|
size = 0;
|
|
for(i=0; i<12; i++) {
|
|
out[size++] = ':';
|
|
len = strlen(cur->str.names.short_mon[i]);
|
|
memcpy(&out[size], cur->str.names.short_mon[i], len);
|
|
size += len;
|
|
|
|
out[size++] = ':';
|
|
len = strlen(cur->str.names.mon[i]);
|
|
memcpy(&out[size], cur->str.names.mon[i], len);
|
|
size += len;
|
|
}
|
|
out[size] = '\0';
|
|
|
|
return out;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
/*********************************************************************
|
|
* _W_Getmonths (MSVCR110.@)
|
|
*/
|
|
wchar_t* CDECL _W_Getmonths(void)
|
|
{
|
|
__lc_time_data *cur = get_locinfo()->lc_time_curr;
|
|
wchar_t *out;
|
|
int i, len, size = 0;
|
|
|
|
TRACE("\n");
|
|
|
|
for(i=0; i<12; i++) {
|
|
size += wcslen(cur->wstr.names.short_mon[i]) + 1;
|
|
size += wcslen(cur->wstr.names.mon[i]) + 1;
|
|
}
|
|
out = malloc((size+1)*sizeof(*out));
|
|
if(!out)
|
|
return NULL;
|
|
|
|
size = 0;
|
|
for(i=0; i<12; i++) {
|
|
out[size++] = ':';
|
|
len = wcslen(cur->wstr.names.short_mon[i]);
|
|
memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
|
|
size += len;
|
|
|
|
out[size++] = ':';
|
|
len = wcslen(cur->wstr.names.mon[i]);
|
|
memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
|
|
size += len;
|
|
}
|
|
out[size] = '\0';
|
|
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* _Gettnames (MSVCRT.@)
|
|
*/
|
|
void* CDECL _Gettnames(void)
|
|
{
|
|
__lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
|
|
unsigned int i, len, size = sizeof(__lc_time_data);
|
|
|
|
TRACE("\n");
|
|
|
|
for(i=0; i<ARRAY_SIZE(cur->str.str); i++)
|
|
size += strlen(cur->str.str[i])+1;
|
|
#if _MSVCR_VER >= 110
|
|
for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++)
|
|
size += (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
|
|
#endif
|
|
|
|
ret = malloc(size);
|
|
if(!ret)
|
|
return NULL;
|
|
memcpy(ret, cur, sizeof(*ret));
|
|
|
|
size = 0;
|
|
for(i=0; i<ARRAY_SIZE(cur->str.str); i++) {
|
|
len = strlen(cur->str.str[i])+1;
|
|
memcpy(&ret->data[size], cur->str.str[i], len);
|
|
ret->str.str[i] = &ret->data[size];
|
|
size += len;
|
|
}
|
|
#if _MSVCR_VER >= 110
|
|
for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++) {
|
|
len = (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
|
|
memcpy(&ret->data[size], cur->wstr.wstr[i], len);
|
|
ret->wstr.wstr[i] = (wchar_t*)&ret->data[size];
|
|
size += len;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
/*********************************************************************
|
|
* _W_Gettnames (MSVCR110.@)
|
|
*/
|
|
void* CDECL _W_Gettnames(void)
|
|
{
|
|
return _Gettnames();
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* __crtLCMapStringA (MSVCRT.@)
|
|
*/
|
|
int CDECL __crtLCMapStringA(
|
|
LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
|
|
int dstlen, unsigned int codepage, int xflag
|
|
) {
|
|
WCHAR buf_in[32], *in = buf_in;
|
|
WCHAR buf_out[32], *out = buf_out;
|
|
int in_len, out_len, r;
|
|
|
|
TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
|
|
lcid, mapflags, src, srclen, dst, dstlen, codepage, xflag);
|
|
|
|
in_len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, NULL, 0);
|
|
if (!in_len) return 0;
|
|
if (in_len > ARRAY_SIZE(buf_in))
|
|
{
|
|
in = malloc(in_len * sizeof(WCHAR));
|
|
if (!in) return 0;
|
|
}
|
|
|
|
r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len);
|
|
if (!r) goto done;
|
|
|
|
if (mapflags & LCMAP_SORTKEY)
|
|
{
|
|
r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen);
|
|
goto done;
|
|
}
|
|
|
|
r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0);
|
|
if (!r) goto done;
|
|
out_len = r;
|
|
if (r > ARRAY_SIZE(buf_out))
|
|
{
|
|
out = malloc(r * sizeof(WCHAR));
|
|
if (!out)
|
|
{
|
|
r = 0;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len);
|
|
if (!r) goto done;
|
|
|
|
r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL);
|
|
|
|
done:
|
|
if (in != buf_in) free(in);
|
|
if (out != buf_out) free(out);
|
|
return r;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* __crtLCMapStringW (MSVCRT.@)
|
|
*/
|
|
int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src,
|
|
int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
|
|
{
|
|
FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
|
|
lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
|
|
|
|
return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* __crtCompareStringA (MSVCRT.@)
|
|
*/
|
|
int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
|
|
const char *src2, int len2 )
|
|
{
|
|
FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
|
|
lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
|
|
/* FIXME: probably not entirely right */
|
|
return CompareStringA( lcid, flags, src1, len1, src2, len2 );
|
|
}
|
|
|
|
/*********************************************************************
|
|
* __crtCompareStringW (MSVCRT.@)
|
|
*/
|
|
int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1,
|
|
const wchar_t *src2, int len2 )
|
|
{
|
|
FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
|
|
lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
|
|
/* FIXME: probably not entirely right */
|
|
return CompareStringW( lcid, flags, src1, len1, src2, len2 );
|
|
}
|
|
|
|
/*********************************************************************
|
|
* __crtGetLocaleInfoW (MSVCRT.@)
|
|
*/
|
|
int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len )
|
|
{
|
|
FIXME("(lcid %lx, type %lx, %p(%d), partial stub\n", lcid, type, buffer, len );
|
|
/* FIXME: probably not entirely right */
|
|
return GetLocaleInfoW( lcid, type, buffer, len );
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
/*********************************************************************
|
|
* __crtGetLocaleInfoEx (MSVC110.@)
|
|
*/
|
|
int CDECL __crtGetLocaleInfoEx( const WCHAR *locale, LCTYPE type, wchar_t *buffer, int len )
|
|
{
|
|
TRACE("(%s, %lx, %p, %d)\n", debugstr_w(locale), type, buffer, len);
|
|
return GetLocaleInfoEx(locale, type, buffer, len);
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* __crtGetStringTypeW(MSVCRT.@)
|
|
*
|
|
* This function was accepting different number of arguments in older
|
|
* versions of msvcrt.
|
|
*/
|
|
BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
|
|
wchar_t *buffer, int len, WORD *out)
|
|
{
|
|
FIXME("(unk %lx, type %lx, wstr %p(%d), %p) partial stub\n",
|
|
unk, type, buffer, len, out);
|
|
|
|
return GetStringTypeW(type, buffer, len, out);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* localeconv (MSVCRT.@)
|
|
*/
|
|
struct lconv* CDECL localeconv(void)
|
|
{
|
|
return get_locinfo()->lconv;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* __lconv_init (MSVCRT.@)
|
|
*/
|
|
int CDECL __lconv_init(void)
|
|
{
|
|
/* this is used to make chars unsigned */
|
|
cloc_lconv.int_frac_digits = (char)UCHAR_MAX;
|
|
cloc_lconv.frac_digits = (char)UCHAR_MAX;
|
|
cloc_lconv.p_cs_precedes = (char)UCHAR_MAX;
|
|
cloc_lconv.p_sep_by_space = (char)UCHAR_MAX;
|
|
cloc_lconv.n_cs_precedes = (char)UCHAR_MAX;
|
|
cloc_lconv.n_sep_by_space = (char)UCHAR_MAX;
|
|
cloc_lconv.p_sign_posn = (char)UCHAR_MAX;
|
|
cloc_lconv.n_sign_posn = (char)UCHAR_MAX;
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* ___lc_handle_func (MSVCRT.@)
|
|
*/
|
|
LCID* CDECL ___lc_handle_func(void)
|
|
{
|
|
return (LCID *)get_locinfo()->lc_handle;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
/*********************************************************************
|
|
* ___lc_locale_name_func (MSVCR110.@)
|
|
*/
|
|
wchar_t** CDECL ___lc_locale_name_func(void)
|
|
{
|
|
return get_locinfo()->lc_name;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* ___lc_codepage_func (MSVCRT.@)
|
|
*/
|
|
unsigned int CDECL ___lc_codepage_func(void)
|
|
{
|
|
return get_locinfo()->lc_codepage;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* ___lc_collate_cp_func (MSVCRT.@)
|
|
*/
|
|
int CDECL ___lc_collate_cp_func(void)
|
|
{
|
|
return get_locinfo()->lc_collate_cp;
|
|
}
|
|
|
|
/* INTERNAL: frees pthreadlocinfo struct */
|
|
void free_locinfo(pthreadlocinfo locinfo)
|
|
{
|
|
int i;
|
|
|
|
if(!locinfo)
|
|
return;
|
|
|
|
for(i=LC_MIN+1; i<=LC_MAX; i++) {
|
|
if(!locinfo->lc_category[i].refcount
|
|
|| !InterlockedDecrement((LONG *)locinfo->lc_category[i].refcount)) {
|
|
free(locinfo->lc_category[i].locale);
|
|
free(locinfo->lc_category[i].refcount);
|
|
}
|
|
if(!locinfo->lc_category[i].wrefcount
|
|
|| !InterlockedDecrement((LONG *)locinfo->lc_category[i].wrefcount)) {
|
|
#if _MSVCR_VER >= 110
|
|
free(locinfo->lc_name[i]);
|
|
#endif
|
|
free(locinfo->lc_category[i].wrefcount);
|
|
}
|
|
}
|
|
|
|
if(locinfo->lconv_num_refcount
|
|
&& !InterlockedDecrement((LONG *)locinfo->lconv_num_refcount)) {
|
|
free(locinfo->lconv->decimal_point);
|
|
free(locinfo->lconv->thousands_sep);
|
|
free(locinfo->lconv->grouping);
|
|
#if _MSVCR_VER >= 100
|
|
free(locinfo->lconv->_W_decimal_point);
|
|
free(locinfo->lconv->_W_thousands_sep);
|
|
#endif
|
|
free(locinfo->lconv_num_refcount);
|
|
}
|
|
if(locinfo->lconv_mon_refcount
|
|
&& !InterlockedDecrement((LONG *)locinfo->lconv_mon_refcount)) {
|
|
free(locinfo->lconv->int_curr_symbol);
|
|
free(locinfo->lconv->currency_symbol);
|
|
free(locinfo->lconv->mon_decimal_point);
|
|
free(locinfo->lconv->mon_thousands_sep);
|
|
free(locinfo->lconv->mon_grouping);
|
|
free(locinfo->lconv->positive_sign);
|
|
free(locinfo->lconv->negative_sign);
|
|
#if _MSVCR_VER >= 100
|
|
free(locinfo->lconv->_W_int_curr_symbol);
|
|
free(locinfo->lconv->_W_currency_symbol);
|
|
free(locinfo->lconv->_W_mon_decimal_point);
|
|
free(locinfo->lconv->_W_mon_thousands_sep);
|
|
free(locinfo->lconv->_W_positive_sign);
|
|
free(locinfo->lconv->_W_negative_sign);
|
|
#endif
|
|
free(locinfo->lconv_mon_refcount);
|
|
}
|
|
if(locinfo->lconv_intl_refcount
|
|
&& !InterlockedDecrement((LONG *)locinfo->lconv_intl_refcount)) {
|
|
free(locinfo->lconv_intl_refcount);
|
|
free(locinfo->lconv);
|
|
}
|
|
|
|
if(locinfo->ctype1_refcount
|
|
&& !InterlockedDecrement((LONG *)locinfo->ctype1_refcount)) {
|
|
free(locinfo->ctype1_refcount);
|
|
free(locinfo->ctype1);
|
|
free((void*)locinfo->pclmap);
|
|
free((void*)locinfo->pcumap);
|
|
}
|
|
|
|
if(locinfo->lc_time_curr && !InterlockedDecrement(&locinfo->lc_time_curr->refcount)
|
|
&& locinfo->lc_time_curr != &cloc_time_data)
|
|
free(locinfo->lc_time_curr);
|
|
|
|
if(InterlockedDecrement((LONG *)&locinfo->refcount))
|
|
return;
|
|
|
|
free(locinfo);
|
|
}
|
|
|
|
/* INTERNAL: frees pthreadmbcinfo struct */
|
|
void free_mbcinfo(pthreadmbcinfo mbcinfo)
|
|
{
|
|
if(!mbcinfo)
|
|
return;
|
|
|
|
if(InterlockedDecrement(&mbcinfo->refcount))
|
|
return;
|
|
|
|
free(mbcinfo);
|
|
}
|
|
|
|
_locale_t CDECL get_current_locale_noalloc(_locale_t locale)
|
|
{
|
|
thread_data_t *data = msvcrt_get_thread_data();
|
|
|
|
update_thread_locale(data);
|
|
locale->locinfo = data->locinfo;
|
|
locale->mbcinfo = data->mbcinfo;
|
|
|
|
grab_locinfo(locale->locinfo);
|
|
InterlockedIncrement(&locale->mbcinfo->refcount);
|
|
return locale;
|
|
}
|
|
|
|
void CDECL free_locale_noalloc(_locale_t locale)
|
|
{
|
|
free_locinfo(locale->locinfo);
|
|
free_mbcinfo(locale->mbcinfo);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _get_current_locale (MSVCRT.@)
|
|
*/
|
|
_locale_t CDECL _get_current_locale(void)
|
|
{
|
|
_locale_t loc = malloc(sizeof(_locale_tstruct));
|
|
if(!loc)
|
|
return NULL;
|
|
|
|
return get_current_locale_noalloc(loc);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _free_locale (MSVCRT.@)
|
|
*/
|
|
void CDECL _free_locale(_locale_t locale)
|
|
{
|
|
if (!locale)
|
|
return;
|
|
|
|
free_locale_noalloc(locale);
|
|
free(locale);
|
|
}
|
|
|
|
static inline BOOL category_needs_update(int cat,
|
|
const threadlocinfo *locinfo, WCHAR *sname, unsigned short cp)
|
|
{
|
|
#if _MSVCR_VER < 110
|
|
LCID lcid;
|
|
#endif
|
|
if(!locinfo) return TRUE;
|
|
#if _MSVCR_VER >= 110
|
|
if(!locinfo->lc_name[cat] || !sname) return TRUE;
|
|
return wcscmp(sname, locinfo->lc_name[cat]) != 0 || cp!=locinfo->lc_id[cat].wCodePage;
|
|
#else
|
|
lcid = sname ? LocaleNameToLCID(sname, 0) : 0;
|
|
return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage;
|
|
#endif
|
|
}
|
|
|
|
static __lc_time_data* create_time_data(WCHAR *sname)
|
|
{
|
|
static const DWORD time_data[] = {
|
|
LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
|
|
LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
|
|
LOCALE_SABBREVDAYNAME6,
|
|
LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
|
|
LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
|
|
LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
|
|
LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
|
|
LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
|
|
LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
|
|
LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
|
|
LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
|
|
LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
|
|
LOCALE_S1159, LOCALE_S2359,
|
|
LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
|
|
LOCALE_STIMEFORMAT
|
|
};
|
|
|
|
__lc_time_data *cur;
|
|
int i, ret, size;
|
|
LCID lcid = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS);
|
|
|
|
size = 0;
|
|
for(i=0; i<ARRAY_SIZE(time_data); i++) {
|
|
ret = GetLocaleInfoA(lcid, time_data[i], NULL, 0);
|
|
if(!ret)
|
|
return NULL;
|
|
size += ret;
|
|
|
|
#if _MSVCR_VER == 0 || _MSVCR_VER >= 100
|
|
ret = GetLocaleInfoEx(sname, time_data[i], NULL, 0);
|
|
if(!ret)
|
|
return NULL;
|
|
size += ret*sizeof(wchar_t);
|
|
#endif
|
|
}
|
|
#if _MSVCR_VER >= 110
|
|
size += (wcslen(sname) + 1) * sizeof(wchar_t);
|
|
#endif
|
|
|
|
cur = malloc(FIELD_OFFSET(__lc_time_data, data[size]));
|
|
if(!cur)
|
|
return NULL;
|
|
|
|
ret = 0;
|
|
for(i=0; i<ARRAY_SIZE(time_data); i++) {
|
|
cur->str.str[i] = &cur->data[ret];
|
|
ret += GetLocaleInfoA(lcid, time_data[i], &cur->data[ret], size-ret);
|
|
}
|
|
#if _MSVCR_VER == 0 || _MSVCR_VER >= 100
|
|
for(i=0; i<ARRAY_SIZE(time_data); i++) {
|
|
cur->wstr.wstr[i] = (wchar_t*)&cur->data[ret];
|
|
ret += GetLocaleInfoEx(sname, time_data[i], (wchar_t*)&cur->data[ret],
|
|
(size - ret) / sizeof(wchar_t)) * sizeof(wchar_t);
|
|
}
|
|
#endif
|
|
#if _MSVCR_VER >= 110
|
|
cur->locname = (wchar_t*)&cur->data[ret];
|
|
wcscpy((wchar_t*)&cur->data[ret], sname);
|
|
#else
|
|
cur->lcid = lcid;
|
|
#endif
|
|
cur->unk = 1;
|
|
cur->refcount = 1;
|
|
|
|
return cur;
|
|
}
|
|
|
|
static pthreadlocinfo create_locinfo(int category,
|
|
const char *locale, const threadlocinfo *old_locinfo)
|
|
{
|
|
static const char collate[] = "COLLATE=";
|
|
static const char ctype[] = "CTYPE=";
|
|
static const char monetary[] = "MONETARY=";
|
|
static const char numeric[] = "NUMERIC=";
|
|
static const char time[] = "TIME=";
|
|
|
|
pthreadlocinfo locinfo = NULL;
|
|
unsigned short cp[6] = { 0 };
|
|
const char *locale_name[6] = { 0 };
|
|
WCHAR *locale_sname[6] = { 0 };
|
|
int val, locale_len[6] = { 0 };
|
|
char buf[256];
|
|
BOOL sname_match;
|
|
wchar_t wbuf[256];
|
|
int i;
|
|
|
|
TRACE("(%d %s)\n", category, locale);
|
|
|
|
if(category<LC_MIN || category>LC_MAX || !locale)
|
|
return NULL;
|
|
|
|
if(locale[0]=='C' && !locale[1]) {
|
|
locale_sname[0] = NULL;
|
|
cp[0] = CP_ACP;
|
|
} else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
|
|
const char *p;
|
|
|
|
while(1) {
|
|
locale += 3; /* LC_ */
|
|
if(!memcmp(locale, collate, sizeof(collate)-1)) {
|
|
i = LC_COLLATE;
|
|
locale += sizeof(collate)-1;
|
|
} else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
|
|
i = LC_CTYPE;
|
|
locale += sizeof(ctype)-1;
|
|
} else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
|
|
i = LC_MONETARY;
|
|
locale += sizeof(monetary)-1;
|
|
} else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
|
|
i = LC_NUMERIC;
|
|
locale += sizeof(numeric)-1;
|
|
} else if(!memcmp(locale, time, sizeof(time)-1)) {
|
|
i = LC_TIME;
|
|
locale += sizeof(time)-1;
|
|
} else
|
|
goto fail;
|
|
|
|
p = strchr(locale, ';');
|
|
if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
|
|
locale_sname[i] = NULL;
|
|
cp[i] = CP_ACP;
|
|
} else {
|
|
BOOL locale_found = FALSE;
|
|
|
|
if(p) {
|
|
memcpy(buf, locale, p-locale);
|
|
buf[p-locale] = '\0';
|
|
locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf);
|
|
} else {
|
|
locale_found = locale_to_sname(locale, &cp[i], &sname_match, wbuf);
|
|
}
|
|
|
|
if(!locale_found || !(locale_sname[i] = wcsdup(wbuf)))
|
|
goto fail;
|
|
if(sname_match) {
|
|
locale_name[i] = locale;
|
|
locale_len[i] = p ? p-locale : strlen(locale);
|
|
}
|
|
}
|
|
|
|
if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
|
|
break;
|
|
|
|
locale = p+1;
|
|
}
|
|
} else {
|
|
BOOL locale_found = locale_to_sname(locale, &cp[0], &sname_match, wbuf);
|
|
|
|
if(!locale_found)
|
|
return NULL;
|
|
|
|
locale_sname[0] = wcsdup(wbuf);
|
|
if(!locale_sname[0])
|
|
return NULL;
|
|
|
|
if(sname_match) {
|
|
locale_name[0] = locale;
|
|
locale_len[0] = strlen(locale);
|
|
}
|
|
|
|
for(i=1; i<6; i++) {
|
|
locale_sname[i] = wcsdup(locale_sname[0]);
|
|
if(!locale_sname[i])
|
|
goto fail;
|
|
|
|
cp[i] = cp[0];
|
|
locale_name[i] = locale_name[0];
|
|
locale_len[i] = locale_len[0];
|
|
}
|
|
}
|
|
|
|
for(i=1; i<6; i++) {
|
|
#if _MSVCR_VER < 140
|
|
if(i==LC_CTYPE && cp[i]==CP_UTF8) {
|
|
#if _MSVCR_VER >= 110
|
|
if(old_locinfo) {
|
|
locale_sname[i] = wcsdup(old_locinfo->lc_name[i]);
|
|
if (old_locinfo->lc_name[i] && !locale_sname[i])
|
|
goto fail;
|
|
}
|
|
#else
|
|
int sname_size;
|
|
if(old_locinfo && old_locinfo->lc_handle[i]) {
|
|
sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0);
|
|
locale_sname[i] = malloc(sname_size * sizeof(WCHAR));
|
|
if(!locale_sname[i])
|
|
goto fail;
|
|
LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0);
|
|
} else {
|
|
locale_sname[i] = NULL;
|
|
}
|
|
#endif
|
|
|
|
locale_name[i] = NULL;
|
|
locale_len[i] = 0;
|
|
cp[i] = old_locinfo ? old_locinfo->lc_id[i].wCodePage : 0;
|
|
}
|
|
#endif
|
|
if(category!=LC_ALL && category!=i) {
|
|
if(old_locinfo) {
|
|
#if _MSVCR_VER >= 110
|
|
locale_sname[i] = wcsdup(old_locinfo->lc_name[i]);
|
|
if(old_locinfo->lc_name[i] && !locale_sname[i])
|
|
goto fail;
|
|
#else
|
|
int sname_size;
|
|
if(old_locinfo->lc_handle[i]) {
|
|
sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0);
|
|
locale_sname[i] = malloc(sname_size * sizeof(WCHAR));
|
|
if(!locale_sname[i])
|
|
goto fail;
|
|
LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0);
|
|
} else {
|
|
locale_sname[i] = NULL;
|
|
}
|
|
#endif
|
|
cp[i] = old_locinfo->lc_id[i].wCodePage;
|
|
} else {
|
|
locale_sname[i] = NULL;
|
|
cp[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
locinfo = malloc(sizeof(threadlocinfo));
|
|
if(!locinfo)
|
|
goto fail;
|
|
|
|
memset(locinfo, 0, sizeof(threadlocinfo));
|
|
locinfo->refcount = 1;
|
|
|
|
if(locale_name[LC_COLLATE] &&
|
|
!init_category_name(locale_name[LC_COLLATE],
|
|
locale_len[LC_COLLATE], locinfo, LC_COLLATE)) {
|
|
goto fail;
|
|
}
|
|
|
|
if(!category_needs_update(LC_COLLATE, old_locinfo,
|
|
locale_sname[LC_COLLATE], cp[LC_COLLATE])) {
|
|
copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE);
|
|
locinfo->lc_collate_cp = old_locinfo->lc_collate_cp;
|
|
} else if(locale_sname[LC_COLLATE]) {
|
|
if(!update_threadlocinfo_category(locale_sname[LC_COLLATE],
|
|
cp[LC_COLLATE], locinfo, LC_COLLATE)) {
|
|
goto fail;
|
|
}
|
|
|
|
locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage;
|
|
} else {
|
|
if(!init_category_name("C", 1, locinfo, LC_COLLATE)) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if(locale_name[LC_CTYPE] &&
|
|
!init_category_name(locale_name[LC_CTYPE],
|
|
locale_len[LC_CTYPE], locinfo, LC_CTYPE)) {
|
|
goto fail;
|
|
}
|
|
|
|
if(!category_needs_update(LC_CTYPE, old_locinfo,
|
|
locale_sname[LC_CTYPE], cp[LC_CTYPE])) {
|
|
copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE);
|
|
locinfo->lc_codepage = old_locinfo->lc_codepage;
|
|
locinfo->lc_clike = old_locinfo->lc_clike;
|
|
locinfo->mb_cur_max = old_locinfo->mb_cur_max;
|
|
locinfo->ctype1 = old_locinfo->ctype1;
|
|
locinfo->ctype1_refcount = old_locinfo->ctype1_refcount;
|
|
locinfo->pctype = old_locinfo->pctype;
|
|
locinfo->pclmap = old_locinfo->pclmap;
|
|
locinfo->pcumap = old_locinfo->pcumap;
|
|
if(locinfo->ctype1_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
|
|
} else if(locale_sname[LC_CTYPE]) {
|
|
CPINFO cp_info;
|
|
int j;
|
|
|
|
if(!update_threadlocinfo_category(locale_sname[LC_CTYPE],
|
|
cp[LC_CTYPE], locinfo, LC_CTYPE)) {
|
|
goto fail;
|
|
}
|
|
|
|
locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage;
|
|
locinfo->lc_clike = 1;
|
|
if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) {
|
|
goto fail;
|
|
}
|
|
locinfo->mb_cur_max = cp_info.MaxCharSize;
|
|
|
|
locinfo->ctype1_refcount = malloc(sizeof(int));
|
|
if(!locinfo->ctype1_refcount) {
|
|
goto fail;
|
|
}
|
|
*locinfo->ctype1_refcount = 1;
|
|
|
|
locinfo->ctype1 = malloc(sizeof(short[257]));
|
|
locinfo->pclmap = malloc(sizeof(char[256]));
|
|
locinfo->pcumap = malloc(sizeof(char[256]));
|
|
if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) {
|
|
goto fail;
|
|
}
|
|
|
|
locinfo->ctype1[0] = 0;
|
|
locinfo->pctype = locinfo->ctype1+1;
|
|
|
|
buf[1] = buf[2] = '\0';
|
|
for(i=1; i<257; i++) {
|
|
buf[0] = i-1;
|
|
|
|
/* builtin GetStringTypeA doesn't set output to 0 on invalid input */
|
|
locinfo->ctype1[i] = 0;
|
|
|
|
GetStringTypeA(locinfo->lc_handle[LC_CTYPE], CT_CTYPE1, buf,
|
|
1, locinfo->ctype1+i);
|
|
}
|
|
|
|
for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
|
|
for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
|
|
locinfo->ctype1[j+1] |= _LEADBYTE;
|
|
|
|
for(i=0; i<256; i++) {
|
|
if(locinfo->pctype[i] & _LEADBYTE)
|
|
buf[i] = ' ';
|
|
else
|
|
buf[i] = i;
|
|
}
|
|
|
|
LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
|
|
(char*)locinfo->pclmap, 256);
|
|
LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
|
|
(char*)locinfo->pcumap, 256);
|
|
} else {
|
|
locinfo->lc_clike = 1;
|
|
locinfo->mb_cur_max = 1;
|
|
locinfo->pctype = MSVCRT__ctype+1;
|
|
locinfo->pclmap = cloc_clmap;
|
|
locinfo->pcumap = cloc_cumap;
|
|
if(!init_category_name("C", 1, locinfo, LC_CTYPE)) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if(!category_needs_update(LC_MONETARY, old_locinfo,
|
|
locale_sname[LC_MONETARY], cp[LC_MONETARY]) &&
|
|
!category_needs_update(LC_NUMERIC, old_locinfo,
|
|
locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) {
|
|
locinfo->lconv = old_locinfo->lconv;
|
|
locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount;
|
|
if(locinfo->lconv_intl_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
|
|
} else if(locale_sname[LC_MONETARY] || locale_sname[LC_NUMERIC]) {
|
|
locinfo->lconv = malloc(sizeof(struct lconv));
|
|
locinfo->lconv_intl_refcount = malloc(sizeof(int));
|
|
if(!locinfo->lconv || !locinfo->lconv_intl_refcount) {
|
|
free(locinfo->lconv);
|
|
free(locinfo->lconv_intl_refcount);
|
|
locinfo->lconv = NULL;
|
|
locinfo->lconv_intl_refcount = NULL;
|
|
goto fail;
|
|
}
|
|
memset(locinfo->lconv, 0, sizeof(struct lconv));
|
|
*locinfo->lconv_intl_refcount = 1;
|
|
} else {
|
|
locinfo->lconv = &cloc_lconv;
|
|
}
|
|
|
|
if(locale_name[LC_MONETARY] &&
|
|
!init_category_name(locale_name[LC_MONETARY],
|
|
locale_len[LC_MONETARY], locinfo, LC_MONETARY)) {
|
|
goto fail;
|
|
}
|
|
|
|
if(!category_needs_update(LC_MONETARY, old_locinfo,
|
|
locale_sname[LC_MONETARY], cp[LC_MONETARY])) {
|
|
copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY);
|
|
locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount;
|
|
if(locinfo->lconv_mon_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
|
|
if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
|
|
locinfo->lconv->int_curr_symbol = old_locinfo->lconv->int_curr_symbol;
|
|
locinfo->lconv->currency_symbol = old_locinfo->lconv->currency_symbol;
|
|
locinfo->lconv->mon_decimal_point = old_locinfo->lconv->mon_decimal_point;
|
|
locinfo->lconv->mon_thousands_sep = old_locinfo->lconv->mon_thousands_sep;
|
|
locinfo->lconv->mon_grouping = old_locinfo->lconv->mon_grouping;
|
|
locinfo->lconv->positive_sign = old_locinfo->lconv->positive_sign;
|
|
locinfo->lconv->negative_sign = old_locinfo->lconv->negative_sign;
|
|
locinfo->lconv->int_frac_digits = old_locinfo->lconv->int_frac_digits;
|
|
locinfo->lconv->frac_digits = old_locinfo->lconv->frac_digits;
|
|
locinfo->lconv->p_cs_precedes = old_locinfo->lconv->p_cs_precedes;
|
|
locinfo->lconv->p_sep_by_space = old_locinfo->lconv->p_sep_by_space;
|
|
locinfo->lconv->n_cs_precedes = old_locinfo->lconv->n_cs_precedes;
|
|
locinfo->lconv->n_sep_by_space = old_locinfo->lconv->n_sep_by_space;
|
|
locinfo->lconv->p_sign_posn = old_locinfo->lconv->p_sign_posn;
|
|
locinfo->lconv->n_sign_posn = old_locinfo->lconv->n_sign_posn;
|
|
#if _MSVCR_VER >= 100
|
|
locinfo->lconv->_W_int_curr_symbol = old_locinfo->lconv->_W_int_curr_symbol;
|
|
locinfo->lconv->_W_currency_symbol = old_locinfo->lconv->_W_currency_symbol;
|
|
locinfo->lconv->_W_mon_decimal_point = old_locinfo->lconv->_W_mon_decimal_point;
|
|
locinfo->lconv->_W_mon_thousands_sep = old_locinfo->lconv->_W_mon_thousands_sep;
|
|
locinfo->lconv->_W_positive_sign = old_locinfo->lconv->_W_positive_sign;
|
|
locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign;
|
|
#endif
|
|
}
|
|
} else if(locale_sname[LC_MONETARY]) {
|
|
if(!update_threadlocinfo_category(locale_sname[LC_MONETARY],
|
|
cp[LC_MONETARY], locinfo, LC_MONETARY)) {
|
|
goto fail;
|
|
}
|
|
|
|
locinfo->lconv_mon_refcount = malloc(sizeof(int));
|
|
if(!locinfo->lconv_mon_refcount) {
|
|
goto fail;
|
|
}
|
|
|
|
*locinfo->lconv_mon_refcount = 1;
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->int_curr_symbol = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->int_curr_symbol, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->currency_symbol = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->currency_symbol, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->mon_decimal_point = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_decimal_point, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->mon_thousands_sep = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_thousands_sep, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONGROUPING
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buf, 256, NULL, NULL);
|
|
if(i>1)
|
|
i = i/2 + (buf[i-2]=='0'?0:1);
|
|
if(i && (locinfo->lconv->mon_grouping = malloc(i))) {
|
|
for(i=0; buf[i+1]==';'; i+=2)
|
|
locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
|
|
locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
|
|
if(buf[i] != '0')
|
|
locinfo->lconv->mon_grouping[i/2+1] = 127;
|
|
} else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->positive_sign = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->positive_sign, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->negative_sign = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->negative_sign, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IINTLCURRDIGITS
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->int_frac_digits = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_ICURRDIGITS
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->frac_digits = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSYMPRECEDES
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->p_cs_precedes = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSEPBYSPACE
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->p_sep_by_space = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSYMPRECEDES
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->n_cs_precedes = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSEPBYSPACE
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->n_sep_by_space = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSIGNPOSN
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->p_sign_posn = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSIGNPOSN
|
|
|LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
|
|
locinfo->lconv->n_sign_posn = val;
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 100
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
#endif
|
|
} else {
|
|
if (locinfo->lconv != &cloc_lconv) {
|
|
locinfo->lconv->int_curr_symbol = cloc_lconv.int_curr_symbol;
|
|
locinfo->lconv->currency_symbol = cloc_lconv.currency_symbol;
|
|
locinfo->lconv->mon_decimal_point = cloc_lconv.mon_decimal_point;
|
|
locinfo->lconv->mon_thousands_sep = cloc_lconv.mon_thousands_sep;
|
|
locinfo->lconv->mon_grouping = cloc_lconv.mon_grouping;
|
|
locinfo->lconv->positive_sign = cloc_lconv.positive_sign;
|
|
locinfo->lconv->negative_sign = cloc_lconv.negative_sign;
|
|
locinfo->lconv->int_frac_digits = cloc_lconv.int_frac_digits;
|
|
locinfo->lconv->frac_digits = cloc_lconv.frac_digits;
|
|
locinfo->lconv->p_cs_precedes = cloc_lconv.p_cs_precedes;
|
|
locinfo->lconv->p_sep_by_space = cloc_lconv.p_sep_by_space;
|
|
locinfo->lconv->n_cs_precedes = cloc_lconv.n_cs_precedes;
|
|
locinfo->lconv->n_sep_by_space = cloc_lconv.n_sep_by_space;
|
|
locinfo->lconv->p_sign_posn = cloc_lconv.p_sign_posn;
|
|
locinfo->lconv->n_sign_posn = cloc_lconv.n_sign_posn;
|
|
|
|
#if _MSVCR_VER >= 100
|
|
locinfo->lconv->_W_int_curr_symbol = cloc_lconv._W_int_curr_symbol;
|
|
locinfo->lconv->_W_currency_symbol = cloc_lconv._W_currency_symbol;
|
|
locinfo->lconv->_W_mon_decimal_point = cloc_lconv._W_mon_decimal_point;
|
|
locinfo->lconv->_W_mon_thousands_sep = cloc_lconv._W_mon_thousands_sep;
|
|
locinfo->lconv->_W_positive_sign = cloc_lconv._W_positive_sign;
|
|
locinfo->lconv->_W_negative_sign = cloc_lconv._W_negative_sign;
|
|
#endif
|
|
}
|
|
|
|
if(!init_category_name("C", 1, locinfo, LC_MONETARY)) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if(locale_name[LC_NUMERIC] &&
|
|
!init_category_name(locale_name[LC_NUMERIC],
|
|
locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) {
|
|
goto fail;
|
|
}
|
|
|
|
if(!category_needs_update(LC_NUMERIC, old_locinfo,
|
|
locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) {
|
|
copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC);
|
|
locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount;
|
|
if(locinfo->lconv_num_refcount)
|
|
InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
|
|
if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
|
|
locinfo->lconv->decimal_point = old_locinfo->lconv->decimal_point;
|
|
locinfo->lconv->thousands_sep = old_locinfo->lconv->thousands_sep;
|
|
locinfo->lconv->grouping = old_locinfo->lconv->grouping;
|
|
#if _MSVCR_VER >= 100
|
|
locinfo->lconv->_W_decimal_point = old_locinfo->lconv->_W_decimal_point;
|
|
locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep;
|
|
#endif
|
|
}
|
|
} else if(locale_sname[LC_NUMERIC]) {
|
|
if(!update_threadlocinfo_category(locale_sname[LC_NUMERIC],
|
|
cp[LC_NUMERIC], locinfo, LC_NUMERIC)) {
|
|
goto fail;
|
|
}
|
|
|
|
locinfo->lconv_num_refcount = malloc(sizeof(int));
|
|
if(!locinfo->lconv_num_refcount) {
|
|
goto fail;
|
|
}
|
|
|
|
*locinfo->lconv_num_refcount = 1;
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->decimal_point = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->decimal_point, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL);
|
|
if(i && (locinfo->lconv->thousands_sep = malloc(i)))
|
|
WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->thousands_sep, i, NULL, NULL);
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SGROUPING
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, buf, 256, NULL, NULL);
|
|
if(i>1)
|
|
i = i/2 + (buf[i-2]=='0'?0:1);
|
|
if(i && (locinfo->lconv->grouping = malloc(i))) {
|
|
for(i=0; buf[i+1]==';'; i+=2)
|
|
locinfo->lconv->grouping[i/2] = buf[i]-'0';
|
|
locinfo->lconv->grouping[i/2] = buf[i]-'0';
|
|
if(buf[i] != '0')
|
|
locinfo->lconv->grouping[i/2+1] = 127;
|
|
} else {
|
|
goto fail;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 100
|
|
i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND
|
|
|LOCALE_NOUSEROVERRIDE, wbuf, 256);
|
|
if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t))))
|
|
memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t));
|
|
else {
|
|
goto fail;
|
|
}
|
|
#endif
|
|
} else {
|
|
if (locinfo->lconv != &cloc_lconv) {
|
|
locinfo->lconv->decimal_point = cloc_lconv.decimal_point;
|
|
locinfo->lconv->thousands_sep = cloc_lconv.thousands_sep;
|
|
locinfo->lconv->grouping = cloc_lconv.grouping;
|
|
|
|
#if _MSVCR_VER >= 100
|
|
locinfo->lconv->_W_decimal_point = cloc_lconv._W_decimal_point;
|
|
locinfo->lconv->_W_thousands_sep = cloc_lconv._W_thousands_sep;
|
|
#endif
|
|
}
|
|
|
|
if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if(locale_name[LC_TIME] &&
|
|
!init_category_name(locale_name[LC_TIME],
|
|
locale_len[LC_TIME], locinfo, LC_TIME)) {
|
|
goto fail;
|
|
}
|
|
|
|
if(!category_needs_update(LC_TIME, old_locinfo,
|
|
locale_sname[LC_TIME], cp[LC_TIME])) {
|
|
copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME);
|
|
locinfo->lc_time_curr = old_locinfo->lc_time_curr;
|
|
InterlockedIncrement(&locinfo->lc_time_curr->refcount);
|
|
} else if(locale_sname[LC_TIME]) {
|
|
if(!update_threadlocinfo_category(locale_sname[LC_TIME],
|
|
cp[LC_TIME], locinfo, LC_TIME)) {
|
|
goto fail;
|
|
}
|
|
|
|
locinfo->lc_time_curr = create_time_data(locale_sname[LC_TIME]);
|
|
if(!locinfo->lc_time_curr) {
|
|
goto fail;
|
|
}
|
|
} else {
|
|
if(!init_category_name("C", 1, locinfo, LC_TIME)) {
|
|
goto fail;
|
|
}
|
|
locinfo->lc_time_curr = &cloc_time_data;
|
|
InterlockedIncrement(&locinfo->lc_time_curr->refcount);
|
|
}
|
|
|
|
for (i = 0; i < LC_MAX; i++)
|
|
free(locale_sname[i]);
|
|
|
|
return locinfo;
|
|
|
|
fail:
|
|
free_locinfo(locinfo);
|
|
|
|
for (i = 0; i < LC_MAX; i++)
|
|
free(locale_sname[i]);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _create_locale (MSVCRT.@)
|
|
*/
|
|
_locale_t CDECL _create_locale(int category, const char *locale)
|
|
{
|
|
_locale_t loc;
|
|
|
|
loc = malloc(sizeof(_locale_tstruct));
|
|
if(!loc)
|
|
return NULL;
|
|
|
|
loc->locinfo = create_locinfo(category, locale, NULL);
|
|
if(!loc->locinfo) {
|
|
free(loc);
|
|
return NULL;
|
|
}
|
|
|
|
loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[LC_CTYPE].wCodePage,
|
|
loc->locinfo->lc_handle[LC_CTYPE], NULL);
|
|
if(!loc->mbcinfo) {
|
|
free_locinfo(loc->locinfo);
|
|
free(loc);
|
|
return NULL;
|
|
}
|
|
return loc;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 110
|
|
/*********************************************************************
|
|
* _wcreate_locale (MSVCR110.@)
|
|
*/
|
|
_locale_t CDECL _wcreate_locale(int category, const wchar_t *locale)
|
|
{
|
|
_locale_t loc;
|
|
size_t len;
|
|
char *str;
|
|
|
|
if(category<LC_MIN || category>LC_MAX || !locale)
|
|
return NULL;
|
|
|
|
len = wcstombs(NULL, locale, 0);
|
|
if(len == -1)
|
|
return NULL;
|
|
if(!(str = malloc(++len)))
|
|
return NULL;
|
|
wcstombs(str, locale, len);
|
|
|
|
loc = _create_locale(category, str);
|
|
|
|
free(str);
|
|
return loc;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* setlocale (MSVCRT.@)
|
|
*/
|
|
char* CDECL setlocale(int category, const char* locale)
|
|
{
|
|
thread_data_t *data = msvcrt_get_thread_data();
|
|
pthreadlocinfo locinfo = get_locinfo(), newlocinfo;
|
|
int locale_flags;
|
|
|
|
if(category<LC_MIN || category>LC_MAX)
|
|
return NULL;
|
|
|
|
if(!locale) {
|
|
if(category == LC_ALL)
|
|
return construct_lc_all(locinfo);
|
|
|
|
return locinfo->lc_category[category].locale;
|
|
}
|
|
|
|
/* Make sure that locinfo is not updated by e.g. stricmp function */
|
|
locale_flags = data->locale_flags;
|
|
data->locale_flags |= LOCALE_THREAD;
|
|
newlocinfo = create_locinfo(category, locale, locinfo);
|
|
data->locale_flags = locale_flags;
|
|
if(!newlocinfo) {
|
|
WARN("%d %s failed\n", category, locale);
|
|
return NULL;
|
|
}
|
|
|
|
if(locale[0] != 'C' || locale[1] != '\0')
|
|
initial_locale = FALSE;
|
|
|
|
if(data->locale_flags & LOCALE_THREAD)
|
|
{
|
|
if(data->locale_flags & LOCALE_FREE)
|
|
free_locinfo(data->locinfo);
|
|
data->locinfo = newlocinfo;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
|
|
_lock_locales();
|
|
free_locinfo(MSVCRT_locale->locinfo);
|
|
MSVCRT_locale->locinfo = newlocinfo;
|
|
|
|
MSVCRT___lc_codepage = newlocinfo->lc_codepage;
|
|
MSVCRT___lc_collate_cp = newlocinfo->lc_collate_cp;
|
|
MSVCRT___mb_cur_max = newlocinfo->mb_cur_max;
|
|
MSVCRT__pctype = newlocinfo->pctype;
|
|
for(i=LC_MIN; i<=LC_MAX; i++)
|
|
MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
|
|
_unlock_locales();
|
|
update_thread_locale(data);
|
|
}
|
|
|
|
if(category == LC_ALL)
|
|
return construct_lc_all(data->locinfo);
|
|
|
|
return data->locinfo->lc_category[category].locale;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _wsetlocale (MSVCRT.@)
|
|
*/
|
|
wchar_t* CDECL _wsetlocale(int category, const wchar_t* wlocale)
|
|
{
|
|
static wchar_t current_lc_all[MAX_LOCALE_LENGTH];
|
|
|
|
char *locale = NULL;
|
|
const char *ret;
|
|
size_t len;
|
|
|
|
if(wlocale) {
|
|
len = wcstombs(NULL, wlocale, 0);
|
|
if(len == -1)
|
|
return NULL;
|
|
|
|
locale = malloc(++len);
|
|
if(!locale)
|
|
return NULL;
|
|
|
|
wcstombs(locale, wlocale, len);
|
|
}
|
|
|
|
_lock_locales();
|
|
ret = setlocale(category, locale);
|
|
free(locale);
|
|
|
|
if(ret && mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
|
|
ret = NULL;
|
|
|
|
_unlock_locales();
|
|
return ret ? current_lc_all : NULL;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 80
|
|
/*********************************************************************
|
|
* _configthreadlocale (MSVCR80.@)
|
|
*/
|
|
int CDECL _configthreadlocale(int type)
|
|
{
|
|
thread_data_t *data = msvcrt_get_thread_data();
|
|
int ret;
|
|
|
|
ret = (data->locale_flags & LOCALE_THREAD ? _ENABLE_PER_THREAD_LOCALE :
|
|
_DISABLE_PER_THREAD_LOCALE);
|
|
|
|
if(type == _ENABLE_PER_THREAD_LOCALE)
|
|
data->locale_flags |= LOCALE_THREAD;
|
|
else if(type == _DISABLE_PER_THREAD_LOCALE)
|
|
data->locale_flags &= ~LOCALE_THREAD;
|
|
else if(type)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
BOOL msvcrt_init_locale(void)
|
|
{
|
|
int i;
|
|
|
|
_lock_locales();
|
|
MSVCRT_locale = _create_locale(0, "C");
|
|
_unlock_locales();
|
|
if(!MSVCRT_locale)
|
|
return FALSE;
|
|
|
|
MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
|
|
MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
|
|
MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
|
|
MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
|
|
for(i=LC_MIN; i<=LC_MAX; i++)
|
|
MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
|
|
_setmbcp(_MB_CP_ANSI);
|
|
return TRUE;
|
|
}
|
|
|
|
#if _MSVCR_VER >= 120
|
|
/*********************************************************************
|
|
* wctrans (MSVCR120.@)
|
|
*/
|
|
wctrans_t CDECL wctrans(const char *property)
|
|
{
|
|
static const char str_tolower[] = "tolower";
|
|
static const char str_toupper[] = "toupper";
|
|
|
|
if(!strcmp(property, str_tolower))
|
|
return 2;
|
|
if(!strcmp(property, str_toupper))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
#endif
|