Release 6.15.
[wine.git] / dlls / msvcrt / locale.c
blob5a4a701dfc615baf5d9bb2ac5de56f3f14fa26b6
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 __lc_time_data cloc_time_data =
56 {{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
57 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
58 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
59 "January", "February", "March", "April", "May", "June", "July",
60 "August", "September", "October", "November", "December",
61 "AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"}},
62 #if _MSVCR_VER < 110
63 MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
64 #endif
65 1, 0,
66 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
67 {{L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat",
68 L"Sunday", L"Monday", L"Tuesday", L"Wednesday", L"Thursday", L"Friday", L"Saturday",
69 L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec",
70 L"January", L"February", L"March", L"April", L"May", L"June", L"July",
71 L"August", L"September", L"October", L"November", L"December",
72 L"AM", L"PM", L"MM/dd/yy", L"dddd, MMMM dd, yyyy", L"HH:mm:ss"}},
73 #endif
74 #if _MSVCR_VER >= 110
75 L"en-US",
76 #endif
79 static const unsigned char cloc_clmap[256] =
81 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
82 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
83 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
84 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
85 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
86 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
87 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
88 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
89 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
90 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
91 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
92 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
93 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
94 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
95 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
96 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
97 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
98 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
99 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
100 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
101 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
102 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
103 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
104 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
105 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
106 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
107 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
108 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
109 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
110 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
111 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
112 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
115 static const unsigned char cloc_cumap[256] =
117 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
118 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
119 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
120 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
121 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
122 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
123 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
124 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
125 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
126 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
127 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
128 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
129 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
130 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
131 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
132 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
133 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
134 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
135 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
136 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
137 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
138 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
139 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
140 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
141 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
142 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
143 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
144 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
145 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
146 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
147 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
148 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
151 static char empty[] = "";
152 static char cloc_dec_point[] = ".";
153 #if _MSVCR_VER >= 100
154 static wchar_t emptyW[] = L"";
155 static wchar_t cloc_dec_pointW[] = L".";
156 #endif
157 static struct lconv cloc_lconv =
159 cloc_dec_point, empty, empty, empty, empty, empty, empty, empty, empty, empty,
160 CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX,
161 #if _MSVCR_VER >= 100
162 cloc_dec_pointW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW
163 #endif
166 /* Friendly country strings & language names abbreviations. */
167 static const char * const _country_synonyms[] =
169 "american", "enu",
170 "american english", "enu",
171 "american-english", "enu",
172 "english-american", "enu",
173 "english-us", "enu",
174 "english-usa", "enu",
175 "us", "enu",
176 "usa", "enu",
177 "australian", "ena",
178 "english-aus", "ena",
179 "belgian", "nlb",
180 "french-belgian", "frb",
181 "canadian", "enc",
182 "english-can", "enc",
183 "french-canadian", "frc",
184 "chinese", "chs",
185 "chinese-simplified", "chs",
186 "chinese-traditional", "cht",
187 "dutch-belgian", "nlb",
188 "english-nz", "enz",
189 "uk", "eng",
190 "english-uk", "eng",
191 "french-swiss", "frs",
192 "swiss", "des",
193 "german-swiss", "des",
194 "italian-swiss", "its",
195 "german-austrian", "dea",
196 "portuguese", "ptb",
197 "portuguese-brazil", "ptb",
198 "spanish-mexican", "esm",
199 "norwegian-bokmal", "nor",
200 "norwegian-nynorsk", "non",
201 "spanish-modern", "esn"
204 /* INTERNAL: Map a synonym to an ISO code */
205 static void remap_synonym(char *name)
207 unsigned int i;
208 for (i = 0; i < ARRAY_SIZE(_country_synonyms); i += 2)
210 if (!_stricmp(_country_synonyms[i],name))
212 TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
213 strcpy(name, _country_synonyms[i+1]);
214 return;
219 /* Note: Flags are weighted in order of matching importance */
220 #define FOUND_SNAME 0x4
221 #define FOUND_LANGUAGE 0x2
222 #define FOUND_COUNTRY 0x1
224 typedef struct {
225 char search_language[MAX_ELEM_LEN];
226 char search_country[MAX_ELEM_LEN];
227 DWORD found_codepage;
228 unsigned int match_flags;
229 LANGID found_lang_id;
230 BOOL allow_sname;
231 } locale_search_t;
233 #define CONTINUE_LOOKING TRUE
234 #define STOP_LOOKING FALSE
236 /* INTERNAL: Get and compare locale info with a given string */
237 static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp, BOOL exact)
239 int len;
241 if(!cmp[0])
242 return 0;
244 buff[0] = 0;
245 GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN);
246 if (!buff[0])
247 return 0;
249 /* Partial matches are only allowed on language/country names */
250 len = strlen(cmp);
251 if(exact || len<=3)
252 return !_stricmp(cmp, buff);
253 else
254 return !_strnicmp(cmp, buff, len);
257 static BOOL CALLBACK
258 find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam)
260 locale_search_t *res = (locale_search_t *)lParam;
261 const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
262 char buff[MAX_ELEM_LEN];
263 unsigned int flags = 0;
265 if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
266 return CONTINUE_LOOKING;
268 #if _MSVCR_VER >= 110
269 if (res->allow_sname && compare_info(lcid,LOCALE_SNAME,buff,res->search_language, TRUE))
271 TRACE(":Found locale: %s->%s\n", res->search_language, buff);
272 res->match_flags = FOUND_SNAME;
273 res->found_lang_id = LangID;
274 return STOP_LOOKING;
276 #endif
278 /* Check Language */
279 if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) ||
280 compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) ||
281 compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE))
283 TRACE(":Found language: %s->%s\n", res->search_language, buff);
284 flags |= FOUND_LANGUAGE;
286 else if (res->match_flags & FOUND_LANGUAGE)
288 return CONTINUE_LOOKING;
291 /* Check Country */
292 if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) ||
293 compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) ||
294 compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE))
296 TRACE("Found country:%s->%s\n", res->search_country, buff);
297 flags |= FOUND_COUNTRY;
299 else if (!flags && (res->match_flags & FOUND_COUNTRY))
301 return CONTINUE_LOOKING;
304 if (flags > res->match_flags)
306 /* Found a better match than previously */
307 res->match_flags = flags;
308 res->found_lang_id = LangID;
310 if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY)) ==
311 (FOUND_LANGUAGE | FOUND_COUNTRY))
313 TRACE(":found exact locale match\n");
314 return STOP_LOOKING;
316 return CONTINUE_LOOKING;
319 /* Internal: Find the LCID for a locale specification */
320 LCID locale_to_LCID(const char *locale, unsigned short *codepage, BOOL *sname)
322 thread_data_t *data = msvcrt_get_thread_data();
323 const char *cp, *region;
324 BOOL is_sname = FALSE;
325 DWORD locale_cp;
326 LCID lcid;
328 if (!strcmp(locale, data->cached_locale)) {
329 if (codepage)
330 *codepage = data->cached_cp;
331 if (sname)
332 *sname = data->cached_sname;
333 return data->cached_lcid;
336 cp = strchr(locale, '.');
337 region = strchr(locale, '_');
339 if(!locale[0] || (cp == locale && !region)) {
340 lcid = GetUserDefaultLCID();
341 } else {
342 locale_search_t search;
344 memset(&search, 0, sizeof(locale_search_t));
345 lstrcpynA(search.search_language, locale, MAX_ELEM_LEN);
346 if(region) {
347 lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN);
348 if(region-locale < MAX_ELEM_LEN)
349 search.search_language[region-locale] = '\0';
350 } else
351 search.search_country[0] = '\0';
353 if(cp) {
354 if(region && cp-region-1<MAX_ELEM_LEN)
355 search.search_country[cp-region-1] = '\0';
356 if(cp-locale < MAX_ELEM_LEN)
357 search.search_language[cp-locale] = '\0';
360 if(!cp && !region)
362 remap_synonym(search.search_language);
363 search.allow_sname = TRUE;
366 if(!_stricmp(search.search_country, "China"))
367 strcpy(search.search_country, "People's Republic of China");
369 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
370 (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
371 (LONG_PTR)&search);
373 if (!search.match_flags)
374 return -1;
376 /* If we were given something that didn't match, fail */
377 if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE)))
378 return -1;
379 if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
380 return -1;
382 lcid = MAKELCID(search.found_lang_id, SORT_DEFAULT);
383 is_sname = (search.match_flags & FOUND_SNAME) != 0;
386 /* Obtain code page */
387 if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) {
388 GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
389 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
390 if (!locale_cp)
391 locale_cp = GetACP();
392 } else if (!_strnicmp(cp, ".OCP", 4)) {
393 GetLocaleInfoW(lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
394 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
395 #if _MSVCR_VER >= 140
396 } else if (!_strnicmp(cp, ".UTF-8", 6)
397 || !_strnicmp(cp, ".UTF8", 5)) {
398 locale_cp = CP_UTF8;
399 #endif
400 } else {
401 locale_cp = atoi(cp + 1);
403 if (!IsValidCodePage(locale_cp))
404 return -1;
406 if (!locale_cp)
407 return -1;
409 if (codepage)
410 *codepage = locale_cp;
411 if (sname)
412 *sname = is_sname;
414 if (strlen(locale) < sizeof(data->cached_locale)) {
415 strcpy(data->cached_locale, locale);
416 data->cached_lcid = lcid;
417 data->cached_cp = locale_cp;
418 data->cached_sname = is_sname;
421 return lcid;
424 static void copy_threadlocinfo_category(pthreadlocinfo locinfo,
425 const threadlocinfo *old_locinfo, int category)
427 locinfo->lc_handle[category] = old_locinfo->lc_handle[category];
428 locinfo->lc_id[category] = old_locinfo->lc_id[category];
429 if(!locinfo->lc_category[category].locale) {
430 locinfo->lc_category[category].locale = old_locinfo->lc_category[category].locale;
431 locinfo->lc_category[category].refcount = old_locinfo->lc_category[category].refcount;
432 InterlockedIncrement(locinfo->lc_category[category].refcount);
434 #if _MSVCR_VER >= 110
435 locinfo->lc_name[category] = old_locinfo->lc_name[category];
436 locinfo->lc_category[category].wrefcount = old_locinfo->lc_category[category].wrefcount;
437 if(locinfo->lc_category[category].wrefcount)
438 InterlockedIncrement(locinfo->lc_category[category].wrefcount);
439 #endif
442 static BOOL init_category_name(const char *name, int len,
443 pthreadlocinfo locinfo, int category)
445 locinfo->lc_category[category].locale = malloc(len+1);
446 locinfo->lc_category[category].refcount = malloc(sizeof(int));
447 if(!locinfo->lc_category[category].locale
448 || !locinfo->lc_category[category].refcount) {
449 free(locinfo->lc_category[category].locale);
450 free(locinfo->lc_category[category].refcount);
451 locinfo->lc_category[category].locale = NULL;
452 locinfo->lc_category[category].refcount = NULL;
453 return FALSE;
456 memcpy(locinfo->lc_category[category].locale, name, len);
457 locinfo->lc_category[category].locale[len] = 0;
458 *locinfo->lc_category[category].refcount = 1;
459 return TRUE;
462 #if _MSVCR_VER >= 110
463 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat)
465 LCID lcid = locinfo->lc_handle[cat];
466 WCHAR buf[100];
467 int len;
469 locinfo->lc_category[cat].wrefcount = malloc(sizeof(int));
470 if(!locinfo->lc_category[cat].wrefcount)
471 return FALSE;
472 *locinfo->lc_category[cat].wrefcount = 1;
474 len = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME
475 |LOCALE_NOUSEROVERRIDE, buf, 100);
476 if(!len) return FALSE;
478 if(LocaleNameToLCID(buf, 0) != lcid)
479 len = LCIDToLocaleName(lcid, buf, 100, 0);
481 if(!len || !(locinfo->lc_name[cat] = malloc(len*sizeof(wchar_t))))
482 return FALSE;
484 memcpy(locinfo->lc_name[cat], buf, len*sizeof(wchar_t));
485 return TRUE;
487 #else
488 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat)
490 return TRUE;
492 #endif
494 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
495 static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp,
496 pthreadlocinfo locinfo, int category)
498 char buf[256], *p;
500 if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) {
501 p = buf;
503 locinfo->lc_id[category].wLanguage = 0;
504 while(*p) {
505 locinfo->lc_id[category].wLanguage *= 16;
507 if(*p <= '9')
508 locinfo->lc_id[category].wLanguage += *p-'0';
509 else
510 locinfo->lc_id[category].wLanguage += *p-'a'+10;
512 p++;
515 locinfo->lc_id[category].wCountry =
516 locinfo->lc_id[category].wLanguage;
519 locinfo->lc_id[category].wCodePage = cp;
521 locinfo->lc_handle[category] = lcid;
523 set_lc_locale_name(locinfo, category);
525 if(!locinfo->lc_category[category].locale) {
526 int len = 0;
528 len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE
529 |LOCALE_NOUSEROVERRIDE, buf, 256);
530 buf[len-1] = '_';
531 len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY
532 |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len);
533 buf[len-1] = '.';
534 sprintf(buf+len, "%d", cp);
535 len += strlen(buf+len);
537 return init_category_name(buf, len, locinfo, category);
540 return TRUE;
543 /*********************************************************************
544 * _lock_locales (UCRTBASE.@)
546 void CDECL _lock_locales(void)
548 _lock(_SETLOCALE_LOCK);
551 /*********************************************************************
552 * _unlock_locales (UCRTBASE.@)
554 void CDECL _unlock_locales(void)
556 _unlock(_SETLOCALE_LOCK);
559 static void CDECL grab_locinfo(pthreadlocinfo locinfo)
561 int i;
563 InterlockedIncrement(&locinfo->refcount);
564 for(i=LC_MIN+1; i<=LC_MAX; i++)
566 InterlockedIncrement(locinfo->lc_category[i].refcount);
567 if(locinfo->lc_category[i].wrefcount)
568 InterlockedIncrement(locinfo->lc_category[i].wrefcount);
570 if(locinfo->lconv_intl_refcount)
571 InterlockedIncrement(locinfo->lconv_intl_refcount);
572 if(locinfo->lconv_num_refcount)
573 InterlockedIncrement(locinfo->lconv_num_refcount);
574 if(locinfo->lconv_mon_refcount)
575 InterlockedIncrement(locinfo->lconv_mon_refcount);
576 if(locinfo->ctype1_refcount)
577 InterlockedIncrement(locinfo->ctype1_refcount);
578 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
581 static void CDECL update_thread_locale(thread_data_t *data)
583 if((data->locale_flags & LOCALE_FREE) && ((data->locale_flags & LOCALE_THREAD) ||
584 (data->locinfo == MSVCRT_locale->locinfo && data->mbcinfo == MSVCRT_locale->mbcinfo)))
585 return;
587 if(data->locale_flags & LOCALE_FREE)
589 free_locinfo(data->locinfo);
590 free_mbcinfo(data->mbcinfo);
593 _lock_locales();
594 data->locinfo = MSVCRT_locale->locinfo;
595 grab_locinfo(data->locinfo);
596 _unlock_locales();
598 _lock(_MB_CP_LOCK);
599 data->mbcinfo = MSVCRT_locale->mbcinfo;
600 InterlockedIncrement(&data->mbcinfo->refcount);
601 _unlock(_MB_CP_LOCK);
603 data->locale_flags |= LOCALE_FREE;
606 /* INTERNAL: returns threadlocinfo struct */
607 pthreadlocinfo CDECL get_locinfo(void) {
608 thread_data_t *data = msvcrt_get_thread_data();
609 update_thread_locale(data);
610 return data->locinfo;
613 /* INTERNAL: returns pthreadmbcinfo struct */
614 pthreadmbcinfo CDECL get_mbcinfo(void) {
615 thread_data_t *data = msvcrt_get_thread_data();
616 update_thread_locale(data);
617 return data->mbcinfo;
620 /* INTERNAL: constructs string returned by setlocale */
621 static inline char* construct_lc_all(pthreadlocinfo locinfo) {
622 static char current_lc_all[MAX_LOCALE_LENGTH];
624 int i;
626 for(i=LC_MIN+1; i<LC_MAX; i++) {
627 if(strcmp(locinfo->lc_category[i].locale,
628 locinfo->lc_category[i+1].locale))
629 break;
632 if(i==LC_MAX)
633 return locinfo->lc_category[LC_COLLATE].locale;
635 sprintf(current_lc_all,
636 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
637 locinfo->lc_category[LC_COLLATE].locale,
638 locinfo->lc_category[LC_CTYPE].locale,
639 locinfo->lc_category[LC_MONETARY].locale,
640 locinfo->lc_category[LC_NUMERIC].locale,
641 locinfo->lc_category[LC_TIME].locale);
643 return current_lc_all;
647 /*********************************************************************
648 * _Getdays (MSVCRT.@)
650 char* CDECL _Getdays(void)
652 __lc_time_data *cur = get_locinfo()->lc_time_curr;
653 int i, len, size = 0;
654 char *out;
656 TRACE("\n");
658 for(i=0; i<7; i++) {
659 size += strlen(cur->str.names.short_wday[i]) + 1;
660 size += strlen(cur->str.names.wday[i]) + 1;
662 out = malloc(size+1);
663 if(!out)
664 return NULL;
666 size = 0;
667 for(i=0; i<7; i++) {
668 out[size++] = ':';
669 len = strlen(cur->str.names.short_wday[i]);
670 memcpy(&out[size], cur->str.names.short_wday[i], len);
671 size += len;
673 out[size++] = ':';
674 len = strlen(cur->str.names.wday[i]);
675 memcpy(&out[size], cur->str.names.wday[i], len);
676 size += len;
678 out[size] = '\0';
680 return out;
683 #if _MSVCR_VER >= 110
684 /*********************************************************************
685 * _W_Getdays (MSVCR110.@)
687 wchar_t* CDECL _W_Getdays(void)
689 __lc_time_data *cur = get_locinfo()->lc_time_curr;
690 wchar_t *out;
691 int i, len, size = 0;
693 TRACE("\n");
695 for(i=0; i<7; i++) {
696 size += wcslen(cur->wstr.names.short_wday[i]) + 1;
697 size += wcslen(cur->wstr.names.wday[i]) + 1;
699 out = malloc((size+1)*sizeof(*out));
700 if(!out)
701 return NULL;
703 size = 0;
704 for(i=0; i<7; i++) {
705 out[size++] = ':';
706 len = wcslen(cur->wstr.names.short_wday[i]);
707 memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
708 size += len;
710 out[size++] = ':';
711 len = wcslen(cur->wstr.names.wday[i]);
712 memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
713 size += len;
715 out[size] = '\0';
717 return out;
719 #endif
721 /*********************************************************************
722 * _Getmonths (MSVCRT.@)
724 char* CDECL _Getmonths(void)
726 __lc_time_data *cur = get_locinfo()->lc_time_curr;
727 int i, len, size = 0;
728 char *out;
730 TRACE("\n");
732 for(i=0; i<12; i++) {
733 size += strlen(cur->str.names.short_mon[i]) + 1;
734 size += strlen(cur->str.names.mon[i]) + 1;
736 out = malloc(size+1);
737 if(!out)
738 return NULL;
740 size = 0;
741 for(i=0; i<12; i++) {
742 out[size++] = ':';
743 len = strlen(cur->str.names.short_mon[i]);
744 memcpy(&out[size], cur->str.names.short_mon[i], len);
745 size += len;
747 out[size++] = ':';
748 len = strlen(cur->str.names.mon[i]);
749 memcpy(&out[size], cur->str.names.mon[i], len);
750 size += len;
752 out[size] = '\0';
754 return out;
757 #if _MSVCR_VER >= 110
758 /*********************************************************************
759 * _W_Getmonths (MSVCR110.@)
761 wchar_t* CDECL _W_Getmonths(void)
763 __lc_time_data *cur = get_locinfo()->lc_time_curr;
764 wchar_t *out;
765 int i, len, size = 0;
767 TRACE("\n");
769 for(i=0; i<12; i++) {
770 size += wcslen(cur->wstr.names.short_mon[i]) + 1;
771 size += wcslen(cur->wstr.names.mon[i]) + 1;
773 out = malloc((size+1)*sizeof(*out));
774 if(!out)
775 return NULL;
777 size = 0;
778 for(i=0; i<12; i++) {
779 out[size++] = ':';
780 len = wcslen(cur->wstr.names.short_mon[i]);
781 memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
782 size += len;
784 out[size++] = ':';
785 len = wcslen(cur->wstr.names.mon[i]);
786 memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
787 size += len;
789 out[size] = '\0';
791 return out;
793 #endif
795 /*********************************************************************
796 * _Gettnames (MSVCRT.@)
798 void* CDECL _Gettnames(void)
800 __lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
801 unsigned int i, len, size = sizeof(__lc_time_data);
803 TRACE("\n");
805 for(i=0; i<ARRAY_SIZE(cur->str.str); i++)
806 size += strlen(cur->str.str[i])+1;
807 #if _MSVCR_VER >= 110
808 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++)
809 size += (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
810 #endif
812 ret = malloc(size);
813 if(!ret)
814 return NULL;
815 memcpy(ret, cur, sizeof(*ret));
817 size = 0;
818 for(i=0; i<ARRAY_SIZE(cur->str.str); i++) {
819 len = strlen(cur->str.str[i])+1;
820 memcpy(&ret->data[size], cur->str.str[i], len);
821 ret->str.str[i] = &ret->data[size];
822 size += len;
824 #if _MSVCR_VER >= 110
825 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++) {
826 len = (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
827 memcpy(&ret->data[size], cur->wstr.wstr[i], len);
828 ret->wstr.wstr[i] = (wchar_t*)&ret->data[size];
829 size += len;
831 #endif
833 return ret;
836 #if _MSVCR_VER >= 110
837 /*********************************************************************
838 * _W_Gettnames (MSVCR110.@)
840 void* CDECL _W_Gettnames(void)
842 return _Gettnames();
844 #endif
846 /*********************************************************************
847 * __crtLCMapStringA (MSVCRT.@)
849 int CDECL __crtLCMapStringA(
850 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
851 int dstlen, unsigned int codepage, int xflag
853 WCHAR buf_in[32], *in = buf_in;
854 WCHAR buf_out[32], *out = buf_out;
855 int in_len, out_len, r;
857 TRACE("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
858 lcid, mapflags, src, srclen, dst, dstlen, codepage, xflag);
860 in_len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, NULL, 0);
861 if (!in_len) return 0;
862 if (in_len > ARRAY_SIZE(buf_in))
864 in = malloc(in_len * sizeof(WCHAR));
865 if (!in) return 0;
868 r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len);
869 if (!r) goto done;
871 if (mapflags & LCMAP_SORTKEY)
873 r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen);
874 goto done;
877 r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0);
878 if (!r) goto done;
879 out_len = r;
880 if (r > ARRAY_SIZE(buf_out))
882 out = malloc(r * sizeof(WCHAR));
883 if (!out)
885 r = 0;
886 goto done;
890 r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len);
891 if (!r) goto done;
893 r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL);
895 done:
896 if (in != buf_in) free(in);
897 if (out != buf_out) free(out);
898 return r;
901 /*********************************************************************
902 * __crtLCMapStringW (MSVCRT.@)
904 int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src,
905 int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
907 FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
908 lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
910 return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
913 /*********************************************************************
914 * __crtCompareStringA (MSVCRT.@)
916 int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
917 const char *src2, int len2 )
919 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
920 lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
921 /* FIXME: probably not entirely right */
922 return CompareStringA( lcid, flags, src1, len1, src2, len2 );
925 /*********************************************************************
926 * __crtCompareStringW (MSVCRT.@)
928 int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1,
929 const wchar_t *src2, int len2 )
931 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
932 lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
933 /* FIXME: probably not entirely right */
934 return CompareStringW( lcid, flags, src1, len1, src2, len2 );
937 /*********************************************************************
938 * __crtGetLocaleInfoW (MSVCRT.@)
940 int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len )
942 FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid, type, buffer, len );
943 /* FIXME: probably not entirely right */
944 return GetLocaleInfoW( lcid, type, buffer, len );
947 #if _MSVCR_VER >= 110
948 /*********************************************************************
949 * __crtGetLocaleInfoEx (MSVC110.@)
951 int CDECL __crtGetLocaleInfoEx( const WCHAR *locale, LCTYPE type, wchar_t *buffer, int len )
953 TRACE("(%s, %x, %p, %d)\n", debugstr_w(locale), type, buffer, len);
954 return GetLocaleInfoEx(locale, type, buffer, len);
956 #endif
958 /*********************************************************************
959 * __crtGetStringTypeW(MSVCRT.@)
961 * This function was accepting different number of arguments in older
962 * versions of msvcrt.
964 BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
965 wchar_t *buffer, int len, WORD *out)
967 FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
968 unk, type, buffer, len, out);
970 return GetStringTypeW(type, buffer, len, out);
973 /*********************************************************************
974 * localeconv (MSVCRT.@)
976 struct lconv* CDECL localeconv(void)
978 return get_locinfo()->lconv;
981 /*********************************************************************
982 * __lconv_init (MSVCRT.@)
984 int CDECL __lconv_init(void)
986 /* this is used to make chars unsigned */
987 cloc_lconv.int_frac_digits = (char)UCHAR_MAX;
988 cloc_lconv.frac_digits = (char)UCHAR_MAX;
989 cloc_lconv.p_cs_precedes = (char)UCHAR_MAX;
990 cloc_lconv.p_sep_by_space = (char)UCHAR_MAX;
991 cloc_lconv.n_cs_precedes = (char)UCHAR_MAX;
992 cloc_lconv.n_sep_by_space = (char)UCHAR_MAX;
993 cloc_lconv.p_sign_posn = (char)UCHAR_MAX;
994 cloc_lconv.n_sign_posn = (char)UCHAR_MAX;
995 return 0;
998 /*********************************************************************
999 * ___lc_handle_func (MSVCRT.@)
1001 LCID* CDECL ___lc_handle_func(void)
1003 return (LCID *)get_locinfo()->lc_handle;
1006 #if _MSVCR_VER >= 110
1007 /*********************************************************************
1008 * ___lc_locale_name_func (MSVCR110.@)
1010 wchar_t** CDECL ___lc_locale_name_func(void)
1012 return get_locinfo()->lc_name;
1014 #endif
1016 /*********************************************************************
1017 * ___lc_codepage_func (MSVCRT.@)
1019 unsigned int CDECL ___lc_codepage_func(void)
1021 return get_locinfo()->lc_codepage;
1024 /*********************************************************************
1025 * ___lc_collate_cp_func (MSVCRT.@)
1027 int CDECL ___lc_collate_cp_func(void)
1029 return get_locinfo()->lc_collate_cp;
1032 /* INTERNAL: frees pthreadlocinfo struct */
1033 void free_locinfo(pthreadlocinfo locinfo)
1035 int i;
1037 if(!locinfo)
1038 return;
1040 for(i=LC_MIN+1; i<=LC_MAX; i++) {
1041 if(!locinfo->lc_category[i].refcount
1042 || !InterlockedDecrement(locinfo->lc_category[i].refcount)) {
1043 free(locinfo->lc_category[i].locale);
1044 free(locinfo->lc_category[i].refcount);
1046 if(!locinfo->lc_category[i].wrefcount
1047 || !InterlockedDecrement(locinfo->lc_category[i].wrefcount)) {
1048 #if _MSVCR_VER >= 110
1049 free(locinfo->lc_name[i]);
1050 #endif
1051 free(locinfo->lc_category[i].wrefcount);
1055 if(locinfo->lconv_num_refcount
1056 && !InterlockedDecrement(locinfo->lconv_num_refcount)) {
1057 free(locinfo->lconv->decimal_point);
1058 free(locinfo->lconv->thousands_sep);
1059 free(locinfo->lconv->grouping);
1060 #if _MSVCR_VER >= 100
1061 free(locinfo->lconv->_W_decimal_point);
1062 free(locinfo->lconv->_W_thousands_sep);
1063 #endif
1064 free(locinfo->lconv_num_refcount);
1066 if(locinfo->lconv_mon_refcount
1067 && !InterlockedDecrement(locinfo->lconv_mon_refcount)) {
1068 free(locinfo->lconv->int_curr_symbol);
1069 free(locinfo->lconv->currency_symbol);
1070 free(locinfo->lconv->mon_decimal_point);
1071 free(locinfo->lconv->mon_thousands_sep);
1072 free(locinfo->lconv->mon_grouping);
1073 free(locinfo->lconv->positive_sign);
1074 free(locinfo->lconv->negative_sign);
1075 #if _MSVCR_VER >= 100
1076 free(locinfo->lconv->_W_int_curr_symbol);
1077 free(locinfo->lconv->_W_currency_symbol);
1078 free(locinfo->lconv->_W_mon_decimal_point);
1079 free(locinfo->lconv->_W_mon_thousands_sep);
1080 free(locinfo->lconv->_W_positive_sign);
1081 free(locinfo->lconv->_W_negative_sign);
1082 #endif
1083 free(locinfo->lconv_mon_refcount);
1085 if(locinfo->lconv_intl_refcount
1086 && !InterlockedDecrement(locinfo->lconv_intl_refcount)) {
1087 free(locinfo->lconv_intl_refcount);
1088 free(locinfo->lconv);
1091 if(locinfo->ctype1_refcount
1092 && !InterlockedDecrement(locinfo->ctype1_refcount)) {
1093 free(locinfo->ctype1_refcount);
1094 free(locinfo->ctype1);
1095 free((void*)locinfo->pclmap);
1096 free((void*)locinfo->pcumap);
1099 if(locinfo->lc_time_curr && !InterlockedDecrement(&locinfo->lc_time_curr->refcount)
1100 && locinfo->lc_time_curr != &cloc_time_data)
1101 free(locinfo->lc_time_curr);
1103 if(InterlockedDecrement(&locinfo->refcount))
1104 return;
1106 free(locinfo);
1109 /* INTERNAL: frees pthreadmbcinfo struct */
1110 void free_mbcinfo(pthreadmbcinfo mbcinfo)
1112 if(!mbcinfo)
1113 return;
1115 if(InterlockedDecrement(&mbcinfo->refcount))
1116 return;
1118 free(mbcinfo);
1121 _locale_t CDECL get_current_locale_noalloc(_locale_t locale)
1123 thread_data_t *data = msvcrt_get_thread_data();
1125 update_thread_locale(data);
1126 locale->locinfo = data->locinfo;
1127 locale->mbcinfo = data->mbcinfo;
1129 grab_locinfo(locale->locinfo);
1130 InterlockedIncrement(&locale->mbcinfo->refcount);
1131 return locale;
1134 void CDECL free_locale_noalloc(_locale_t locale)
1136 free_locinfo(locale->locinfo);
1137 free_mbcinfo(locale->mbcinfo);
1140 /*********************************************************************
1141 * _get_current_locale (MSVCRT.@)
1143 _locale_t CDECL _get_current_locale(void)
1145 _locale_t loc = malloc(sizeof(_locale_tstruct));
1146 if(!loc)
1147 return NULL;
1149 return get_current_locale_noalloc(loc);
1152 /*********************************************************************
1153 * _free_locale (MSVCRT.@)
1155 void CDECL _free_locale(_locale_t locale)
1157 if (!locale)
1158 return;
1160 free_locale_noalloc(locale);
1161 free(locale);
1164 static inline BOOL category_needs_update(int cat,
1165 const threadlocinfo *locinfo, LCID lcid, unsigned short cp)
1167 if(!locinfo) return TRUE;
1168 return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage;
1171 static __lc_time_data* create_time_data(LCID lcid)
1173 static const DWORD time_data[] = {
1174 LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
1175 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
1176 LOCALE_SABBREVDAYNAME6,
1177 LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
1178 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
1179 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
1180 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
1181 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
1182 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
1183 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
1184 LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
1185 LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
1186 LOCALE_S1159, LOCALE_S2359,
1187 LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
1188 LOCALE_STIMEFORMAT
1191 __lc_time_data *cur;
1192 int i, ret, size;
1194 size = sizeof(__lc_time_data);
1195 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1196 ret = GetLocaleInfoA(lcid, time_data[i], NULL, 0);
1197 if(!ret)
1198 return NULL;
1199 size += ret;
1201 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1202 ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0);
1203 if(!ret)
1204 return NULL;
1205 size += ret*sizeof(wchar_t);
1206 #endif
1208 #if _MSVCR_VER >= 110
1209 size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(wchar_t);
1210 #endif
1212 cur = malloc(size);
1213 if(!cur)
1214 return NULL;
1216 ret = 0;
1217 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1218 cur->str.str[i] = &cur->data[ret];
1219 ret += GetLocaleInfoA(lcid, time_data[i], &cur->data[ret], size-ret);
1221 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1222 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1223 cur->wstr.wstr[i] = (wchar_t*)&cur->data[ret];
1224 ret += GetLocaleInfoW(lcid, time_data[i],
1225 (wchar_t*)&cur->data[ret], size-ret)*sizeof(wchar_t);
1227 #endif
1228 #if _MSVCR_VER >= 110
1229 cur->locname = (wchar_t*)&cur->data[ret];
1230 LCIDToLocaleName(lcid, (wchar_t*)&cur->data[ret], (size-ret)/sizeof(wchar_t), 0);
1231 #else
1232 cur->lcid = lcid;
1233 #endif
1234 cur->unk = 1;
1235 cur->refcount = 1;
1237 return cur;
1240 static pthreadlocinfo create_locinfo(int category,
1241 const char *locale, const threadlocinfo *old_locinfo)
1243 static const char collate[] = "COLLATE=";
1244 static const char ctype[] = "CTYPE=";
1245 static const char monetary[] = "MONETARY=";
1246 static const char numeric[] = "NUMERIC=";
1247 static const char time[] = "TIME=";
1249 pthreadlocinfo locinfo;
1250 LCID lcid[6] = { 0 };
1251 unsigned short cp[6] = { 0 };
1252 const char *locale_name[6] = { 0 };
1253 int val, locale_len[6] = { 0 };
1254 char buf[256];
1255 BOOL sname;
1256 #if _MSVCR_VER >= 100
1257 wchar_t wbuf[256];
1258 #endif
1259 int i;
1261 TRACE("(%d %s)\n", category, locale);
1263 if(category<LC_MIN || category>LC_MAX || !locale)
1264 return NULL;
1266 if(locale[0]=='C' && !locale[1]) {
1267 lcid[0] = 0;
1268 cp[0] = CP_ACP;
1269 } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
1270 const char *p;
1272 while(1) {
1273 locale += 3; /* LC_ */
1274 if(!memcmp(locale, collate, sizeof(collate)-1)) {
1275 i = LC_COLLATE;
1276 locale += sizeof(collate)-1;
1277 } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
1278 i = LC_CTYPE;
1279 locale += sizeof(ctype)-1;
1280 } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
1281 i = LC_MONETARY;
1282 locale += sizeof(monetary)-1;
1283 } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
1284 i = LC_NUMERIC;
1285 locale += sizeof(numeric)-1;
1286 } else if(!memcmp(locale, time, sizeof(time)-1)) {
1287 i = LC_TIME;
1288 locale += sizeof(time)-1;
1289 } else
1290 return NULL;
1292 p = strchr(locale, ';');
1293 if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
1294 lcid[i] = 0;
1295 cp[i] = CP_ACP;
1296 } else if(p) {
1297 memcpy(buf, locale, p-locale);
1298 buf[p-locale] = '\0';
1299 lcid[i] = locale_to_LCID(buf, &cp[i], &sname);
1300 if(sname) {
1301 locale_name[i] = locale;
1302 locale_len[i] = p-locale;
1304 } else {
1305 lcid[i] = locale_to_LCID(locale, &cp[i], &sname);
1306 if(sname) {
1307 locale_name[i] = locale;
1308 locale_len[i] = strlen(locale);
1312 if(lcid[i] == -1)
1313 return NULL;
1315 if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
1316 break;
1318 locale = p+1;
1320 } else {
1321 lcid[0] = locale_to_LCID(locale, &cp[0], &sname);
1322 if(lcid[0] == -1)
1323 return NULL;
1324 if(sname) {
1325 locale_name[0] = locale;
1326 locale_len[0] = strlen(locale);
1329 for(i=1; i<6; i++) {
1330 lcid[i] = lcid[0];
1331 cp[i] = cp[0];
1332 locale_name[i] = locale_name[0];
1333 locale_len[i] = locale_len[0];
1337 for(i=1; i<6; i++) {
1338 if(category!=LC_ALL && category!=i) {
1339 if(old_locinfo) {
1340 lcid[i] = old_locinfo->lc_handle[i];
1341 cp[i] = old_locinfo->lc_id[i].wCodePage;
1342 } else {
1343 lcid[i] = 0;
1344 cp[i] = 0;
1349 locinfo = malloc(sizeof(threadlocinfo));
1350 if(!locinfo)
1351 return NULL;
1353 memset(locinfo, 0, sizeof(threadlocinfo));
1354 locinfo->refcount = 1;
1356 if(locale_name[LC_COLLATE] &&
1357 !init_category_name(locale_name[LC_COLLATE],
1358 locale_len[LC_COLLATE], locinfo, LC_COLLATE)) {
1359 free_locinfo(locinfo);
1360 return NULL;
1363 if(!category_needs_update(LC_COLLATE, old_locinfo,
1364 lcid[LC_COLLATE], cp[LC_COLLATE])) {
1365 copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE);
1366 locinfo->lc_collate_cp = old_locinfo->lc_collate_cp;
1367 } else if(lcid[LC_COLLATE]) {
1368 if(!update_threadlocinfo_category(lcid[LC_COLLATE],
1369 cp[LC_COLLATE], locinfo, LC_COLLATE)) {
1370 free_locinfo(locinfo);
1371 return NULL;
1374 locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage;
1375 } else {
1376 if(!init_category_name("C", 1, locinfo, LC_COLLATE)) {
1377 free_locinfo(locinfo);
1378 return NULL;
1382 if(locale_name[LC_CTYPE] &&
1383 !init_category_name(locale_name[LC_CTYPE],
1384 locale_len[LC_CTYPE], locinfo, LC_CTYPE)) {
1385 free_locinfo(locinfo);
1386 return NULL;
1389 if(!category_needs_update(LC_CTYPE, old_locinfo,
1390 lcid[LC_CTYPE], cp[LC_CTYPE])) {
1391 copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE);
1392 locinfo->lc_codepage = old_locinfo->lc_codepage;
1393 locinfo->lc_clike = old_locinfo->lc_clike;
1394 locinfo->mb_cur_max = old_locinfo->mb_cur_max;
1395 locinfo->ctype1 = old_locinfo->ctype1;
1396 locinfo->ctype1_refcount = old_locinfo->ctype1_refcount;
1397 locinfo->pctype = old_locinfo->pctype;
1398 locinfo->pclmap = old_locinfo->pclmap;
1399 locinfo->pcumap = old_locinfo->pcumap;
1400 if(locinfo->ctype1_refcount)
1401 InterlockedIncrement(locinfo->ctype1_refcount);
1402 } else if(lcid[LC_CTYPE]) {
1403 CPINFO cp_info;
1404 int j;
1406 if(!update_threadlocinfo_category(lcid[LC_CTYPE],
1407 cp[LC_CTYPE], locinfo, LC_CTYPE)) {
1408 free_locinfo(locinfo);
1409 return NULL;
1412 locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage;
1413 locinfo->lc_clike = 1;
1414 if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) {
1415 free_locinfo(locinfo);
1416 return NULL;
1418 locinfo->mb_cur_max = cp_info.MaxCharSize;
1420 locinfo->ctype1_refcount = malloc(sizeof(int));
1421 if(!locinfo->ctype1_refcount) {
1422 free_locinfo(locinfo);
1423 return NULL;
1425 *locinfo->ctype1_refcount = 1;
1427 locinfo->ctype1 = malloc(sizeof(short[257]));
1428 locinfo->pclmap = malloc(sizeof(char[256]));
1429 locinfo->pcumap = malloc(sizeof(char[256]));
1430 if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) {
1431 free_locinfo(locinfo);
1432 return NULL;
1435 locinfo->ctype1[0] = 0;
1436 locinfo->pctype = locinfo->ctype1+1;
1438 buf[1] = buf[2] = '\0';
1439 for(i=1; i<257; i++) {
1440 buf[0] = i-1;
1442 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1443 locinfo->ctype1[i] = 0;
1445 GetStringTypeA(lcid[LC_CTYPE], CT_CTYPE1, buf,
1446 1, locinfo->ctype1+i);
1449 for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
1450 for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
1451 locinfo->ctype1[j+1] |= _LEADBYTE;
1453 for(i=0; i<256; i++) {
1454 if(locinfo->pctype[i] & _LEADBYTE)
1455 buf[i] = ' ';
1456 else
1457 buf[i] = i;
1460 LCMapStringA(lcid[LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
1461 (char*)locinfo->pclmap, 256);
1462 LCMapStringA(lcid[LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
1463 (char*)locinfo->pcumap, 256);
1464 } else {
1465 locinfo->lc_clike = 1;
1466 locinfo->mb_cur_max = 1;
1467 locinfo->pctype = MSVCRT__ctype+1;
1468 locinfo->pclmap = cloc_clmap;
1469 locinfo->pcumap = cloc_cumap;
1470 if(!init_category_name("C", 1, locinfo, LC_CTYPE)) {
1471 free_locinfo(locinfo);
1472 return NULL;
1476 if(!category_needs_update(LC_MONETARY, old_locinfo,
1477 lcid[LC_MONETARY], cp[LC_MONETARY]) &&
1478 !category_needs_update(LC_NUMERIC, old_locinfo,
1479 lcid[LC_NUMERIC], cp[LC_NUMERIC])) {
1480 locinfo->lconv = old_locinfo->lconv;
1481 locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount;
1482 if(locinfo->lconv_intl_refcount)
1483 InterlockedIncrement(locinfo->lconv_intl_refcount);
1484 } else if(lcid[LC_MONETARY] || lcid[LC_NUMERIC]) {
1485 locinfo->lconv = malloc(sizeof(struct lconv));
1486 locinfo->lconv_intl_refcount = malloc(sizeof(int));
1487 if(!locinfo->lconv || !locinfo->lconv_intl_refcount) {
1488 free(locinfo->lconv);
1489 free(locinfo->lconv_intl_refcount);
1490 locinfo->lconv = NULL;
1491 locinfo->lconv_intl_refcount = NULL;
1492 free_locinfo(locinfo);
1493 return NULL;
1495 memset(locinfo->lconv, 0, sizeof(struct lconv));
1496 *locinfo->lconv_intl_refcount = 1;
1497 } else {
1498 locinfo->lconv = &cloc_lconv;
1501 if(locale_name[LC_MONETARY] &&
1502 !init_category_name(locale_name[LC_MONETARY],
1503 locale_len[LC_MONETARY], locinfo, LC_MONETARY)) {
1504 free_locinfo(locinfo);
1505 return NULL;
1508 if(!category_needs_update(LC_MONETARY, old_locinfo,
1509 lcid[LC_MONETARY], cp[LC_MONETARY])) {
1510 copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY);
1511 locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount;
1512 if(locinfo->lconv_mon_refcount)
1513 InterlockedIncrement(locinfo->lconv_mon_refcount);
1514 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1515 locinfo->lconv->int_curr_symbol = old_locinfo->lconv->int_curr_symbol;
1516 locinfo->lconv->currency_symbol = old_locinfo->lconv->currency_symbol;
1517 locinfo->lconv->mon_decimal_point = old_locinfo->lconv->mon_decimal_point;
1518 locinfo->lconv->mon_thousands_sep = old_locinfo->lconv->mon_thousands_sep;
1519 locinfo->lconv->mon_grouping = old_locinfo->lconv->mon_grouping;
1520 locinfo->lconv->positive_sign = old_locinfo->lconv->positive_sign;
1521 locinfo->lconv->negative_sign = old_locinfo->lconv->negative_sign;
1522 locinfo->lconv->int_frac_digits = old_locinfo->lconv->int_frac_digits;
1523 locinfo->lconv->frac_digits = old_locinfo->lconv->frac_digits;
1524 locinfo->lconv->p_cs_precedes = old_locinfo->lconv->p_cs_precedes;
1525 locinfo->lconv->p_sep_by_space = old_locinfo->lconv->p_sep_by_space;
1526 locinfo->lconv->n_cs_precedes = old_locinfo->lconv->n_cs_precedes;
1527 locinfo->lconv->n_sep_by_space = old_locinfo->lconv->n_sep_by_space;
1528 locinfo->lconv->p_sign_posn = old_locinfo->lconv->p_sign_posn;
1529 locinfo->lconv->n_sign_posn = old_locinfo->lconv->n_sign_posn;
1530 #if _MSVCR_VER >= 100
1531 locinfo->lconv->_W_int_curr_symbol = old_locinfo->lconv->_W_int_curr_symbol;
1532 locinfo->lconv->_W_currency_symbol = old_locinfo->lconv->_W_currency_symbol;
1533 locinfo->lconv->_W_mon_decimal_point = old_locinfo->lconv->_W_mon_decimal_point;
1534 locinfo->lconv->_W_mon_thousands_sep = old_locinfo->lconv->_W_mon_thousands_sep;
1535 locinfo->lconv->_W_positive_sign = old_locinfo->lconv->_W_positive_sign;
1536 locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign;
1537 #endif
1539 } else if(lcid[LC_MONETARY]) {
1540 if(!update_threadlocinfo_category(lcid[LC_MONETARY],
1541 cp[LC_MONETARY], locinfo, LC_MONETARY)) {
1542 free_locinfo(locinfo);
1543 return NULL;
1546 locinfo->lconv_mon_refcount = malloc(sizeof(int));
1547 if(!locinfo->lconv_mon_refcount) {
1548 free_locinfo(locinfo);
1549 return NULL;
1552 *locinfo->lconv_mon_refcount = 1;
1554 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL
1555 |LOCALE_NOUSEROVERRIDE, buf, 256);
1556 if(i && (locinfo->lconv->int_curr_symbol = malloc(i)))
1557 memcpy(locinfo->lconv->int_curr_symbol, buf, i);
1558 else {
1559 free_locinfo(locinfo);
1560 return NULL;
1563 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SCURRENCY
1564 |LOCALE_NOUSEROVERRIDE, buf, 256);
1565 if(i && (locinfo->lconv->currency_symbol = malloc(i)))
1566 memcpy(locinfo->lconv->currency_symbol, buf, i);
1567 else {
1568 free_locinfo(locinfo);
1569 return NULL;
1572 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP
1573 |LOCALE_NOUSEROVERRIDE, buf, 256);
1574 if(i && (locinfo->lconv->mon_decimal_point = malloc(i)))
1575 memcpy(locinfo->lconv->mon_decimal_point, buf, i);
1576 else {
1577 free_locinfo(locinfo);
1578 return NULL;
1581 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1582 |LOCALE_NOUSEROVERRIDE, buf, 256);
1583 if(i && (locinfo->lconv->mon_thousands_sep = malloc(i)))
1584 memcpy(locinfo->lconv->mon_thousands_sep, buf, i);
1585 else {
1586 free_locinfo(locinfo);
1587 return NULL;
1590 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONGROUPING
1591 |LOCALE_NOUSEROVERRIDE, buf, 256);
1592 if(i>1)
1593 i = i/2 + (buf[i-2]=='0'?0:1);
1594 if(i && (locinfo->lconv->mon_grouping = malloc(i))) {
1595 for(i=0; buf[i+1]==';'; i+=2)
1596 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1597 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1598 if(buf[i] != '0')
1599 locinfo->lconv->mon_grouping[i/2+1] = 127;
1600 } else {
1601 free_locinfo(locinfo);
1602 return NULL;
1605 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN
1606 |LOCALE_NOUSEROVERRIDE, buf, 256);
1607 if(i && (locinfo->lconv->positive_sign = malloc(i)))
1608 memcpy(locinfo->lconv->positive_sign, buf, i);
1609 else {
1610 free_locinfo(locinfo);
1611 return NULL;
1614 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN
1615 |LOCALE_NOUSEROVERRIDE, buf, 256);
1616 if(i && (locinfo->lconv->negative_sign = malloc(i)))
1617 memcpy(locinfo->lconv->negative_sign, buf, i);
1618 else {
1619 free_locinfo(locinfo);
1620 return NULL;
1623 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IINTLCURRDIGITS
1624 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1625 locinfo->lconv->int_frac_digits = val;
1626 else {
1627 free_locinfo(locinfo);
1628 return NULL;
1631 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_ICURRDIGITS
1632 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1633 locinfo->lconv->frac_digits = val;
1634 else {
1635 free_locinfo(locinfo);
1636 return NULL;
1639 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSYMPRECEDES
1640 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1641 locinfo->lconv->p_cs_precedes = val;
1642 else {
1643 free_locinfo(locinfo);
1644 return NULL;
1647 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSEPBYSPACE
1648 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1649 locinfo->lconv->p_sep_by_space = val;
1650 else {
1651 free_locinfo(locinfo);
1652 return NULL;
1655 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSYMPRECEDES
1656 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1657 locinfo->lconv->n_cs_precedes = val;
1658 else {
1659 free_locinfo(locinfo);
1660 return NULL;
1663 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSEPBYSPACE
1664 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1665 locinfo->lconv->n_sep_by_space = val;
1666 else {
1667 free_locinfo(locinfo);
1668 return NULL;
1671 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSIGNPOSN
1672 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1673 locinfo->lconv->p_sign_posn = val;
1674 else {
1675 free_locinfo(locinfo);
1676 return NULL;
1679 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSIGNPOSN
1680 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1681 locinfo->lconv->n_sign_posn = val;
1682 else {
1683 free_locinfo(locinfo);
1684 return NULL;
1687 #if _MSVCR_VER >= 100
1688 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL
1689 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1690 if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t))))
1691 memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t));
1692 else {
1693 free_locinfo(locinfo);
1694 return NULL;
1697 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SCURRENCY
1698 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1699 if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t))))
1700 memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t));
1701 else {
1702 free_locinfo(locinfo);
1703 return NULL;
1706 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP
1707 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1708 if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t))))
1709 memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t));
1710 else {
1711 free_locinfo(locinfo);
1712 return NULL;
1715 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1716 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1717 if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t))))
1718 memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t));
1719 else {
1720 free_locinfo(locinfo);
1721 return NULL;
1724 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN
1725 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1726 if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t))))
1727 memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t));
1728 else {
1729 free_locinfo(locinfo);
1730 return NULL;
1733 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN
1734 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1735 if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t))))
1736 memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t));
1737 else {
1738 free_locinfo(locinfo);
1739 return NULL;
1741 #endif
1742 } else {
1743 if (locinfo->lconv != &cloc_lconv) {
1744 locinfo->lconv->int_curr_symbol = cloc_lconv.int_curr_symbol;
1745 locinfo->lconv->currency_symbol = cloc_lconv.currency_symbol;
1746 locinfo->lconv->mon_decimal_point = cloc_lconv.mon_decimal_point;
1747 locinfo->lconv->mon_thousands_sep = cloc_lconv.mon_thousands_sep;
1748 locinfo->lconv->mon_grouping = cloc_lconv.mon_grouping;
1749 locinfo->lconv->positive_sign = cloc_lconv.positive_sign;
1750 locinfo->lconv->negative_sign = cloc_lconv.negative_sign;
1751 locinfo->lconv->int_frac_digits = cloc_lconv.int_frac_digits;
1752 locinfo->lconv->frac_digits = cloc_lconv.frac_digits;
1753 locinfo->lconv->p_cs_precedes = cloc_lconv.p_cs_precedes;
1754 locinfo->lconv->p_sep_by_space = cloc_lconv.p_sep_by_space;
1755 locinfo->lconv->n_cs_precedes = cloc_lconv.n_cs_precedes;
1756 locinfo->lconv->n_sep_by_space = cloc_lconv.n_sep_by_space;
1757 locinfo->lconv->p_sign_posn = cloc_lconv.p_sign_posn;
1758 locinfo->lconv->n_sign_posn = cloc_lconv.n_sign_posn;
1760 #if _MSVCR_VER >= 100
1761 locinfo->lconv->_W_int_curr_symbol = cloc_lconv._W_int_curr_symbol;
1762 locinfo->lconv->_W_currency_symbol = cloc_lconv._W_currency_symbol;
1763 locinfo->lconv->_W_mon_decimal_point = cloc_lconv._W_mon_decimal_point;
1764 locinfo->lconv->_W_mon_thousands_sep = cloc_lconv._W_mon_thousands_sep;
1765 locinfo->lconv->_W_positive_sign = cloc_lconv._W_positive_sign;
1766 locinfo->lconv->_W_negative_sign = cloc_lconv._W_negative_sign;
1767 #endif
1770 if(!init_category_name("C", 1, locinfo, LC_MONETARY)) {
1771 free_locinfo(locinfo);
1772 return NULL;
1776 if(locale_name[LC_NUMERIC] &&
1777 !init_category_name(locale_name[LC_NUMERIC],
1778 locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1779 free_locinfo(locinfo);
1780 return NULL;
1783 if(!category_needs_update(LC_NUMERIC, old_locinfo,
1784 lcid[LC_NUMERIC], cp[LC_NUMERIC])) {
1785 copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC);
1786 locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount;
1787 if(locinfo->lconv_num_refcount)
1788 InterlockedIncrement(locinfo->lconv_num_refcount);
1789 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1790 locinfo->lconv->decimal_point = old_locinfo->lconv->decimal_point;
1791 locinfo->lconv->thousands_sep = old_locinfo->lconv->thousands_sep;
1792 locinfo->lconv->grouping = old_locinfo->lconv->grouping;
1793 #if _MSVCR_VER >= 100
1794 locinfo->lconv->_W_decimal_point = old_locinfo->lconv->_W_decimal_point;
1795 locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep;
1796 #endif
1798 } else if(lcid[LC_NUMERIC]) {
1799 if(!update_threadlocinfo_category(lcid[LC_NUMERIC],
1800 cp[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1801 free_locinfo(locinfo);
1802 return NULL;
1805 locinfo->lconv_num_refcount = malloc(sizeof(int));
1806 if(!locinfo->lconv_num_refcount) {
1807 free_locinfo(locinfo);
1808 return NULL;
1811 *locinfo->lconv_num_refcount = 1;
1813 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SDECIMAL
1814 |LOCALE_NOUSEROVERRIDE, buf, 256);
1815 if(i && (locinfo->lconv->decimal_point = malloc(i)))
1816 memcpy(locinfo->lconv->decimal_point, buf, i);
1817 else {
1818 free_locinfo(locinfo);
1819 return NULL;
1822 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_STHOUSAND
1823 |LOCALE_NOUSEROVERRIDE, buf, 256);
1824 if(i && (locinfo->lconv->thousands_sep = malloc(i)))
1825 memcpy(locinfo->lconv->thousands_sep, buf, i);
1826 else {
1827 free_locinfo(locinfo);
1828 return NULL;
1831 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SGROUPING
1832 |LOCALE_NOUSEROVERRIDE, buf, 256);
1833 if(i>1)
1834 i = i/2 + (buf[i-2]=='0'?0:1);
1835 if(i && (locinfo->lconv->grouping = malloc(i))) {
1836 for(i=0; buf[i+1]==';'; i+=2)
1837 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1838 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1839 if(buf[i] != '0')
1840 locinfo->lconv->grouping[i/2+1] = 127;
1841 } else {
1842 free_locinfo(locinfo);
1843 return NULL;
1846 #if _MSVCR_VER >= 100
1847 i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_SDECIMAL
1848 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1849 if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t))))
1850 memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t));
1851 else {
1852 free_locinfo(locinfo);
1853 return NULL;
1856 i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_STHOUSAND
1857 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1858 if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t))))
1859 memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t));
1860 else {
1861 free_locinfo(locinfo);
1862 return NULL;
1864 #endif
1865 } else {
1866 if (locinfo->lconv != &cloc_lconv) {
1867 locinfo->lconv->decimal_point = cloc_lconv.decimal_point;
1868 locinfo->lconv->thousands_sep = cloc_lconv.thousands_sep;
1869 locinfo->lconv->grouping = cloc_lconv.grouping;
1871 #if _MSVCR_VER >= 100
1872 locinfo->lconv->_W_decimal_point = cloc_lconv._W_decimal_point;
1873 locinfo->lconv->_W_thousands_sep = cloc_lconv._W_thousands_sep;
1874 #endif
1877 if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) {
1878 free_locinfo(locinfo);
1879 return NULL;
1883 if(locale_name[LC_TIME] &&
1884 !init_category_name(locale_name[LC_TIME],
1885 locale_len[LC_TIME], locinfo, LC_TIME)) {
1886 free_locinfo(locinfo);
1887 return NULL;
1890 if(!category_needs_update(LC_TIME, old_locinfo,
1891 lcid[LC_TIME], cp[LC_TIME])) {
1892 copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME);
1893 locinfo->lc_time_curr = old_locinfo->lc_time_curr;
1894 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1895 } else if(lcid[LC_TIME]) {
1896 if(!update_threadlocinfo_category(lcid[LC_TIME],
1897 cp[LC_TIME], locinfo, LC_TIME)) {
1898 free_locinfo(locinfo);
1899 return NULL;
1902 locinfo->lc_time_curr = create_time_data(lcid[LC_TIME]);
1903 if(!locinfo->lc_time_curr) {
1904 free_locinfo(locinfo);
1905 return NULL;
1907 } else {
1908 if(!init_category_name("C", 1, locinfo, LC_TIME)) {
1909 free_locinfo(locinfo);
1910 return NULL;
1912 locinfo->lc_time_curr = &cloc_time_data;
1913 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1916 return locinfo;
1919 /*********************************************************************
1920 * _create_locale (MSVCRT.@)
1922 _locale_t CDECL _create_locale(int category, const char *locale)
1924 _locale_t loc;
1926 loc = malloc(sizeof(_locale_tstruct));
1927 if(!loc)
1928 return NULL;
1930 loc->locinfo = create_locinfo(category, locale, NULL);
1931 if(!loc->locinfo) {
1932 free(loc);
1933 return NULL;
1936 loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[LC_CTYPE].wCodePage,
1937 loc->locinfo->lc_handle[LC_CTYPE], NULL);
1938 if(!loc->mbcinfo) {
1939 free_locinfo(loc->locinfo);
1940 free(loc);
1941 return NULL;
1943 return loc;
1946 #if _MSVCR_VER >= 110
1947 /*********************************************************************
1948 * _wcreate_locale (MSVCR110.@)
1950 _locale_t CDECL _wcreate_locale(int category, const wchar_t *locale)
1952 _locale_t loc;
1953 size_t len;
1954 char *str;
1956 if(category<LC_MIN || category>LC_MAX || !locale)
1957 return NULL;
1959 len = wcstombs(NULL, locale, 0);
1960 if(len == -1)
1961 return NULL;
1962 if(!(str = malloc(++len)))
1963 return NULL;
1964 wcstombs(str, locale, len);
1966 loc = _create_locale(category, str);
1968 free(str);
1969 return loc;
1971 #endif
1973 /*********************************************************************
1974 * setlocale (MSVCRT.@)
1976 char* CDECL setlocale(int category, const char* locale)
1978 thread_data_t *data = msvcrt_get_thread_data();
1979 pthreadlocinfo locinfo = get_locinfo(), newlocinfo;
1981 if(category<LC_MIN || category>LC_MAX)
1982 return NULL;
1984 if(!locale) {
1985 if(category == LC_ALL)
1986 return construct_lc_all(locinfo);
1988 return locinfo->lc_category[category].locale;
1991 newlocinfo = create_locinfo(category, locale, locinfo);
1992 if(!newlocinfo) {
1993 WARN("%d %s failed\n", category, locale);
1994 return NULL;
1997 if(locale[0] != 'C' || locale[1] != '\0')
1998 initial_locale = FALSE;
2000 if(data->locale_flags & LOCALE_THREAD)
2002 if(data->locale_flags & LOCALE_FREE)
2003 free_locinfo(data->locinfo);
2004 data->locinfo = newlocinfo;
2006 else
2008 int i;
2010 _lock_locales();
2011 free_locinfo(MSVCRT_locale->locinfo);
2012 MSVCRT_locale->locinfo = newlocinfo;
2014 MSVCRT___lc_codepage = newlocinfo->lc_codepage;
2015 MSVCRT___lc_collate_cp = newlocinfo->lc_collate_cp;
2016 MSVCRT___mb_cur_max = newlocinfo->mb_cur_max;
2017 MSVCRT__pctype = newlocinfo->pctype;
2018 for(i=LC_MIN; i<=LC_MAX; i++)
2019 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2020 _unlock_locales();
2021 update_thread_locale(data);
2024 if(category == LC_ALL)
2025 return construct_lc_all(data->locinfo);
2027 return data->locinfo->lc_category[category].locale;
2030 /*********************************************************************
2031 * _wsetlocale (MSVCRT.@)
2033 wchar_t* CDECL _wsetlocale(int category, const wchar_t* wlocale)
2035 static wchar_t current_lc_all[MAX_LOCALE_LENGTH];
2037 char *locale = NULL;
2038 const char *ret;
2039 size_t len;
2041 if(wlocale) {
2042 len = wcstombs(NULL, wlocale, 0);
2043 if(len == -1)
2044 return NULL;
2046 locale = malloc(++len);
2047 if(!locale)
2048 return NULL;
2050 wcstombs(locale, wlocale, len);
2053 _lock_locales();
2054 ret = setlocale(category, locale);
2055 free(locale);
2057 if(ret && mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
2058 ret = NULL;
2060 _unlock_locales();
2061 return ret ? current_lc_all : NULL;
2064 #if _MSVCR_VER >= 80
2065 /*********************************************************************
2066 * _configthreadlocale (MSVCR80.@)
2068 int CDECL _configthreadlocale(int type)
2070 thread_data_t *data = msvcrt_get_thread_data();
2071 int ret;
2073 ret = (data->locale_flags & LOCALE_THREAD ? _ENABLE_PER_THREAD_LOCALE :
2074 _DISABLE_PER_THREAD_LOCALE);
2076 if(type == _ENABLE_PER_THREAD_LOCALE)
2077 data->locale_flags |= LOCALE_THREAD;
2078 else if(type == _DISABLE_PER_THREAD_LOCALE)
2079 data->locale_flags &= ~LOCALE_THREAD;
2080 else if(type)
2081 ret = -1;
2083 return ret;
2085 #endif
2087 BOOL msvcrt_init_locale(void)
2089 int i;
2091 _lock_locales();
2092 MSVCRT_locale = _create_locale(0, "C");
2093 _unlock_locales();
2094 if(!MSVCRT_locale)
2095 return FALSE;
2097 MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
2098 MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
2099 MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
2100 MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
2101 for(i=LC_MIN; i<=LC_MAX; i++)
2102 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2103 _setmbcp(_MB_CP_ANSI);
2104 return TRUE;
2107 #if _MSVCR_VER >= 120
2108 /*********************************************************************
2109 * wctrans (MSVCR120.@)
2111 wctrans_t CDECL wctrans(const char *property)
2113 static const char str_tolower[] = "tolower";
2114 static const char str_toupper[] = "toupper";
2116 if(!strcmp(property, str_tolower))
2117 return 2;
2118 if(!strcmp(property, str_toupper))
2119 return 1;
2120 return 0;
2122 #endif