winmm: Avoid explicitly casting the pointer returned from Heap(Re)Alloc.
[wine.git] / dlls / msvcrt / locale.c
blobf7be2a1e9bca507fb4b21292c2122cae253e48f8
1 /*
2 * msvcrt.dll locale functions
4 * Copyright 2000 Jon Griffiths
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <limits.h>
22 #include <locale.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <mbctype.h>
27 #include <wctype.h>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "winnls.h"
34 #include "msvcrt.h"
35 #include "mtdll.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
41 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
42 #define MAX_LOCALE_LENGTH 256
43 _locale_t MSVCRT_locale = NULL;
44 unsigned short *MSVCRT__pctype = NULL;
45 unsigned int MSVCRT___lc_codepage = 0;
46 int MSVCRT___lc_collate_cp = 0;
47 LCID MSVCRT___lc_handle[LC_MAX - LC_MIN + 1] = { 0 };
48 int MSVCRT___mb_cur_max = 1;
49 BOOL initial_locale = TRUE;
51 #define MSVCRT_LEADBYTE 0x8000
52 #define MSVCRT_C1_DEFINED 0x200
54 #if _MSVCR_VER >= 110
55 #define LCID_CONVERSION_FLAGS LOCALE_ALLOW_NEUTRAL_NAMES
56 #else
57 #define LCID_CONVERSION_FLAGS 0
58 #endif
60 __lc_time_data cloc_time_data =
62 {{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
63 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
64 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
65 "January", "February", "March", "April", "May", "June", "July",
66 "August", "September", "October", "November", "December",
67 "AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"}},
68 #if _MSVCR_VER < 110
69 MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
70 #endif
71 1, -1,
72 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
73 {{L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat",
74 L"Sunday", L"Monday", L"Tuesday", L"Wednesday", L"Thursday", L"Friday", L"Saturday",
75 L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec",
76 L"January", L"February", L"March", L"April", L"May", L"June", L"July",
77 L"August", L"September", L"October", L"November", L"December",
78 L"AM", L"PM", L"MM/dd/yy", L"dddd, MMMM dd, yyyy", L"HH:mm:ss"}},
79 #endif
80 #if _MSVCR_VER >= 110
81 L"en-US",
82 #endif
85 static const unsigned char cloc_clmap[256] =
87 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
88 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
89 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
90 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
91 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
92 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
93 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
94 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
95 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
96 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
97 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
98 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
99 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
100 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
101 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
102 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
103 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
104 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
105 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
106 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
107 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
108 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
109 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
110 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
111 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
112 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
113 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
114 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
115 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
116 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
117 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
118 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
121 static const unsigned char cloc_cumap[256] =
123 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
124 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
125 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
126 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
127 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
128 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
129 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
130 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
131 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
132 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
133 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
134 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
135 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
136 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
137 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
138 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
139 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
140 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
141 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
142 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
143 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
144 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
145 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
146 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
147 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
148 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
149 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
150 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
151 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
152 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
153 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
154 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
157 static char empty[] = "";
158 static char cloc_dec_point[] = ".";
159 #if _MSVCR_VER >= 100
160 static wchar_t emptyW[] = L"";
161 static wchar_t cloc_dec_pointW[] = L".";
162 #endif
163 static struct lconv cloc_lconv =
165 cloc_dec_point, empty, empty, empty, empty, empty, empty, empty, empty, empty,
166 CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX,
167 #if _MSVCR_VER >= 100
168 cloc_dec_pointW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW
169 #endif
172 /* Friendly country strings & language names abbreviations. */
173 static const char * const _country_synonyms[] =
175 "american", "en",
176 "american english", "en-US",
177 "american-english", "en-US",
178 "english-american", "en-US",
179 "english-us", "en-US",
180 "english-usa", "en-US",
181 "us", "en-US",
182 "usa", "en-US",
183 "australian", "en-AU",
184 "english-aus", "en-AU",
185 "belgian", "nl-BE",
186 "french-belgian", "fr-BE",
187 "canadian", "en-CA",
188 "english-can", "en-CA",
189 "french-canadian", "fr-CA",
190 #if _MSVCR_VER >= 110
191 "chinese", "zh",
192 "chinese-simplified", "zh",
193 "chinese-traditional", "zh-HK",
194 "chs", "zh",
195 "cht", "zh-HK",
196 #else
197 "chinese", "zh-CN",
198 "chinese-simplified", "zh-CN",
199 "chinese-traditional", "zh-TW",
200 "chs", "zh-CN",
201 "cht", "zh-TW",
202 #endif
203 "dutch-belgian", "nl-BE",
204 "english-nz", "en-NZ",
205 "uk", "en-GB",
206 "english-uk", "en-GB",
207 "french-swiss", "fr-CH",
208 "swiss", "de-CH",
209 "german-swiss", "de-CH",
210 "italian-swiss", "it-CH",
211 "german-austrian", "de-AT",
212 "portuguese", "pt-BR",
213 "portuguese-brazil", "pt-BR",
214 "spanish-mexican", "es-MX",
215 "norwegian-bokmal", "nb",
216 "norwegian-nynorsk", "nn-NO",
217 "spanish-modern", "es-ES"
221 /* INTERNAL: Map a synonym to an ISO code */
222 static BOOL remap_synonym(char *name)
224 unsigned int i;
225 for (i = 0; i < ARRAY_SIZE(_country_synonyms); i += 2)
227 if (!_stricmp(_country_synonyms[i],name))
229 TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
230 strcpy(name, _country_synonyms[i+1]);
231 return TRUE;
235 return FALSE;
238 /* Note: Flags are weighted in order of matching importance */
239 #define FOUND_SNAME 0x4
240 #define FOUND_LANGUAGE 0x2
241 #define FOUND_COUNTRY 0x1
243 typedef struct {
244 WCHAR search_language[MAX_ELEM_LEN];
245 WCHAR search_country[MAX_ELEM_LEN];
246 WCHAR found_lang_sname[LOCALE_NAME_MAX_LENGTH];
247 unsigned int match_flags;
248 BOOL allow_sname;
249 } locale_search_t;
251 #define CONTINUE_LOOKING TRUE
252 #define STOP_LOOKING FALSE
254 /* INTERNAL: Get and compare locale info with a given string */
255 static int compare_info(WCHAR *name, DWORD flags, WCHAR *buff, const WCHAR *cmp, BOOL exact)
257 int len;
259 if(!cmp[0])
260 return 0;
262 buff[0] = 0;
263 GetLocaleInfoEx(name, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN);
264 if (!buff[0])
265 return 0;
267 /* Partial matches are only allowed on language/country names */
268 len = wcslen(cmp);
270 if(exact || len<=3)
271 return !_wcsicmp(cmp, buff);
272 else
273 return !_wcsnicmp(cmp, buff, len);
276 static BOOL CALLBACK
277 find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam )
279 locale_search_t *res = (locale_search_t *)lParam;
280 WCHAR buff[MAX_ELEM_LEN];
281 unsigned int flags = 0;
283 if (res->allow_sname && compare_info(name,LOCALE_SNAME,buff,res->search_language, TRUE))
285 TRACE(":Found locale: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff));
286 res->match_flags = FOUND_SNAME;
287 wcscpy(res->found_lang_sname, name);
288 return STOP_LOOKING;
291 /* Check Language */
292 if (compare_info(name,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) ||
293 compare_info(name,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) ||
294 compare_info(name,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE))
296 TRACE(":Found language: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff));
297 flags |= FOUND_LANGUAGE;
299 else if (res->match_flags & FOUND_LANGUAGE)
301 return CONTINUE_LOOKING;
304 /* Check Country */
305 if (compare_info(name,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) ||
306 compare_info(name,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) ||
307 compare_info(name,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE))
309 TRACE("Found country:%s->%s\n", wine_dbgstr_w(res->search_country), wine_dbgstr_w(buff));
310 flags |= FOUND_COUNTRY;
312 else if (!flags && (res->match_flags & FOUND_COUNTRY))
314 return CONTINUE_LOOKING;
317 if (flags > res->match_flags)
319 /* Found a better match than previously */
320 res->match_flags = flags;
321 wcscpy(res->found_lang_sname, name);
323 if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY)) ==
324 (FOUND_LANGUAGE | FOUND_COUNTRY))
326 TRACE(":found exact locale match\n");
327 return STOP_LOOKING;
329 return CONTINUE_LOOKING;
332 /* Internal: Find the sname for a locale specification.
333 * sname must be at least LOCALE_NAME_MAX_LENGTH characters long
335 BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_match, WCHAR *sname)
337 thread_data_t *data = msvcrt_get_thread_data();
338 const char *cp, *region;
339 BOOL is_sname = FALSE;
340 DWORD locale_cp;
342 if (!strcmp(locale, data->cached_locale)) {
343 if (codepage)
344 *codepage = data->cached_cp;
345 if (sname_match)
346 *sname_match = data->cached_sname_match;
347 wcscpy(sname, data->cached_sname);
348 return TRUE;
351 cp = strchr(locale, '.');
352 region = strchr(locale, '_');
354 if(!locale[0] || (cp == locale && !region)) {
355 GetUserDefaultLocaleName(sname, LOCALE_NAME_MAX_LENGTH);
356 } else {
357 char search_language_buf[MAX_ELEM_LEN] = { 0 }, search_country_buf[MAX_ELEM_LEN] = { 0 };
358 locale_search_t search;
359 BOOL remapped = FALSE;
361 memset(&search, 0, sizeof(locale_search_t));
362 lstrcpynA(search_language_buf, locale, MAX_ELEM_LEN);
363 if(region) {
364 lstrcpynA(search_country_buf, region+1, MAX_ELEM_LEN);
365 if(region-locale < MAX_ELEM_LEN)
366 search_language_buf[region-locale] = '\0';
367 } else
368 search_country_buf[0] = '\0';
370 if(cp) {
371 if(region && cp-region-1<MAX_ELEM_LEN)
372 search_country_buf[cp-region-1] = '\0';
373 if(cp-locale < MAX_ELEM_LEN)
374 search_language_buf[cp-locale] = '\0';
377 if ((remapped = remap_synonym(search_language_buf)))
379 search.allow_sname = TRUE;
382 #if _MSVCR_VER >= 110
383 if(!cp && !region)
385 search.allow_sname = TRUE;
387 #endif
389 MultiByteToWideChar(CP_ACP, 0, search_language_buf, -1, search.search_language, MAX_ELEM_LEN);
390 if (search.allow_sname && IsValidLocaleName(search.search_language))
392 search.match_flags = FOUND_SNAME;
393 wcscpy(sname, search.search_language);
395 else
397 MultiByteToWideChar(CP_ACP, 0, search_country_buf, -1, search.search_country, MAX_ELEM_LEN);
398 EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL);
400 if (!search.match_flags)
401 return FALSE;
403 /* If we were given something that didn't match, fail */
404 if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE)))
405 return FALSE;
406 if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
407 return FALSE;
409 wcscpy(sname, search.found_lang_sname);
412 is_sname = !remapped && (search.match_flags & FOUND_SNAME) != 0;
415 /* Obtain code page */
416 if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) {
417 GetLocaleInfoEx(sname, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
418 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
419 if (!locale_cp)
420 locale_cp = GetACP();
421 } else if (!_strnicmp(cp, ".OCP", 4)) {
422 GetLocaleInfoEx(sname, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
423 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
424 #if _MSVCR_VER >= 140
425 } else if (!_strnicmp(cp, ".UTF-8", 6)
426 || !_strnicmp(cp, ".UTF8", 5)) {
427 locale_cp = CP_UTF8;
428 #endif
429 } else {
430 locale_cp = atoi(cp + 1);
432 if (!IsValidCodePage(locale_cp))
433 return FALSE;
435 if (!locale_cp)
436 return FALSE;
438 if (codepage)
439 *codepage = locale_cp;
440 if (sname_match)
441 *sname_match = is_sname;
443 if (strlen(locale) < sizeof(data->cached_locale)) {
444 strcpy(data->cached_locale, locale);
445 data->cached_cp = locale_cp;
446 data->cached_sname_match = is_sname;
447 wcscpy(data->cached_sname, sname);
450 return TRUE;
453 static void copy_threadlocinfo_category(pthreadlocinfo locinfo,
454 const threadlocinfo *old_locinfo, int category)
456 locinfo->lc_handle[category] = old_locinfo->lc_handle[category];
457 locinfo->lc_id[category] = old_locinfo->lc_id[category];
458 if(!locinfo->lc_category[category].locale) {
459 locinfo->lc_category[category].locale = old_locinfo->lc_category[category].locale;
460 locinfo->lc_category[category].refcount = old_locinfo->lc_category[category].refcount;
461 InterlockedIncrement((LONG *)locinfo->lc_category[category].refcount);
463 #if _MSVCR_VER >= 110
464 locinfo->lc_name[category] = old_locinfo->lc_name[category];
465 locinfo->lc_category[category].wrefcount = old_locinfo->lc_category[category].wrefcount;
466 if(locinfo->lc_category[category].wrefcount)
467 InterlockedIncrement((LONG *)locinfo->lc_category[category].wrefcount);
468 #endif
471 static BOOL init_category_name(const char *name, int len,
472 pthreadlocinfo locinfo, int category)
474 locinfo->lc_category[category].locale = malloc(len+1);
475 locinfo->lc_category[category].refcount = malloc(sizeof(int));
476 if(!locinfo->lc_category[category].locale
477 || !locinfo->lc_category[category].refcount) {
478 free(locinfo->lc_category[category].locale);
479 free(locinfo->lc_category[category].refcount);
480 locinfo->lc_category[category].locale = NULL;
481 locinfo->lc_category[category].refcount = NULL;
482 return FALSE;
485 memcpy(locinfo->lc_category[category].locale, name, len);
486 locinfo->lc_category[category].locale[len] = 0;
487 *locinfo->lc_category[category].refcount = 1;
488 return TRUE;
491 #if _MSVCR_VER >= 110
492 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname)
494 locinfo->lc_category[cat].wrefcount = malloc(sizeof(int));
495 if(!locinfo->lc_category[cat].wrefcount)
496 return FALSE;
497 *locinfo->lc_category[cat].wrefcount = 1;
499 if(!(locinfo->lc_name[cat] = wcsdup(sname)))
500 return FALSE;
502 return TRUE;
504 #else
505 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname)
507 return TRUE;
509 #endif
511 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
512 static BOOL update_threadlocinfo_category(WCHAR *sname, unsigned short cp,
513 pthreadlocinfo locinfo, int category)
515 WCHAR wbuf[256], *p;
517 if(GetLocaleInfoEx(sname, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf))) {
518 p = wbuf;
520 locinfo->lc_id[category].wLanguage = 0;
521 while(*p) {
522 locinfo->lc_id[category].wLanguage *= 16;
524 if(*p <= '9')
525 locinfo->lc_id[category].wLanguage += *p-'0';
526 else
527 locinfo->lc_id[category].wLanguage += *p-'a'+10;
529 p++;
532 locinfo->lc_id[category].wCountry =
533 locinfo->lc_id[category].wLanguage;
536 locinfo->lc_id[category].wCodePage = cp;
538 locinfo->lc_handle[category] = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS);
540 set_lc_locale_name(locinfo, category, sname);
542 if(!locinfo->lc_category[category].locale) {
543 char buf[256];
544 int len = 0;
546 #if _MSVCR_VER < 110
547 if (LANGIDFROMLCID(locinfo->lc_handle[category]) == MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK))
549 /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */
550 wcscpy( wbuf, L"Norwegian-Nynorsk" );
551 len = wcslen( wbuf ) + 1;
553 else
554 #endif
555 len += GetLocaleInfoEx(sname, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf));
556 wbuf[len-1] = '_';
557 len += GetLocaleInfoEx(sname, LOCALE_SENGCOUNTRY
558 |LOCALE_NOUSEROVERRIDE, &wbuf[len], ARRAY_SIZE(wbuf) - len);
559 wbuf[len-1] = '.';
560 swprintf(wbuf+len, ARRAY_SIZE(wbuf) - len,L"%d", cp);
561 len += wcslen(wbuf+len);
563 WideCharToMultiByte(cp, 0, wbuf, -1, buf, ARRAY_SIZE(buf), NULL, NULL);
565 return init_category_name(buf, len, locinfo, category);
568 return TRUE;
571 /*********************************************************************
572 * _lock_locales (UCRTBASE.@)
574 void CDECL _lock_locales(void)
576 _lock(_SETLOCALE_LOCK);
579 /*********************************************************************
580 * _unlock_locales (UCRTBASE.@)
582 void CDECL _unlock_locales(void)
584 _unlock(_SETLOCALE_LOCK);
587 static void grab_locinfo(pthreadlocinfo locinfo)
589 int i;
591 InterlockedIncrement((LONG *)&locinfo->refcount);
592 for(i=LC_MIN+1; i<=LC_MAX; i++)
594 InterlockedIncrement((LONG *)locinfo->lc_category[i].refcount);
595 if(locinfo->lc_category[i].wrefcount)
596 InterlockedIncrement((LONG *)locinfo->lc_category[i].wrefcount);
598 if(locinfo->lconv_intl_refcount)
599 InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
600 if(locinfo->lconv_num_refcount)
601 InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
602 if(locinfo->lconv_mon_refcount)
603 InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
604 if(locinfo->ctype1_refcount)
605 InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
606 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
609 static void update_thread_locale(thread_data_t *data)
611 if((data->locale_flags & LOCALE_FREE) && ((data->locale_flags & LOCALE_THREAD) ||
612 (data->locinfo == MSVCRT_locale->locinfo && data->mbcinfo == MSVCRT_locale->mbcinfo)))
613 return;
615 if(data->locale_flags & LOCALE_FREE)
617 free_locinfo(data->locinfo);
618 free_mbcinfo(data->mbcinfo);
621 _lock_locales();
622 data->locinfo = MSVCRT_locale->locinfo;
623 grab_locinfo(data->locinfo);
624 _unlock_locales();
626 _lock(_MB_CP_LOCK);
627 data->mbcinfo = MSVCRT_locale->mbcinfo;
628 InterlockedIncrement(&data->mbcinfo->refcount);
629 _unlock(_MB_CP_LOCK);
631 data->locale_flags |= LOCALE_FREE;
634 /* INTERNAL: returns threadlocinfo struct */
635 pthreadlocinfo CDECL get_locinfo(void) {
636 thread_data_t *data = msvcrt_get_thread_data();
637 update_thread_locale(data);
638 return data->locinfo;
641 /* INTERNAL: returns pthreadmbcinfo struct */
642 pthreadmbcinfo CDECL get_mbcinfo(void) {
643 thread_data_t *data = msvcrt_get_thread_data();
644 update_thread_locale(data);
645 return data->mbcinfo;
648 /* INTERNAL: constructs string returned by setlocale */
649 static inline char* construct_lc_all(pthreadlocinfo locinfo) {
650 static char current_lc_all[MAX_LOCALE_LENGTH];
652 int i;
654 for(i=LC_MIN+1; i<LC_MAX; i++) {
655 if(strcmp(locinfo->lc_category[i].locale,
656 locinfo->lc_category[i+1].locale))
657 break;
660 if(i==LC_MAX)
661 return locinfo->lc_category[LC_COLLATE].locale;
663 sprintf(current_lc_all,
664 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
665 locinfo->lc_category[LC_COLLATE].locale,
666 locinfo->lc_category[LC_CTYPE].locale,
667 locinfo->lc_category[LC_MONETARY].locale,
668 locinfo->lc_category[LC_NUMERIC].locale,
669 locinfo->lc_category[LC_TIME].locale);
671 return current_lc_all;
675 /*********************************************************************
676 * _Getdays (MSVCRT.@)
678 char* CDECL _Getdays(void)
680 __lc_time_data *cur = get_locinfo()->lc_time_curr;
681 int i, len, size = 0;
682 char *out;
684 TRACE("\n");
686 for(i=0; i<7; i++) {
687 size += strlen(cur->str.names.short_wday[i]) + 1;
688 size += strlen(cur->str.names.wday[i]) + 1;
690 out = malloc(size+1);
691 if(!out)
692 return NULL;
694 size = 0;
695 for(i=0; i<7; i++) {
696 out[size++] = ':';
697 len = strlen(cur->str.names.short_wday[i]);
698 memcpy(&out[size], cur->str.names.short_wday[i], len);
699 size += len;
701 out[size++] = ':';
702 len = strlen(cur->str.names.wday[i]);
703 memcpy(&out[size], cur->str.names.wday[i], len);
704 size += len;
706 out[size] = '\0';
708 return out;
711 #if _MSVCR_VER >= 110
712 /*********************************************************************
713 * _W_Getdays (MSVCR110.@)
715 wchar_t* CDECL _W_Getdays(void)
717 __lc_time_data *cur = get_locinfo()->lc_time_curr;
718 wchar_t *out;
719 int i, len, size = 0;
721 TRACE("\n");
723 for(i=0; i<7; i++) {
724 size += wcslen(cur->wstr.names.short_wday[i]) + 1;
725 size += wcslen(cur->wstr.names.wday[i]) + 1;
727 out = malloc((size+1)*sizeof(*out));
728 if(!out)
729 return NULL;
731 size = 0;
732 for(i=0; i<7; i++) {
733 out[size++] = ':';
734 len = wcslen(cur->wstr.names.short_wday[i]);
735 memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
736 size += len;
738 out[size++] = ':';
739 len = wcslen(cur->wstr.names.wday[i]);
740 memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
741 size += len;
743 out[size] = '\0';
745 return out;
747 #endif
749 /*********************************************************************
750 * _Getmonths (MSVCRT.@)
752 char* CDECL _Getmonths(void)
754 __lc_time_data *cur = get_locinfo()->lc_time_curr;
755 int i, len, size = 0;
756 char *out;
758 TRACE("\n");
760 for(i=0; i<12; i++) {
761 size += strlen(cur->str.names.short_mon[i]) + 1;
762 size += strlen(cur->str.names.mon[i]) + 1;
764 out = malloc(size+1);
765 if(!out)
766 return NULL;
768 size = 0;
769 for(i=0; i<12; i++) {
770 out[size++] = ':';
771 len = strlen(cur->str.names.short_mon[i]);
772 memcpy(&out[size], cur->str.names.short_mon[i], len);
773 size += len;
775 out[size++] = ':';
776 len = strlen(cur->str.names.mon[i]);
777 memcpy(&out[size], cur->str.names.mon[i], len);
778 size += len;
780 out[size] = '\0';
782 return out;
785 #if _MSVCR_VER >= 110
786 /*********************************************************************
787 * _W_Getmonths (MSVCR110.@)
789 wchar_t* CDECL _W_Getmonths(void)
791 __lc_time_data *cur = get_locinfo()->lc_time_curr;
792 wchar_t *out;
793 int i, len, size = 0;
795 TRACE("\n");
797 for(i=0; i<12; i++) {
798 size += wcslen(cur->wstr.names.short_mon[i]) + 1;
799 size += wcslen(cur->wstr.names.mon[i]) + 1;
801 out = malloc((size+1)*sizeof(*out));
802 if(!out)
803 return NULL;
805 size = 0;
806 for(i=0; i<12; i++) {
807 out[size++] = ':';
808 len = wcslen(cur->wstr.names.short_mon[i]);
809 memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
810 size += len;
812 out[size++] = ':';
813 len = wcslen(cur->wstr.names.mon[i]);
814 memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
815 size += len;
817 out[size] = '\0';
819 return out;
821 #endif
823 /*********************************************************************
824 * _Gettnames (MSVCRT.@)
826 void* CDECL _Gettnames(void)
828 __lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
829 unsigned int i, len, size = sizeof(__lc_time_data);
831 TRACE("\n");
833 for(i=0; i<ARRAY_SIZE(cur->str.str); i++)
834 size += strlen(cur->str.str[i])+1;
835 #if _MSVCR_VER >= 110
836 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++)
837 size += (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
838 #endif
840 ret = malloc(size);
841 if(!ret)
842 return NULL;
843 memcpy(ret, cur, sizeof(*ret));
845 size = 0;
846 for(i=0; i<ARRAY_SIZE(cur->str.str); i++) {
847 len = strlen(cur->str.str[i])+1;
848 memcpy(&ret->data[size], cur->str.str[i], len);
849 ret->str.str[i] = &ret->data[size];
850 size += len;
852 #if _MSVCR_VER >= 110
853 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++) {
854 len = (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
855 memcpy(&ret->data[size], cur->wstr.wstr[i], len);
856 ret->wstr.wstr[i] = (wchar_t*)&ret->data[size];
857 size += len;
859 #endif
861 return ret;
864 #if _MSVCR_VER >= 110
865 /*********************************************************************
866 * _W_Gettnames (MSVCR110.@)
868 void* CDECL _W_Gettnames(void)
870 return _Gettnames();
872 #endif
874 /*********************************************************************
875 * __crtLCMapStringA (MSVCRT.@)
877 int CDECL __crtLCMapStringA(
878 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
879 int dstlen, unsigned int codepage, int xflag
881 WCHAR buf_in[32], *in = buf_in;
882 WCHAR buf_out[32], *out = buf_out;
883 int in_len, out_len, r;
885 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
886 lcid, mapflags, src, srclen, dst, dstlen, codepage, xflag);
888 in_len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, NULL, 0);
889 if (!in_len) return 0;
890 if (in_len > ARRAY_SIZE(buf_in))
892 in = malloc(in_len * sizeof(WCHAR));
893 if (!in) return 0;
896 r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len);
897 if (!r) goto done;
899 if (mapflags & LCMAP_SORTKEY)
901 r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen);
902 goto done;
905 r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0);
906 if (!r) goto done;
907 out_len = r;
908 if (r > ARRAY_SIZE(buf_out))
910 out = malloc(r * sizeof(WCHAR));
911 if (!out)
913 r = 0;
914 goto done;
918 r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len);
919 if (!r) goto done;
921 r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL);
923 done:
924 if (in != buf_in) free(in);
925 if (out != buf_out) free(out);
926 return r;
929 /*********************************************************************
930 * __crtLCMapStringW (MSVCRT.@)
932 int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src,
933 int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
935 FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
936 lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
938 return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
941 /*********************************************************************
942 * __crtCompareStringA (MSVCRT.@)
944 int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
945 const char *src2, int len2 )
947 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
948 lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
949 /* FIXME: probably not entirely right */
950 return CompareStringA( lcid, flags, src1, len1, src2, len2 );
953 /*********************************************************************
954 * __crtCompareStringW (MSVCRT.@)
956 int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1,
957 const wchar_t *src2, int len2 )
959 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
960 lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
961 /* FIXME: probably not entirely right */
962 return CompareStringW( lcid, flags, src1, len1, src2, len2 );
965 /*********************************************************************
966 * __crtGetLocaleInfoW (MSVCRT.@)
968 int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len )
970 FIXME("(lcid %lx, type %lx, %p(%d), partial stub\n", lcid, type, buffer, len );
971 /* FIXME: probably not entirely right */
972 return GetLocaleInfoW( lcid, type, buffer, len );
975 #if _MSVCR_VER >= 110
976 /*********************************************************************
977 * __crtGetLocaleInfoEx (MSVC110.@)
979 int CDECL __crtGetLocaleInfoEx( const WCHAR *locale, LCTYPE type, wchar_t *buffer, int len )
981 TRACE("(%s, %lx, %p, %d)\n", debugstr_w(locale), type, buffer, len);
982 return GetLocaleInfoEx(locale, type, buffer, len);
984 #endif
986 /*********************************************************************
987 * __crtGetStringTypeW(MSVCRT.@)
989 * This function was accepting different number of arguments in older
990 * versions of msvcrt.
992 BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
993 wchar_t *buffer, int len, WORD *out)
995 FIXME("(unk %lx, type %lx, wstr %p(%d), %p) partial stub\n",
996 unk, type, buffer, len, out);
998 return GetStringTypeW(type, buffer, len, out);
1001 /*********************************************************************
1002 * localeconv (MSVCRT.@)
1004 struct lconv* CDECL localeconv(void)
1006 return get_locinfo()->lconv;
1009 /*********************************************************************
1010 * __lconv_init (MSVCRT.@)
1012 int CDECL __lconv_init(void)
1014 /* this is used to make chars unsigned */
1015 cloc_lconv.int_frac_digits = (char)UCHAR_MAX;
1016 cloc_lconv.frac_digits = (char)UCHAR_MAX;
1017 cloc_lconv.p_cs_precedes = (char)UCHAR_MAX;
1018 cloc_lconv.p_sep_by_space = (char)UCHAR_MAX;
1019 cloc_lconv.n_cs_precedes = (char)UCHAR_MAX;
1020 cloc_lconv.n_sep_by_space = (char)UCHAR_MAX;
1021 cloc_lconv.p_sign_posn = (char)UCHAR_MAX;
1022 cloc_lconv.n_sign_posn = (char)UCHAR_MAX;
1023 return 0;
1026 /*********************************************************************
1027 * ___lc_handle_func (MSVCRT.@)
1029 LCID* CDECL ___lc_handle_func(void)
1031 return (LCID *)get_locinfo()->lc_handle;
1034 #if _MSVCR_VER >= 110
1035 /*********************************************************************
1036 * ___lc_locale_name_func (MSVCR110.@)
1038 wchar_t** CDECL ___lc_locale_name_func(void)
1040 return get_locinfo()->lc_name;
1042 #endif
1044 /*********************************************************************
1045 * ___lc_codepage_func (MSVCRT.@)
1047 unsigned int CDECL ___lc_codepage_func(void)
1049 return get_locinfo()->lc_codepage;
1052 /*********************************************************************
1053 * ___lc_collate_cp_func (MSVCRT.@)
1055 int CDECL ___lc_collate_cp_func(void)
1057 return get_locinfo()->lc_collate_cp;
1060 /* INTERNAL: frees pthreadlocinfo struct */
1061 void free_locinfo(pthreadlocinfo locinfo)
1063 int i;
1065 if(!locinfo)
1066 return;
1068 for(i=LC_MIN+1; i<=LC_MAX; i++) {
1069 if(!locinfo->lc_category[i].refcount
1070 || !InterlockedDecrement((LONG *)locinfo->lc_category[i].refcount)) {
1071 free(locinfo->lc_category[i].locale);
1072 free(locinfo->lc_category[i].refcount);
1074 if(!locinfo->lc_category[i].wrefcount
1075 || !InterlockedDecrement((LONG *)locinfo->lc_category[i].wrefcount)) {
1076 #if _MSVCR_VER >= 110
1077 free(locinfo->lc_name[i]);
1078 #endif
1079 free(locinfo->lc_category[i].wrefcount);
1083 if(locinfo->lconv_num_refcount
1084 && !InterlockedDecrement((LONG *)locinfo->lconv_num_refcount)) {
1085 free(locinfo->lconv->decimal_point);
1086 free(locinfo->lconv->thousands_sep);
1087 free(locinfo->lconv->grouping);
1088 #if _MSVCR_VER >= 100
1089 free(locinfo->lconv->_W_decimal_point);
1090 free(locinfo->lconv->_W_thousands_sep);
1091 #endif
1092 free(locinfo->lconv_num_refcount);
1094 if(locinfo->lconv_mon_refcount
1095 && !InterlockedDecrement((LONG *)locinfo->lconv_mon_refcount)) {
1096 free(locinfo->lconv->int_curr_symbol);
1097 free(locinfo->lconv->currency_symbol);
1098 free(locinfo->lconv->mon_decimal_point);
1099 free(locinfo->lconv->mon_thousands_sep);
1100 free(locinfo->lconv->mon_grouping);
1101 free(locinfo->lconv->positive_sign);
1102 free(locinfo->lconv->negative_sign);
1103 #if _MSVCR_VER >= 100
1104 free(locinfo->lconv->_W_int_curr_symbol);
1105 free(locinfo->lconv->_W_currency_symbol);
1106 free(locinfo->lconv->_W_mon_decimal_point);
1107 free(locinfo->lconv->_W_mon_thousands_sep);
1108 free(locinfo->lconv->_W_positive_sign);
1109 free(locinfo->lconv->_W_negative_sign);
1110 #endif
1111 free(locinfo->lconv_mon_refcount);
1113 if(locinfo->lconv_intl_refcount
1114 && !InterlockedDecrement((LONG *)locinfo->lconv_intl_refcount)) {
1115 free(locinfo->lconv_intl_refcount);
1116 free(locinfo->lconv);
1119 if(locinfo->ctype1_refcount
1120 && !InterlockedDecrement((LONG *)locinfo->ctype1_refcount)) {
1121 free(locinfo->ctype1_refcount);
1122 free(locinfo->ctype1);
1123 free((void*)locinfo->pclmap);
1124 free((void*)locinfo->pcumap);
1127 if(locinfo->lc_time_curr && !InterlockedDecrement(&locinfo->lc_time_curr->refcount)
1128 && locinfo->lc_time_curr != &cloc_time_data)
1129 free(locinfo->lc_time_curr);
1131 if(InterlockedDecrement((LONG *)&locinfo->refcount))
1132 return;
1134 free(locinfo);
1137 /* INTERNAL: frees pthreadmbcinfo struct */
1138 void free_mbcinfo(pthreadmbcinfo mbcinfo)
1140 if(!mbcinfo)
1141 return;
1143 if(InterlockedDecrement(&mbcinfo->refcount))
1144 return;
1146 free(mbcinfo);
1149 _locale_t CDECL get_current_locale_noalloc(_locale_t locale)
1151 thread_data_t *data = msvcrt_get_thread_data();
1153 update_thread_locale(data);
1154 locale->locinfo = data->locinfo;
1155 locale->mbcinfo = data->mbcinfo;
1157 grab_locinfo(locale->locinfo);
1158 InterlockedIncrement(&locale->mbcinfo->refcount);
1159 return locale;
1162 void CDECL free_locale_noalloc(_locale_t locale)
1164 free_locinfo(locale->locinfo);
1165 free_mbcinfo(locale->mbcinfo);
1168 /*********************************************************************
1169 * _get_current_locale (MSVCRT.@)
1171 _locale_t CDECL _get_current_locale(void)
1173 _locale_t loc = malloc(sizeof(_locale_tstruct));
1174 if(!loc)
1175 return NULL;
1177 return get_current_locale_noalloc(loc);
1180 /*********************************************************************
1181 * _free_locale (MSVCRT.@)
1183 void CDECL _free_locale(_locale_t locale)
1185 if (!locale)
1186 return;
1188 free_locale_noalloc(locale);
1189 free(locale);
1192 static inline BOOL category_needs_update(int cat,
1193 const threadlocinfo *locinfo, WCHAR *sname, unsigned short cp)
1195 #if _MSVCR_VER < 110
1196 LCID lcid;
1197 #endif
1198 if(!locinfo) return TRUE;
1199 #if _MSVCR_VER >= 110
1200 if(!locinfo->lc_name[cat] || !sname) return TRUE;
1201 return wcscmp(sname, locinfo->lc_name[cat]) != 0 || cp!=locinfo->lc_id[cat].wCodePage;
1202 #else
1203 lcid = sname ? LocaleNameToLCID(sname, 0) : 0;
1204 return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage;
1205 #endif
1208 static __lc_time_data* create_time_data(WCHAR *sname)
1210 static const DWORD time_data[] = {
1211 LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
1212 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
1213 LOCALE_SABBREVDAYNAME6,
1214 LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
1215 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
1216 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
1217 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
1218 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
1219 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
1220 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
1221 LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
1222 LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
1223 LOCALE_S1159, LOCALE_S2359,
1224 LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
1225 LOCALE_STIMEFORMAT
1228 __lc_time_data *cur;
1229 int i, ret, size;
1230 LCID lcid = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS);
1232 size = 0;
1233 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1234 ret = GetLocaleInfoA(lcid, time_data[i], NULL, 0);
1235 if(!ret)
1236 return NULL;
1237 size += ret;
1239 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1240 ret = GetLocaleInfoEx(sname, time_data[i], NULL, 0);
1241 if(!ret)
1242 return NULL;
1243 size += ret*sizeof(wchar_t);
1244 #endif
1246 #if _MSVCR_VER >= 110
1247 size += (wcslen(sname) + 1) * sizeof(wchar_t);
1248 #endif
1250 cur = malloc(FIELD_OFFSET(__lc_time_data, data[size]));
1251 if(!cur)
1252 return NULL;
1254 ret = 0;
1255 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1256 cur->str.str[i] = &cur->data[ret];
1257 ret += GetLocaleInfoA(lcid, time_data[i], &cur->data[ret], size-ret);
1259 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1260 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1261 cur->wstr.wstr[i] = (wchar_t*)&cur->data[ret];
1262 ret += GetLocaleInfoEx(sname, time_data[i], (wchar_t*)&cur->data[ret],
1263 (size - ret) / sizeof(wchar_t)) * sizeof(wchar_t);
1265 #endif
1266 #if _MSVCR_VER >= 110
1267 cur->locname = (wchar_t*)&cur->data[ret];
1268 wcscpy((wchar_t*)&cur->data[ret], sname);
1269 #else
1270 cur->lcid = lcid;
1271 #endif
1272 cur->unk = 1;
1273 cur->refcount = 1;
1275 return cur;
1278 static pthreadlocinfo create_locinfo(int category,
1279 const char *locale, const threadlocinfo *old_locinfo)
1281 static const char collate[] = "COLLATE=";
1282 static const char ctype[] = "CTYPE=";
1283 static const char monetary[] = "MONETARY=";
1284 static const char numeric[] = "NUMERIC=";
1285 static const char time[] = "TIME=";
1287 pthreadlocinfo locinfo = NULL;
1288 unsigned short cp[6] = { 0 };
1289 const char *locale_name[6] = { 0 };
1290 WCHAR *locale_sname[6] = { 0 };
1291 int val, locale_len[6] = { 0 };
1292 char buf[256];
1293 BOOL sname_match;
1294 wchar_t wbuf[256];
1295 int i;
1297 TRACE("(%d %s)\n", category, locale);
1299 if(category<LC_MIN || category>LC_MAX || !locale)
1300 return NULL;
1302 if(locale[0]=='C' && !locale[1]) {
1303 locale_sname[0] = NULL;
1304 cp[0] = CP_ACP;
1305 } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
1306 const char *p;
1308 while(1) {
1309 locale += 3; /* LC_ */
1310 if(!memcmp(locale, collate, sizeof(collate)-1)) {
1311 i = LC_COLLATE;
1312 locale += sizeof(collate)-1;
1313 } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
1314 i = LC_CTYPE;
1315 locale += sizeof(ctype)-1;
1316 } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
1317 i = LC_MONETARY;
1318 locale += sizeof(monetary)-1;
1319 } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
1320 i = LC_NUMERIC;
1321 locale += sizeof(numeric)-1;
1322 } else if(!memcmp(locale, time, sizeof(time)-1)) {
1323 i = LC_TIME;
1324 locale += sizeof(time)-1;
1325 } else
1326 goto fail;
1328 p = strchr(locale, ';');
1329 if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
1330 locale_sname[i] = NULL;
1331 cp[i] = CP_ACP;
1332 } else {
1333 BOOL locale_found = FALSE;
1335 if(p) {
1336 memcpy(buf, locale, p-locale);
1337 buf[p-locale] = '\0';
1338 locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf);
1339 } else {
1340 locale_found = locale_to_sname(locale, &cp[i], &sname_match, wbuf);
1343 if(!locale_found || !(locale_sname[i] = wcsdup(wbuf)))
1344 goto fail;
1345 if(sname_match) {
1346 locale_name[i] = locale;
1347 locale_len[i] = p ? p-locale : strlen(locale);
1351 if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
1352 break;
1354 locale = p+1;
1356 } else {
1357 BOOL locale_found = locale_to_sname(locale, &cp[0], &sname_match, wbuf);
1359 if(!locale_found)
1360 return NULL;
1362 locale_sname[0] = wcsdup(wbuf);
1363 if(!locale_sname[0])
1364 return NULL;
1366 if(sname_match) {
1367 locale_name[0] = locale;
1368 locale_len[0] = strlen(locale);
1371 for(i=1; i<6; i++) {
1372 locale_sname[i] = wcsdup(locale_sname[0]);
1373 if(!locale_sname[i])
1374 goto fail;
1376 cp[i] = cp[0];
1377 locale_name[i] = locale_name[0];
1378 locale_len[i] = locale_len[0];
1382 for(i=1; i<6; i++) {
1383 #if _MSVCR_VER < 140
1384 if(i==LC_CTYPE && cp[i]==CP_UTF8) {
1385 #if _MSVCR_VER >= 110
1386 if(old_locinfo) {
1387 locale_sname[i] = wcsdup(old_locinfo->lc_name[i]);
1388 if (old_locinfo->lc_name[i] && !locale_sname[i])
1389 goto fail;
1391 #else
1392 int sname_size;
1393 if(old_locinfo && old_locinfo->lc_handle[i]) {
1394 sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0);
1395 locale_sname[i] = malloc(sname_size * sizeof(WCHAR));
1396 if(!locale_sname[i])
1397 goto fail;
1398 LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0);
1399 } else {
1400 locale_sname[i] = NULL;
1402 #endif
1404 locale_name[i] = NULL;
1405 locale_len[i] = 0;
1406 cp[i] = old_locinfo ? old_locinfo->lc_id[i].wCodePage : 0;
1408 #endif
1409 if(category!=LC_ALL && category!=i) {
1410 if(old_locinfo) {
1411 #if _MSVCR_VER >= 110
1412 locale_sname[i] = wcsdup(old_locinfo->lc_name[i]);
1413 if(old_locinfo->lc_name[i] && !locale_sname[i])
1414 goto fail;
1415 #else
1416 int sname_size;
1417 if(old_locinfo->lc_handle[i]) {
1418 sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0);
1419 locale_sname[i] = malloc(sname_size * sizeof(WCHAR));
1420 if(!locale_sname[i])
1421 goto fail;
1422 LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0);
1423 } else {
1424 locale_sname[i] = NULL;
1426 #endif
1427 cp[i] = old_locinfo->lc_id[i].wCodePage;
1428 } else {
1429 locale_sname[i] = NULL;
1430 cp[i] = 0;
1435 locinfo = malloc(sizeof(threadlocinfo));
1436 if(!locinfo)
1437 goto fail;
1439 memset(locinfo, 0, sizeof(threadlocinfo));
1440 locinfo->refcount = 1;
1442 if(locale_name[LC_COLLATE] &&
1443 !init_category_name(locale_name[LC_COLLATE],
1444 locale_len[LC_COLLATE], locinfo, LC_COLLATE)) {
1445 goto fail;
1448 if(!category_needs_update(LC_COLLATE, old_locinfo,
1449 locale_sname[LC_COLLATE], cp[LC_COLLATE])) {
1450 copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE);
1451 locinfo->lc_collate_cp = old_locinfo->lc_collate_cp;
1452 } else if(locale_sname[LC_COLLATE]) {
1453 if(!update_threadlocinfo_category(locale_sname[LC_COLLATE],
1454 cp[LC_COLLATE], locinfo, LC_COLLATE)) {
1455 goto fail;
1458 locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage;
1459 } else {
1460 if(!init_category_name("C", 1, locinfo, LC_COLLATE)) {
1461 goto fail;
1465 if(locale_name[LC_CTYPE] &&
1466 !init_category_name(locale_name[LC_CTYPE],
1467 locale_len[LC_CTYPE], locinfo, LC_CTYPE)) {
1468 goto fail;
1471 if(!category_needs_update(LC_CTYPE, old_locinfo,
1472 locale_sname[LC_CTYPE], cp[LC_CTYPE])) {
1473 copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE);
1474 locinfo->lc_codepage = old_locinfo->lc_codepage;
1475 locinfo->lc_clike = old_locinfo->lc_clike;
1476 locinfo->mb_cur_max = old_locinfo->mb_cur_max;
1477 locinfo->ctype1 = old_locinfo->ctype1;
1478 locinfo->ctype1_refcount = old_locinfo->ctype1_refcount;
1479 locinfo->pctype = old_locinfo->pctype;
1480 locinfo->pclmap = old_locinfo->pclmap;
1481 locinfo->pcumap = old_locinfo->pcumap;
1482 if(locinfo->ctype1_refcount)
1483 InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
1484 } else if(locale_sname[LC_CTYPE]) {
1485 CPINFO cp_info;
1486 int j;
1488 if(!update_threadlocinfo_category(locale_sname[LC_CTYPE],
1489 cp[LC_CTYPE], locinfo, LC_CTYPE)) {
1490 goto fail;
1493 locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage;
1494 locinfo->lc_clike = 1;
1495 if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) {
1496 goto fail;
1498 locinfo->mb_cur_max = cp_info.MaxCharSize;
1500 locinfo->ctype1_refcount = malloc(sizeof(int));
1501 if(!locinfo->ctype1_refcount) {
1502 goto fail;
1504 *locinfo->ctype1_refcount = 1;
1506 locinfo->ctype1 = malloc(sizeof(short[257]));
1507 locinfo->pclmap = malloc(sizeof(char[256]));
1508 locinfo->pcumap = malloc(sizeof(char[256]));
1509 if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) {
1510 goto fail;
1513 locinfo->ctype1[0] = 0;
1514 locinfo->pctype = locinfo->ctype1+1;
1516 buf[1] = buf[2] = '\0';
1517 for(i=1; i<257; i++) {
1518 buf[0] = i-1;
1520 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1521 locinfo->ctype1[i] = 0;
1523 GetStringTypeA(locinfo->lc_handle[LC_CTYPE], CT_CTYPE1, buf,
1524 1, locinfo->ctype1+i);
1527 for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
1528 for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
1529 locinfo->ctype1[j+1] |= _LEADBYTE;
1531 for(i=0; i<256; i++) {
1532 if(locinfo->pctype[i] & _LEADBYTE)
1533 buf[i] = ' ';
1534 else
1535 buf[i] = i;
1538 LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
1539 (char*)locinfo->pclmap, 256);
1540 LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
1541 (char*)locinfo->pcumap, 256);
1542 } else {
1543 locinfo->lc_clike = 1;
1544 locinfo->mb_cur_max = 1;
1545 locinfo->pctype = MSVCRT__ctype+1;
1546 locinfo->pclmap = cloc_clmap;
1547 locinfo->pcumap = cloc_cumap;
1548 if(!init_category_name("C", 1, locinfo, LC_CTYPE)) {
1549 goto fail;
1553 if(!category_needs_update(LC_MONETARY, old_locinfo,
1554 locale_sname[LC_MONETARY], cp[LC_MONETARY]) &&
1555 !category_needs_update(LC_NUMERIC, old_locinfo,
1556 locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) {
1557 locinfo->lconv = old_locinfo->lconv;
1558 locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount;
1559 if(locinfo->lconv_intl_refcount)
1560 InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
1561 } else if(locale_sname[LC_MONETARY] || locale_sname[LC_NUMERIC]) {
1562 locinfo->lconv = malloc(sizeof(struct lconv));
1563 locinfo->lconv_intl_refcount = malloc(sizeof(int));
1564 if(!locinfo->lconv || !locinfo->lconv_intl_refcount) {
1565 free(locinfo->lconv);
1566 free(locinfo->lconv_intl_refcount);
1567 locinfo->lconv = NULL;
1568 locinfo->lconv_intl_refcount = NULL;
1569 goto fail;
1571 memset(locinfo->lconv, 0, sizeof(struct lconv));
1572 *locinfo->lconv_intl_refcount = 1;
1573 } else {
1574 locinfo->lconv = &cloc_lconv;
1577 if(locale_name[LC_MONETARY] &&
1578 !init_category_name(locale_name[LC_MONETARY],
1579 locale_len[LC_MONETARY], locinfo, LC_MONETARY)) {
1580 goto fail;
1583 if(!category_needs_update(LC_MONETARY, old_locinfo,
1584 locale_sname[LC_MONETARY], cp[LC_MONETARY])) {
1585 copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY);
1586 locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount;
1587 if(locinfo->lconv_mon_refcount)
1588 InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
1589 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1590 locinfo->lconv->int_curr_symbol = old_locinfo->lconv->int_curr_symbol;
1591 locinfo->lconv->currency_symbol = old_locinfo->lconv->currency_symbol;
1592 locinfo->lconv->mon_decimal_point = old_locinfo->lconv->mon_decimal_point;
1593 locinfo->lconv->mon_thousands_sep = old_locinfo->lconv->mon_thousands_sep;
1594 locinfo->lconv->mon_grouping = old_locinfo->lconv->mon_grouping;
1595 locinfo->lconv->positive_sign = old_locinfo->lconv->positive_sign;
1596 locinfo->lconv->negative_sign = old_locinfo->lconv->negative_sign;
1597 locinfo->lconv->int_frac_digits = old_locinfo->lconv->int_frac_digits;
1598 locinfo->lconv->frac_digits = old_locinfo->lconv->frac_digits;
1599 locinfo->lconv->p_cs_precedes = old_locinfo->lconv->p_cs_precedes;
1600 locinfo->lconv->p_sep_by_space = old_locinfo->lconv->p_sep_by_space;
1601 locinfo->lconv->n_cs_precedes = old_locinfo->lconv->n_cs_precedes;
1602 locinfo->lconv->n_sep_by_space = old_locinfo->lconv->n_sep_by_space;
1603 locinfo->lconv->p_sign_posn = old_locinfo->lconv->p_sign_posn;
1604 locinfo->lconv->n_sign_posn = old_locinfo->lconv->n_sign_posn;
1605 #if _MSVCR_VER >= 100
1606 locinfo->lconv->_W_int_curr_symbol = old_locinfo->lconv->_W_int_curr_symbol;
1607 locinfo->lconv->_W_currency_symbol = old_locinfo->lconv->_W_currency_symbol;
1608 locinfo->lconv->_W_mon_decimal_point = old_locinfo->lconv->_W_mon_decimal_point;
1609 locinfo->lconv->_W_mon_thousands_sep = old_locinfo->lconv->_W_mon_thousands_sep;
1610 locinfo->lconv->_W_positive_sign = old_locinfo->lconv->_W_positive_sign;
1611 locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign;
1612 #endif
1614 } else if(locale_sname[LC_MONETARY]) {
1615 if(!update_threadlocinfo_category(locale_sname[LC_MONETARY],
1616 cp[LC_MONETARY], locinfo, LC_MONETARY)) {
1617 goto fail;
1620 locinfo->lconv_mon_refcount = malloc(sizeof(int));
1621 if(!locinfo->lconv_mon_refcount) {
1622 goto fail;
1625 *locinfo->lconv_mon_refcount = 1;
1627 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL
1628 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1629 i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
1630 if(i && (locinfo->lconv->int_curr_symbol = malloc(i)))
1631 WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->int_curr_symbol, i, NULL, NULL);
1632 else {
1633 goto fail;
1636 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY
1637 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1638 i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
1639 if(i && (locinfo->lconv->currency_symbol = malloc(i)))
1640 WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->currency_symbol, i, NULL, NULL);
1641 else {
1642 goto fail;
1645 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP
1646 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1647 i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
1648 if(i && (locinfo->lconv->mon_decimal_point = malloc(i)))
1649 WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_decimal_point, i, NULL, NULL);
1650 else {
1651 goto fail;
1654 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1655 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1656 i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
1657 if(i && (locinfo->lconv->mon_thousands_sep = malloc(i)))
1658 WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_thousands_sep, i, NULL, NULL);
1659 else {
1660 goto fail;
1663 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONGROUPING
1664 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1665 WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buf, 256, NULL, NULL);
1666 if(i>1)
1667 i = i/2 + (buf[i-2]=='0'?0:1);
1668 if(i && (locinfo->lconv->mon_grouping = malloc(i))) {
1669 for(i=0; buf[i+1]==';'; i+=2)
1670 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1671 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1672 if(buf[i] != '0')
1673 locinfo->lconv->mon_grouping[i/2+1] = 127;
1674 } else {
1675 goto fail;
1678 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN
1679 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1680 i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
1681 if(i && (locinfo->lconv->positive_sign = malloc(i)))
1682 WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->positive_sign, i, NULL, NULL);
1683 else {
1684 goto fail;
1687 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN
1688 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1689 i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL);
1690 if(i && (locinfo->lconv->negative_sign = malloc(i)))
1691 WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->negative_sign, i, NULL, NULL);
1692 else {
1693 goto fail;
1696 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IINTLCURRDIGITS
1697 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1698 locinfo->lconv->int_frac_digits = val;
1699 else {
1700 goto fail;
1703 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_ICURRDIGITS
1704 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1705 locinfo->lconv->frac_digits = val;
1706 else {
1707 goto fail;
1710 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSYMPRECEDES
1711 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1712 locinfo->lconv->p_cs_precedes = val;
1713 else {
1714 goto fail;
1717 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSEPBYSPACE
1718 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1719 locinfo->lconv->p_sep_by_space = val;
1720 else {
1721 goto fail;
1724 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSYMPRECEDES
1725 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1726 locinfo->lconv->n_cs_precedes = val;
1727 else {
1728 goto fail;
1731 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSEPBYSPACE
1732 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1733 locinfo->lconv->n_sep_by_space = val;
1734 else {
1735 goto fail;
1738 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSIGNPOSN
1739 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1740 locinfo->lconv->p_sign_posn = val;
1741 else {
1742 goto fail;
1745 if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSIGNPOSN
1746 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1747 locinfo->lconv->n_sign_posn = val;
1748 else {
1749 goto fail;
1752 #if _MSVCR_VER >= 100
1753 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL
1754 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1755 if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t))))
1756 memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t));
1757 else {
1758 goto fail;
1761 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY
1762 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1763 if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t))))
1764 memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t));
1765 else {
1766 goto fail;
1769 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP
1770 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1771 if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t))))
1772 memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t));
1773 else {
1774 goto fail;
1777 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1778 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1779 if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t))))
1780 memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t));
1781 else {
1782 goto fail;
1785 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN
1786 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1787 if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t))))
1788 memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t));
1789 else {
1790 goto fail;
1793 i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN
1794 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1795 if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t))))
1796 memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t));
1797 else {
1798 goto fail;
1800 #endif
1801 } else {
1802 if (locinfo->lconv != &cloc_lconv) {
1803 locinfo->lconv->int_curr_symbol = cloc_lconv.int_curr_symbol;
1804 locinfo->lconv->currency_symbol = cloc_lconv.currency_symbol;
1805 locinfo->lconv->mon_decimal_point = cloc_lconv.mon_decimal_point;
1806 locinfo->lconv->mon_thousands_sep = cloc_lconv.mon_thousands_sep;
1807 locinfo->lconv->mon_grouping = cloc_lconv.mon_grouping;
1808 locinfo->lconv->positive_sign = cloc_lconv.positive_sign;
1809 locinfo->lconv->negative_sign = cloc_lconv.negative_sign;
1810 locinfo->lconv->int_frac_digits = cloc_lconv.int_frac_digits;
1811 locinfo->lconv->frac_digits = cloc_lconv.frac_digits;
1812 locinfo->lconv->p_cs_precedes = cloc_lconv.p_cs_precedes;
1813 locinfo->lconv->p_sep_by_space = cloc_lconv.p_sep_by_space;
1814 locinfo->lconv->n_cs_precedes = cloc_lconv.n_cs_precedes;
1815 locinfo->lconv->n_sep_by_space = cloc_lconv.n_sep_by_space;
1816 locinfo->lconv->p_sign_posn = cloc_lconv.p_sign_posn;
1817 locinfo->lconv->n_sign_posn = cloc_lconv.n_sign_posn;
1819 #if _MSVCR_VER >= 100
1820 locinfo->lconv->_W_int_curr_symbol = cloc_lconv._W_int_curr_symbol;
1821 locinfo->lconv->_W_currency_symbol = cloc_lconv._W_currency_symbol;
1822 locinfo->lconv->_W_mon_decimal_point = cloc_lconv._W_mon_decimal_point;
1823 locinfo->lconv->_W_mon_thousands_sep = cloc_lconv._W_mon_thousands_sep;
1824 locinfo->lconv->_W_positive_sign = cloc_lconv._W_positive_sign;
1825 locinfo->lconv->_W_negative_sign = cloc_lconv._W_negative_sign;
1826 #endif
1829 if(!init_category_name("C", 1, locinfo, LC_MONETARY)) {
1830 goto fail;
1834 if(locale_name[LC_NUMERIC] &&
1835 !init_category_name(locale_name[LC_NUMERIC],
1836 locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1837 goto fail;
1840 if(!category_needs_update(LC_NUMERIC, old_locinfo,
1841 locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) {
1842 copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC);
1843 locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount;
1844 if(locinfo->lconv_num_refcount)
1845 InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
1846 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1847 locinfo->lconv->decimal_point = old_locinfo->lconv->decimal_point;
1848 locinfo->lconv->thousands_sep = old_locinfo->lconv->thousands_sep;
1849 locinfo->lconv->grouping = old_locinfo->lconv->grouping;
1850 #if _MSVCR_VER >= 100
1851 locinfo->lconv->_W_decimal_point = old_locinfo->lconv->_W_decimal_point;
1852 locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep;
1853 #endif
1855 } else if(locale_sname[LC_NUMERIC]) {
1856 if(!update_threadlocinfo_category(locale_sname[LC_NUMERIC],
1857 cp[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1858 goto fail;
1861 locinfo->lconv_num_refcount = malloc(sizeof(int));
1862 if(!locinfo->lconv_num_refcount) {
1863 goto fail;
1866 *locinfo->lconv_num_refcount = 1;
1868 i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL
1869 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1870 i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL);
1871 if(i && (locinfo->lconv->decimal_point = malloc(i)))
1872 WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->decimal_point, i, NULL, NULL);
1873 else {
1874 goto fail;
1877 i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND
1878 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1879 i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL);
1880 if(i && (locinfo->lconv->thousands_sep = malloc(i)))
1881 WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->thousands_sep, i, NULL, NULL);
1882 else {
1883 goto fail;
1886 i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SGROUPING
1887 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1888 WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, buf, 256, NULL, NULL);
1889 if(i>1)
1890 i = i/2 + (buf[i-2]=='0'?0:1);
1891 if(i && (locinfo->lconv->grouping = malloc(i))) {
1892 for(i=0; buf[i+1]==';'; i+=2)
1893 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1894 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1895 if(buf[i] != '0')
1896 locinfo->lconv->grouping[i/2+1] = 127;
1897 } else {
1898 goto fail;
1901 #if _MSVCR_VER >= 100
1902 i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL
1903 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1904 if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t))))
1905 memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t));
1906 else {
1907 goto fail;
1910 i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND
1911 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1912 if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t))))
1913 memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t));
1914 else {
1915 goto fail;
1917 #endif
1918 } else {
1919 if (locinfo->lconv != &cloc_lconv) {
1920 locinfo->lconv->decimal_point = cloc_lconv.decimal_point;
1921 locinfo->lconv->thousands_sep = cloc_lconv.thousands_sep;
1922 locinfo->lconv->grouping = cloc_lconv.grouping;
1924 #if _MSVCR_VER >= 100
1925 locinfo->lconv->_W_decimal_point = cloc_lconv._W_decimal_point;
1926 locinfo->lconv->_W_thousands_sep = cloc_lconv._W_thousands_sep;
1927 #endif
1930 if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) {
1931 goto fail;
1935 if(locale_name[LC_TIME] &&
1936 !init_category_name(locale_name[LC_TIME],
1937 locale_len[LC_TIME], locinfo, LC_TIME)) {
1938 goto fail;
1941 if(!category_needs_update(LC_TIME, old_locinfo,
1942 locale_sname[LC_TIME], cp[LC_TIME])) {
1943 copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME);
1944 locinfo->lc_time_curr = old_locinfo->lc_time_curr;
1945 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1946 } else if(locale_sname[LC_TIME]) {
1947 if(!update_threadlocinfo_category(locale_sname[LC_TIME],
1948 cp[LC_TIME], locinfo, LC_TIME)) {
1949 goto fail;
1952 locinfo->lc_time_curr = create_time_data(locale_sname[LC_TIME]);
1953 if(!locinfo->lc_time_curr) {
1954 goto fail;
1956 } else {
1957 if(!init_category_name("C", 1, locinfo, LC_TIME)) {
1958 goto fail;
1960 locinfo->lc_time_curr = &cloc_time_data;
1961 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1964 for (i = 0; i < LC_MAX; i++)
1965 free(locale_sname[i]);
1967 return locinfo;
1969 fail:
1970 free_locinfo(locinfo);
1972 for (i = 0; i < LC_MAX; i++)
1973 free(locale_sname[i]);
1975 return NULL;
1978 /*********************************************************************
1979 * _create_locale (MSVCRT.@)
1981 _locale_t CDECL _create_locale(int category, const char *locale)
1983 _locale_t loc;
1985 loc = malloc(sizeof(_locale_tstruct));
1986 if(!loc)
1987 return NULL;
1989 loc->locinfo = create_locinfo(category, locale, NULL);
1990 if(!loc->locinfo) {
1991 free(loc);
1992 return NULL;
1995 loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[LC_CTYPE].wCodePage,
1996 loc->locinfo->lc_handle[LC_CTYPE], NULL);
1997 if(!loc->mbcinfo) {
1998 free_locinfo(loc->locinfo);
1999 free(loc);
2000 return NULL;
2002 return loc;
2005 #if _MSVCR_VER >= 110
2006 /*********************************************************************
2007 * _wcreate_locale (MSVCR110.@)
2009 _locale_t CDECL _wcreate_locale(int category, const wchar_t *locale)
2011 _locale_t loc;
2012 size_t len;
2013 char *str;
2015 if(category<LC_MIN || category>LC_MAX || !locale)
2016 return NULL;
2018 len = wcstombs(NULL, locale, 0);
2019 if(len == -1)
2020 return NULL;
2021 if(!(str = malloc(++len)))
2022 return NULL;
2023 wcstombs(str, locale, len);
2025 loc = _create_locale(category, str);
2027 free(str);
2028 return loc;
2030 #endif
2032 /*********************************************************************
2033 * setlocale (MSVCRT.@)
2035 char* CDECL setlocale(int category, const char* locale)
2037 thread_data_t *data = msvcrt_get_thread_data();
2038 pthreadlocinfo locinfo = get_locinfo(), newlocinfo;
2040 if(category<LC_MIN || category>LC_MAX)
2041 return NULL;
2043 if(!locale) {
2044 if(category == LC_ALL)
2045 return construct_lc_all(locinfo);
2047 return locinfo->lc_category[category].locale;
2050 newlocinfo = create_locinfo(category, locale, locinfo);
2051 if(!newlocinfo) {
2052 WARN("%d %s failed\n", category, locale);
2053 return NULL;
2056 if(locale[0] != 'C' || locale[1] != '\0')
2057 initial_locale = FALSE;
2059 if(data->locale_flags & LOCALE_THREAD)
2061 if(data->locale_flags & LOCALE_FREE)
2062 free_locinfo(data->locinfo);
2063 data->locinfo = newlocinfo;
2065 else
2067 int i;
2069 _lock_locales();
2070 free_locinfo(MSVCRT_locale->locinfo);
2071 MSVCRT_locale->locinfo = newlocinfo;
2073 MSVCRT___lc_codepage = newlocinfo->lc_codepage;
2074 MSVCRT___lc_collate_cp = newlocinfo->lc_collate_cp;
2075 MSVCRT___mb_cur_max = newlocinfo->mb_cur_max;
2076 MSVCRT__pctype = newlocinfo->pctype;
2077 for(i=LC_MIN; i<=LC_MAX; i++)
2078 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2079 _unlock_locales();
2080 update_thread_locale(data);
2083 if(category == LC_ALL)
2084 return construct_lc_all(data->locinfo);
2086 return data->locinfo->lc_category[category].locale;
2089 /*********************************************************************
2090 * _wsetlocale (MSVCRT.@)
2092 wchar_t* CDECL _wsetlocale(int category, const wchar_t* wlocale)
2094 static wchar_t current_lc_all[MAX_LOCALE_LENGTH];
2096 char *locale = NULL;
2097 const char *ret;
2098 size_t len;
2100 if(wlocale) {
2101 len = wcstombs(NULL, wlocale, 0);
2102 if(len == -1)
2103 return NULL;
2105 locale = malloc(++len);
2106 if(!locale)
2107 return NULL;
2109 wcstombs(locale, wlocale, len);
2112 _lock_locales();
2113 ret = setlocale(category, locale);
2114 free(locale);
2116 if(ret && mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
2117 ret = NULL;
2119 _unlock_locales();
2120 return ret ? current_lc_all : NULL;
2123 #if _MSVCR_VER >= 80
2124 /*********************************************************************
2125 * _configthreadlocale (MSVCR80.@)
2127 int CDECL _configthreadlocale(int type)
2129 thread_data_t *data = msvcrt_get_thread_data();
2130 int ret;
2132 ret = (data->locale_flags & LOCALE_THREAD ? _ENABLE_PER_THREAD_LOCALE :
2133 _DISABLE_PER_THREAD_LOCALE);
2135 if(type == _ENABLE_PER_THREAD_LOCALE)
2136 data->locale_flags |= LOCALE_THREAD;
2137 else if(type == _DISABLE_PER_THREAD_LOCALE)
2138 data->locale_flags &= ~LOCALE_THREAD;
2139 else if(type)
2140 ret = -1;
2142 return ret;
2144 #endif
2146 BOOL msvcrt_init_locale(void)
2148 int i;
2150 _lock_locales();
2151 MSVCRT_locale = _create_locale(0, "C");
2152 _unlock_locales();
2153 if(!MSVCRT_locale)
2154 return FALSE;
2156 MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
2157 MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
2158 MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
2159 MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
2160 for(i=LC_MIN; i<=LC_MAX; i++)
2161 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2162 _setmbcp(_MB_CP_ANSI);
2163 return TRUE;
2166 #if _MSVCR_VER >= 120
2167 /*********************************************************************
2168 * wctrans (MSVCR120.@)
2170 wctrans_t CDECL wctrans(const char *property)
2172 static const char str_tolower[] = "tolower";
2173 static const char str_toupper[] = "toupper";
2175 if(!strcmp(property, str_tolower))
2176 return 2;
2177 if(!strcmp(property, str_toupper))
2178 return 1;
2179 return 0;
2181 #endif