kernelbase: Reimplement LOCALE_*CTRYNAME in GetLocaleInfoW/Ex using the locale.nls...
[wine.git] / dlls / msvcrt / locale.c
blob037660d0a91503ba2d333e147d8ce774c3ead230
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 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
367 (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
368 (LONG_PTR)&search);
370 if (!search.match_flags)
371 return -1;
373 /* If we were given something that didn't match, fail */
374 if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE)))
375 return -1;
376 if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
377 return -1;
379 lcid = MAKELCID(search.found_lang_id, SORT_DEFAULT);
380 is_sname = (search.match_flags & FOUND_SNAME) != 0;
383 /* Obtain code page */
384 if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) {
385 GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
386 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
387 if (!locale_cp)
388 locale_cp = GetACP();
389 } else if (!_strnicmp(cp, ".OCP", 4)) {
390 GetLocaleInfoW(lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
391 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
392 #if _MSVCR_VER >= 140
393 } else if (!_strnicmp(cp, ".UTF-8", 6)
394 || !_strnicmp(cp, ".UTF8", 5)) {
395 locale_cp = CP_UTF8;
396 #endif
397 } else {
398 locale_cp = atoi(cp + 1);
400 if (!IsValidCodePage(locale_cp))
401 return -1;
403 if (!locale_cp)
404 return -1;
406 if (codepage)
407 *codepage = locale_cp;
408 if (sname)
409 *sname = is_sname;
411 if (strlen(locale) < sizeof(data->cached_locale)) {
412 strcpy(data->cached_locale, locale);
413 data->cached_lcid = lcid;
414 data->cached_cp = locale_cp;
415 data->cached_sname = is_sname;
418 return lcid;
421 static void copy_threadlocinfo_category(pthreadlocinfo locinfo,
422 const threadlocinfo *old_locinfo, int category)
424 locinfo->lc_handle[category] = old_locinfo->lc_handle[category];
425 locinfo->lc_id[category] = old_locinfo->lc_id[category];
426 if(!locinfo->lc_category[category].locale) {
427 locinfo->lc_category[category].locale = old_locinfo->lc_category[category].locale;
428 locinfo->lc_category[category].refcount = old_locinfo->lc_category[category].refcount;
429 InterlockedIncrement((LONG *)locinfo->lc_category[category].refcount);
431 #if _MSVCR_VER >= 110
432 locinfo->lc_name[category] = old_locinfo->lc_name[category];
433 locinfo->lc_category[category].wrefcount = old_locinfo->lc_category[category].wrefcount;
434 if(locinfo->lc_category[category].wrefcount)
435 InterlockedIncrement((LONG *)locinfo->lc_category[category].wrefcount);
436 #endif
439 static BOOL init_category_name(const char *name, int len,
440 pthreadlocinfo locinfo, int category)
442 locinfo->lc_category[category].locale = malloc(len+1);
443 locinfo->lc_category[category].refcount = malloc(sizeof(int));
444 if(!locinfo->lc_category[category].locale
445 || !locinfo->lc_category[category].refcount) {
446 free(locinfo->lc_category[category].locale);
447 free(locinfo->lc_category[category].refcount);
448 locinfo->lc_category[category].locale = NULL;
449 locinfo->lc_category[category].refcount = NULL;
450 return FALSE;
453 memcpy(locinfo->lc_category[category].locale, name, len);
454 locinfo->lc_category[category].locale[len] = 0;
455 *locinfo->lc_category[category].refcount = 1;
456 return TRUE;
459 #if _MSVCR_VER >= 110
460 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat)
462 LCID lcid = locinfo->lc_handle[cat];
463 WCHAR buf[100];
464 int len;
466 locinfo->lc_category[cat].wrefcount = malloc(sizeof(int));
467 if(!locinfo->lc_category[cat].wrefcount)
468 return FALSE;
469 *locinfo->lc_category[cat].wrefcount = 1;
471 len = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME
472 |LOCALE_NOUSEROVERRIDE, buf, 100);
473 if(!len) return FALSE;
475 if(LocaleNameToLCID(buf, 0) != lcid)
476 len = LCIDToLocaleName(lcid, buf, 100, 0);
478 if(!len || !(locinfo->lc_name[cat] = malloc(len*sizeof(wchar_t))))
479 return FALSE;
481 memcpy(locinfo->lc_name[cat], buf, len*sizeof(wchar_t));
482 return TRUE;
484 #else
485 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat)
487 return TRUE;
489 #endif
491 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
492 static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp,
493 pthreadlocinfo locinfo, int category)
495 char buf[256], *p;
497 if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) {
498 p = buf;
500 locinfo->lc_id[category].wLanguage = 0;
501 while(*p) {
502 locinfo->lc_id[category].wLanguage *= 16;
504 if(*p <= '9')
505 locinfo->lc_id[category].wLanguage += *p-'0';
506 else
507 locinfo->lc_id[category].wLanguage += *p-'a'+10;
509 p++;
512 locinfo->lc_id[category].wCountry =
513 locinfo->lc_id[category].wLanguage;
516 locinfo->lc_id[category].wCodePage = cp;
518 locinfo->lc_handle[category] = lcid;
520 set_lc_locale_name(locinfo, category);
522 if(!locinfo->lc_category[category].locale) {
523 int len = 0;
525 if (lcid == MAKELANGID( LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK ))
527 /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */
528 strcpy( buf, "Norwegian-Nynorsk" );
529 len = strlen( buf ) + 1;
531 else len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256);
532 buf[len-1] = '_';
533 len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY
534 |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len);
535 buf[len-1] = '.';
536 sprintf(buf+len, "%d", cp);
537 len += strlen(buf+len);
539 return init_category_name(buf, len, locinfo, category);
542 return TRUE;
545 /*********************************************************************
546 * _lock_locales (UCRTBASE.@)
548 void CDECL _lock_locales(void)
550 _lock(_SETLOCALE_LOCK);
553 /*********************************************************************
554 * _unlock_locales (UCRTBASE.@)
556 void CDECL _unlock_locales(void)
558 _unlock(_SETLOCALE_LOCK);
561 static void grab_locinfo(pthreadlocinfo locinfo)
563 int i;
565 InterlockedIncrement((LONG *)&locinfo->refcount);
566 for(i=LC_MIN+1; i<=LC_MAX; i++)
568 InterlockedIncrement((LONG *)locinfo->lc_category[i].refcount);
569 if(locinfo->lc_category[i].wrefcount)
570 InterlockedIncrement((LONG *)locinfo->lc_category[i].wrefcount);
572 if(locinfo->lconv_intl_refcount)
573 InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
574 if(locinfo->lconv_num_refcount)
575 InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
576 if(locinfo->lconv_mon_refcount)
577 InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
578 if(locinfo->ctype1_refcount)
579 InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
580 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
583 static void update_thread_locale(thread_data_t *data)
585 if((data->locale_flags & LOCALE_FREE) && ((data->locale_flags & LOCALE_THREAD) ||
586 (data->locinfo == MSVCRT_locale->locinfo && data->mbcinfo == MSVCRT_locale->mbcinfo)))
587 return;
589 if(data->locale_flags & LOCALE_FREE)
591 free_locinfo(data->locinfo);
592 free_mbcinfo(data->mbcinfo);
595 _lock_locales();
596 data->locinfo = MSVCRT_locale->locinfo;
597 grab_locinfo(data->locinfo);
598 _unlock_locales();
600 _lock(_MB_CP_LOCK);
601 data->mbcinfo = MSVCRT_locale->mbcinfo;
602 InterlockedIncrement(&data->mbcinfo->refcount);
603 _unlock(_MB_CP_LOCK);
605 data->locale_flags |= LOCALE_FREE;
608 /* INTERNAL: returns threadlocinfo struct */
609 pthreadlocinfo CDECL get_locinfo(void) {
610 thread_data_t *data = msvcrt_get_thread_data();
611 update_thread_locale(data);
612 return data->locinfo;
615 /* INTERNAL: returns pthreadmbcinfo struct */
616 pthreadmbcinfo CDECL get_mbcinfo(void) {
617 thread_data_t *data = msvcrt_get_thread_data();
618 update_thread_locale(data);
619 return data->mbcinfo;
622 /* INTERNAL: constructs string returned by setlocale */
623 static inline char* construct_lc_all(pthreadlocinfo locinfo) {
624 static char current_lc_all[MAX_LOCALE_LENGTH];
626 int i;
628 for(i=LC_MIN+1; i<LC_MAX; i++) {
629 if(strcmp(locinfo->lc_category[i].locale,
630 locinfo->lc_category[i+1].locale))
631 break;
634 if(i==LC_MAX)
635 return locinfo->lc_category[LC_COLLATE].locale;
637 sprintf(current_lc_all,
638 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
639 locinfo->lc_category[LC_COLLATE].locale,
640 locinfo->lc_category[LC_CTYPE].locale,
641 locinfo->lc_category[LC_MONETARY].locale,
642 locinfo->lc_category[LC_NUMERIC].locale,
643 locinfo->lc_category[LC_TIME].locale);
645 return current_lc_all;
649 /*********************************************************************
650 * _Getdays (MSVCRT.@)
652 char* CDECL _Getdays(void)
654 __lc_time_data *cur = get_locinfo()->lc_time_curr;
655 int i, len, size = 0;
656 char *out;
658 TRACE("\n");
660 for(i=0; i<7; i++) {
661 size += strlen(cur->str.names.short_wday[i]) + 1;
662 size += strlen(cur->str.names.wday[i]) + 1;
664 out = malloc(size+1);
665 if(!out)
666 return NULL;
668 size = 0;
669 for(i=0; i<7; i++) {
670 out[size++] = ':';
671 len = strlen(cur->str.names.short_wday[i]);
672 memcpy(&out[size], cur->str.names.short_wday[i], len);
673 size += len;
675 out[size++] = ':';
676 len = strlen(cur->str.names.wday[i]);
677 memcpy(&out[size], cur->str.names.wday[i], len);
678 size += len;
680 out[size] = '\0';
682 return out;
685 #if _MSVCR_VER >= 110
686 /*********************************************************************
687 * _W_Getdays (MSVCR110.@)
689 wchar_t* CDECL _W_Getdays(void)
691 __lc_time_data *cur = get_locinfo()->lc_time_curr;
692 wchar_t *out;
693 int i, len, size = 0;
695 TRACE("\n");
697 for(i=0; i<7; i++) {
698 size += wcslen(cur->wstr.names.short_wday[i]) + 1;
699 size += wcslen(cur->wstr.names.wday[i]) + 1;
701 out = malloc((size+1)*sizeof(*out));
702 if(!out)
703 return NULL;
705 size = 0;
706 for(i=0; i<7; i++) {
707 out[size++] = ':';
708 len = wcslen(cur->wstr.names.short_wday[i]);
709 memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
710 size += len;
712 out[size++] = ':';
713 len = wcslen(cur->wstr.names.wday[i]);
714 memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
715 size += len;
717 out[size] = '\0';
719 return out;
721 #endif
723 /*********************************************************************
724 * _Getmonths (MSVCRT.@)
726 char* CDECL _Getmonths(void)
728 __lc_time_data *cur = get_locinfo()->lc_time_curr;
729 int i, len, size = 0;
730 char *out;
732 TRACE("\n");
734 for(i=0; i<12; i++) {
735 size += strlen(cur->str.names.short_mon[i]) + 1;
736 size += strlen(cur->str.names.mon[i]) + 1;
738 out = malloc(size+1);
739 if(!out)
740 return NULL;
742 size = 0;
743 for(i=0; i<12; i++) {
744 out[size++] = ':';
745 len = strlen(cur->str.names.short_mon[i]);
746 memcpy(&out[size], cur->str.names.short_mon[i], len);
747 size += len;
749 out[size++] = ':';
750 len = strlen(cur->str.names.mon[i]);
751 memcpy(&out[size], cur->str.names.mon[i], len);
752 size += len;
754 out[size] = '\0';
756 return out;
759 #if _MSVCR_VER >= 110
760 /*********************************************************************
761 * _W_Getmonths (MSVCR110.@)
763 wchar_t* CDECL _W_Getmonths(void)
765 __lc_time_data *cur = get_locinfo()->lc_time_curr;
766 wchar_t *out;
767 int i, len, size = 0;
769 TRACE("\n");
771 for(i=0; i<12; i++) {
772 size += wcslen(cur->wstr.names.short_mon[i]) + 1;
773 size += wcslen(cur->wstr.names.mon[i]) + 1;
775 out = malloc((size+1)*sizeof(*out));
776 if(!out)
777 return NULL;
779 size = 0;
780 for(i=0; i<12; i++) {
781 out[size++] = ':';
782 len = wcslen(cur->wstr.names.short_mon[i]);
783 memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
784 size += len;
786 out[size++] = ':';
787 len = wcslen(cur->wstr.names.mon[i]);
788 memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
789 size += len;
791 out[size] = '\0';
793 return out;
795 #endif
797 /*********************************************************************
798 * _Gettnames (MSVCRT.@)
800 void* CDECL _Gettnames(void)
802 __lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
803 unsigned int i, len, size = sizeof(__lc_time_data);
805 TRACE("\n");
807 for(i=0; i<ARRAY_SIZE(cur->str.str); i++)
808 size += strlen(cur->str.str[i])+1;
809 #if _MSVCR_VER >= 110
810 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++)
811 size += (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
812 #endif
814 ret = malloc(size);
815 if(!ret)
816 return NULL;
817 memcpy(ret, cur, sizeof(*ret));
819 size = 0;
820 for(i=0; i<ARRAY_SIZE(cur->str.str); i++) {
821 len = strlen(cur->str.str[i])+1;
822 memcpy(&ret->data[size], cur->str.str[i], len);
823 ret->str.str[i] = &ret->data[size];
824 size += len;
826 #if _MSVCR_VER >= 110
827 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++) {
828 len = (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
829 memcpy(&ret->data[size], cur->wstr.wstr[i], len);
830 ret->wstr.wstr[i] = (wchar_t*)&ret->data[size];
831 size += len;
833 #endif
835 return ret;
838 #if _MSVCR_VER >= 110
839 /*********************************************************************
840 * _W_Gettnames (MSVCR110.@)
842 void* CDECL _W_Gettnames(void)
844 return _Gettnames();
846 #endif
848 /*********************************************************************
849 * __crtLCMapStringA (MSVCRT.@)
851 int CDECL __crtLCMapStringA(
852 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
853 int dstlen, unsigned int codepage, int xflag
855 WCHAR buf_in[32], *in = buf_in;
856 WCHAR buf_out[32], *out = buf_out;
857 int in_len, out_len, r;
859 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
860 lcid, mapflags, src, srclen, dst, dstlen, codepage, xflag);
862 in_len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, NULL, 0);
863 if (!in_len) return 0;
864 if (in_len > ARRAY_SIZE(buf_in))
866 in = malloc(in_len * sizeof(WCHAR));
867 if (!in) return 0;
870 r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len);
871 if (!r) goto done;
873 if (mapflags & LCMAP_SORTKEY)
875 r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen);
876 goto done;
879 r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0);
880 if (!r) goto done;
881 out_len = r;
882 if (r > ARRAY_SIZE(buf_out))
884 out = malloc(r * sizeof(WCHAR));
885 if (!out)
887 r = 0;
888 goto done;
892 r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len);
893 if (!r) goto done;
895 r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL);
897 done:
898 if (in != buf_in) free(in);
899 if (out != buf_out) free(out);
900 return r;
903 /*********************************************************************
904 * __crtLCMapStringW (MSVCRT.@)
906 int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src,
907 int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
909 FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
910 lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
912 return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
915 /*********************************************************************
916 * __crtCompareStringA (MSVCRT.@)
918 int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
919 const char *src2, int len2 )
921 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
922 lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
923 /* FIXME: probably not entirely right */
924 return CompareStringA( lcid, flags, src1, len1, src2, len2 );
927 /*********************************************************************
928 * __crtCompareStringW (MSVCRT.@)
930 int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1,
931 const wchar_t *src2, int len2 )
933 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
934 lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
935 /* FIXME: probably not entirely right */
936 return CompareStringW( lcid, flags, src1, len1, src2, len2 );
939 /*********************************************************************
940 * __crtGetLocaleInfoW (MSVCRT.@)
942 int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len )
944 FIXME("(lcid %lx, type %lx, %p(%d), partial stub\n", lcid, type, buffer, len );
945 /* FIXME: probably not entirely right */
946 return GetLocaleInfoW( lcid, type, buffer, len );
949 #if _MSVCR_VER >= 110
950 /*********************************************************************
951 * __crtGetLocaleInfoEx (MSVC110.@)
953 int CDECL __crtGetLocaleInfoEx( const WCHAR *locale, LCTYPE type, wchar_t *buffer, int len )
955 TRACE("(%s, %lx, %p, %d)\n", debugstr_w(locale), type, buffer, len);
956 return GetLocaleInfoEx(locale, type, buffer, len);
958 #endif
960 /*********************************************************************
961 * __crtGetStringTypeW(MSVCRT.@)
963 * This function was accepting different number of arguments in older
964 * versions of msvcrt.
966 BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
967 wchar_t *buffer, int len, WORD *out)
969 FIXME("(unk %lx, type %lx, wstr %p(%d), %p) partial stub\n",
970 unk, type, buffer, len, out);
972 return GetStringTypeW(type, buffer, len, out);
975 /*********************************************************************
976 * localeconv (MSVCRT.@)
978 struct lconv* CDECL localeconv(void)
980 return get_locinfo()->lconv;
983 /*********************************************************************
984 * __lconv_init (MSVCRT.@)
986 int CDECL __lconv_init(void)
988 /* this is used to make chars unsigned */
989 cloc_lconv.int_frac_digits = (char)UCHAR_MAX;
990 cloc_lconv.frac_digits = (char)UCHAR_MAX;
991 cloc_lconv.p_cs_precedes = (char)UCHAR_MAX;
992 cloc_lconv.p_sep_by_space = (char)UCHAR_MAX;
993 cloc_lconv.n_cs_precedes = (char)UCHAR_MAX;
994 cloc_lconv.n_sep_by_space = (char)UCHAR_MAX;
995 cloc_lconv.p_sign_posn = (char)UCHAR_MAX;
996 cloc_lconv.n_sign_posn = (char)UCHAR_MAX;
997 return 0;
1000 /*********************************************************************
1001 * ___lc_handle_func (MSVCRT.@)
1003 LCID* CDECL ___lc_handle_func(void)
1005 return (LCID *)get_locinfo()->lc_handle;
1008 #if _MSVCR_VER >= 110
1009 /*********************************************************************
1010 * ___lc_locale_name_func (MSVCR110.@)
1012 wchar_t** CDECL ___lc_locale_name_func(void)
1014 return get_locinfo()->lc_name;
1016 #endif
1018 /*********************************************************************
1019 * ___lc_codepage_func (MSVCRT.@)
1021 unsigned int CDECL ___lc_codepage_func(void)
1023 return get_locinfo()->lc_codepage;
1026 /*********************************************************************
1027 * ___lc_collate_cp_func (MSVCRT.@)
1029 int CDECL ___lc_collate_cp_func(void)
1031 return get_locinfo()->lc_collate_cp;
1034 /* INTERNAL: frees pthreadlocinfo struct */
1035 void free_locinfo(pthreadlocinfo locinfo)
1037 int i;
1039 if(!locinfo)
1040 return;
1042 for(i=LC_MIN+1; i<=LC_MAX; i++) {
1043 if(!locinfo->lc_category[i].refcount
1044 || !InterlockedDecrement((LONG *)locinfo->lc_category[i].refcount)) {
1045 free(locinfo->lc_category[i].locale);
1046 free(locinfo->lc_category[i].refcount);
1048 if(!locinfo->lc_category[i].wrefcount
1049 || !InterlockedDecrement((LONG *)locinfo->lc_category[i].wrefcount)) {
1050 #if _MSVCR_VER >= 110
1051 free(locinfo->lc_name[i]);
1052 #endif
1053 free(locinfo->lc_category[i].wrefcount);
1057 if(locinfo->lconv_num_refcount
1058 && !InterlockedDecrement((LONG *)locinfo->lconv_num_refcount)) {
1059 free(locinfo->lconv->decimal_point);
1060 free(locinfo->lconv->thousands_sep);
1061 free(locinfo->lconv->grouping);
1062 #if _MSVCR_VER >= 100
1063 free(locinfo->lconv->_W_decimal_point);
1064 free(locinfo->lconv->_W_thousands_sep);
1065 #endif
1066 free(locinfo->lconv_num_refcount);
1068 if(locinfo->lconv_mon_refcount
1069 && !InterlockedDecrement((LONG *)locinfo->lconv_mon_refcount)) {
1070 free(locinfo->lconv->int_curr_symbol);
1071 free(locinfo->lconv->currency_symbol);
1072 free(locinfo->lconv->mon_decimal_point);
1073 free(locinfo->lconv->mon_thousands_sep);
1074 free(locinfo->lconv->mon_grouping);
1075 free(locinfo->lconv->positive_sign);
1076 free(locinfo->lconv->negative_sign);
1077 #if _MSVCR_VER >= 100
1078 free(locinfo->lconv->_W_int_curr_symbol);
1079 free(locinfo->lconv->_W_currency_symbol);
1080 free(locinfo->lconv->_W_mon_decimal_point);
1081 free(locinfo->lconv->_W_mon_thousands_sep);
1082 free(locinfo->lconv->_W_positive_sign);
1083 free(locinfo->lconv->_W_negative_sign);
1084 #endif
1085 free(locinfo->lconv_mon_refcount);
1087 if(locinfo->lconv_intl_refcount
1088 && !InterlockedDecrement((LONG *)locinfo->lconv_intl_refcount)) {
1089 free(locinfo->lconv_intl_refcount);
1090 free(locinfo->lconv);
1093 if(locinfo->ctype1_refcount
1094 && !InterlockedDecrement((LONG *)locinfo->ctype1_refcount)) {
1095 free(locinfo->ctype1_refcount);
1096 free(locinfo->ctype1);
1097 free((void*)locinfo->pclmap);
1098 free((void*)locinfo->pcumap);
1101 if(locinfo->lc_time_curr && !InterlockedDecrement(&locinfo->lc_time_curr->refcount)
1102 && locinfo->lc_time_curr != &cloc_time_data)
1103 free(locinfo->lc_time_curr);
1105 if(InterlockedDecrement((LONG *)&locinfo->refcount))
1106 return;
1108 free(locinfo);
1111 /* INTERNAL: frees pthreadmbcinfo struct */
1112 void free_mbcinfo(pthreadmbcinfo mbcinfo)
1114 if(!mbcinfo)
1115 return;
1117 if(InterlockedDecrement(&mbcinfo->refcount))
1118 return;
1120 free(mbcinfo);
1123 _locale_t CDECL get_current_locale_noalloc(_locale_t locale)
1125 thread_data_t *data = msvcrt_get_thread_data();
1127 update_thread_locale(data);
1128 locale->locinfo = data->locinfo;
1129 locale->mbcinfo = data->mbcinfo;
1131 grab_locinfo(locale->locinfo);
1132 InterlockedIncrement(&locale->mbcinfo->refcount);
1133 return locale;
1136 void CDECL free_locale_noalloc(_locale_t locale)
1138 free_locinfo(locale->locinfo);
1139 free_mbcinfo(locale->mbcinfo);
1142 /*********************************************************************
1143 * _get_current_locale (MSVCRT.@)
1145 _locale_t CDECL _get_current_locale(void)
1147 _locale_t loc = malloc(sizeof(_locale_tstruct));
1148 if(!loc)
1149 return NULL;
1151 return get_current_locale_noalloc(loc);
1154 /*********************************************************************
1155 * _free_locale (MSVCRT.@)
1157 void CDECL _free_locale(_locale_t locale)
1159 if (!locale)
1160 return;
1162 free_locale_noalloc(locale);
1163 free(locale);
1166 static inline BOOL category_needs_update(int cat,
1167 const threadlocinfo *locinfo, LCID lcid, unsigned short cp)
1169 if(!locinfo) return TRUE;
1170 return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage;
1173 static __lc_time_data* create_time_data(LCID lcid)
1175 static const DWORD time_data[] = {
1176 LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
1177 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
1178 LOCALE_SABBREVDAYNAME6,
1179 LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
1180 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
1181 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
1182 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
1183 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
1184 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
1185 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
1186 LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
1187 LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
1188 LOCALE_S1159, LOCALE_S2359,
1189 LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
1190 LOCALE_STIMEFORMAT
1193 __lc_time_data *cur;
1194 int i, ret, size;
1196 size = sizeof(__lc_time_data);
1197 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1198 ret = GetLocaleInfoA(lcid, time_data[i], NULL, 0);
1199 if(!ret)
1200 return NULL;
1201 size += ret;
1203 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1204 ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0);
1205 if(!ret)
1206 return NULL;
1207 size += ret*sizeof(wchar_t);
1208 #endif
1210 #if _MSVCR_VER >= 110
1211 size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(wchar_t);
1212 #endif
1214 cur = malloc(size);
1215 if(!cur)
1216 return NULL;
1218 ret = 0;
1219 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1220 cur->str.str[i] = &cur->data[ret];
1221 ret += GetLocaleInfoA(lcid, time_data[i], &cur->data[ret], size-ret);
1223 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1224 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1225 cur->wstr.wstr[i] = (wchar_t*)&cur->data[ret];
1226 ret += GetLocaleInfoW(lcid, time_data[i],
1227 (wchar_t*)&cur->data[ret], size-ret)*sizeof(wchar_t);
1229 #endif
1230 #if _MSVCR_VER >= 110
1231 cur->locname = (wchar_t*)&cur->data[ret];
1232 LCIDToLocaleName(lcid, (wchar_t*)&cur->data[ret], (size-ret)/sizeof(wchar_t), 0);
1233 #else
1234 cur->lcid = lcid;
1235 #endif
1236 cur->unk = 1;
1237 cur->refcount = 1;
1239 return cur;
1242 static pthreadlocinfo create_locinfo(int category,
1243 const char *locale, const threadlocinfo *old_locinfo)
1245 static const char collate[] = "COLLATE=";
1246 static const char ctype[] = "CTYPE=";
1247 static const char monetary[] = "MONETARY=";
1248 static const char numeric[] = "NUMERIC=";
1249 static const char time[] = "TIME=";
1251 pthreadlocinfo locinfo;
1252 LCID lcid[6] = { 0 };
1253 unsigned short cp[6] = { 0 };
1254 const char *locale_name[6] = { 0 };
1255 int val, locale_len[6] = { 0 };
1256 char buf[256];
1257 BOOL sname;
1258 #if _MSVCR_VER >= 100
1259 wchar_t wbuf[256];
1260 #endif
1261 int i;
1263 TRACE("(%d %s)\n", category, locale);
1265 if(category<LC_MIN || category>LC_MAX || !locale)
1266 return NULL;
1268 if(locale[0]=='C' && !locale[1]) {
1269 lcid[0] = 0;
1270 cp[0] = CP_ACP;
1271 } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
1272 const char *p;
1274 while(1) {
1275 locale += 3; /* LC_ */
1276 if(!memcmp(locale, collate, sizeof(collate)-1)) {
1277 i = LC_COLLATE;
1278 locale += sizeof(collate)-1;
1279 } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
1280 i = LC_CTYPE;
1281 locale += sizeof(ctype)-1;
1282 } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
1283 i = LC_MONETARY;
1284 locale += sizeof(monetary)-1;
1285 } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
1286 i = LC_NUMERIC;
1287 locale += sizeof(numeric)-1;
1288 } else if(!memcmp(locale, time, sizeof(time)-1)) {
1289 i = LC_TIME;
1290 locale += sizeof(time)-1;
1291 } else
1292 return NULL;
1294 p = strchr(locale, ';');
1295 if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
1296 lcid[i] = 0;
1297 cp[i] = CP_ACP;
1298 } else if(p) {
1299 memcpy(buf, locale, p-locale);
1300 buf[p-locale] = '\0';
1301 lcid[i] = locale_to_LCID(buf, &cp[i], &sname);
1302 if(sname) {
1303 locale_name[i] = locale;
1304 locale_len[i] = p-locale;
1306 } else {
1307 lcid[i] = locale_to_LCID(locale, &cp[i], &sname);
1308 if(sname) {
1309 locale_name[i] = locale;
1310 locale_len[i] = strlen(locale);
1314 if(lcid[i] == -1)
1315 return NULL;
1317 if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
1318 break;
1320 locale = p+1;
1322 } else {
1323 lcid[0] = locale_to_LCID(locale, &cp[0], &sname);
1324 if(lcid[0] == -1)
1325 return NULL;
1326 if(sname) {
1327 locale_name[0] = locale;
1328 locale_len[0] = strlen(locale);
1331 for(i=1; i<6; i++) {
1332 lcid[i] = lcid[0];
1333 cp[i] = cp[0];
1334 locale_name[i] = locale_name[0];
1335 locale_len[i] = locale_len[0];
1339 for(i=1; i<6; i++) {
1340 if(category!=LC_ALL && category!=i) {
1341 if(old_locinfo) {
1342 lcid[i] = old_locinfo->lc_handle[i];
1343 cp[i] = old_locinfo->lc_id[i].wCodePage;
1344 } else {
1345 lcid[i] = 0;
1346 cp[i] = 0;
1351 locinfo = malloc(sizeof(threadlocinfo));
1352 if(!locinfo)
1353 return NULL;
1355 memset(locinfo, 0, sizeof(threadlocinfo));
1356 locinfo->refcount = 1;
1358 if(locale_name[LC_COLLATE] &&
1359 !init_category_name(locale_name[LC_COLLATE],
1360 locale_len[LC_COLLATE], locinfo, LC_COLLATE)) {
1361 free_locinfo(locinfo);
1362 return NULL;
1365 if(!category_needs_update(LC_COLLATE, old_locinfo,
1366 lcid[LC_COLLATE], cp[LC_COLLATE])) {
1367 copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE);
1368 locinfo->lc_collate_cp = old_locinfo->lc_collate_cp;
1369 } else if(lcid[LC_COLLATE]) {
1370 if(!update_threadlocinfo_category(lcid[LC_COLLATE],
1371 cp[LC_COLLATE], locinfo, LC_COLLATE)) {
1372 free_locinfo(locinfo);
1373 return NULL;
1376 locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage;
1377 } else {
1378 if(!init_category_name("C", 1, locinfo, LC_COLLATE)) {
1379 free_locinfo(locinfo);
1380 return NULL;
1384 if(locale_name[LC_CTYPE] &&
1385 !init_category_name(locale_name[LC_CTYPE],
1386 locale_len[LC_CTYPE], locinfo, LC_CTYPE)) {
1387 free_locinfo(locinfo);
1388 return NULL;
1391 if(!category_needs_update(LC_CTYPE, old_locinfo,
1392 lcid[LC_CTYPE], cp[LC_CTYPE])) {
1393 copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE);
1394 locinfo->lc_codepage = old_locinfo->lc_codepage;
1395 locinfo->lc_clike = old_locinfo->lc_clike;
1396 locinfo->mb_cur_max = old_locinfo->mb_cur_max;
1397 locinfo->ctype1 = old_locinfo->ctype1;
1398 locinfo->ctype1_refcount = old_locinfo->ctype1_refcount;
1399 locinfo->pctype = old_locinfo->pctype;
1400 locinfo->pclmap = old_locinfo->pclmap;
1401 locinfo->pcumap = old_locinfo->pcumap;
1402 if(locinfo->ctype1_refcount)
1403 InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
1404 } else if(lcid[LC_CTYPE]) {
1405 CPINFO cp_info;
1406 int j;
1408 if(!update_threadlocinfo_category(lcid[LC_CTYPE],
1409 cp[LC_CTYPE], locinfo, LC_CTYPE)) {
1410 free_locinfo(locinfo);
1411 return NULL;
1414 locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage;
1415 locinfo->lc_clike = 1;
1416 if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) {
1417 free_locinfo(locinfo);
1418 return NULL;
1420 locinfo->mb_cur_max = cp_info.MaxCharSize;
1422 locinfo->ctype1_refcount = malloc(sizeof(int));
1423 if(!locinfo->ctype1_refcount) {
1424 free_locinfo(locinfo);
1425 return NULL;
1427 *locinfo->ctype1_refcount = 1;
1429 locinfo->ctype1 = malloc(sizeof(short[257]));
1430 locinfo->pclmap = malloc(sizeof(char[256]));
1431 locinfo->pcumap = malloc(sizeof(char[256]));
1432 if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) {
1433 free_locinfo(locinfo);
1434 return NULL;
1437 locinfo->ctype1[0] = 0;
1438 locinfo->pctype = locinfo->ctype1+1;
1440 buf[1] = buf[2] = '\0';
1441 for(i=1; i<257; i++) {
1442 buf[0] = i-1;
1444 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1445 locinfo->ctype1[i] = 0;
1447 GetStringTypeA(lcid[LC_CTYPE], CT_CTYPE1, buf,
1448 1, locinfo->ctype1+i);
1451 for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
1452 for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
1453 locinfo->ctype1[j+1] |= _LEADBYTE;
1455 for(i=0; i<256; i++) {
1456 if(locinfo->pctype[i] & _LEADBYTE)
1457 buf[i] = ' ';
1458 else
1459 buf[i] = i;
1462 LCMapStringA(lcid[LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
1463 (char*)locinfo->pclmap, 256);
1464 LCMapStringA(lcid[LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
1465 (char*)locinfo->pcumap, 256);
1466 } else {
1467 locinfo->lc_clike = 1;
1468 locinfo->mb_cur_max = 1;
1469 locinfo->pctype = MSVCRT__ctype+1;
1470 locinfo->pclmap = cloc_clmap;
1471 locinfo->pcumap = cloc_cumap;
1472 if(!init_category_name("C", 1, locinfo, LC_CTYPE)) {
1473 free_locinfo(locinfo);
1474 return NULL;
1478 if(!category_needs_update(LC_MONETARY, old_locinfo,
1479 lcid[LC_MONETARY], cp[LC_MONETARY]) &&
1480 !category_needs_update(LC_NUMERIC, old_locinfo,
1481 lcid[LC_NUMERIC], cp[LC_NUMERIC])) {
1482 locinfo->lconv = old_locinfo->lconv;
1483 locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount;
1484 if(locinfo->lconv_intl_refcount)
1485 InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
1486 } else if(lcid[LC_MONETARY] || lcid[LC_NUMERIC]) {
1487 locinfo->lconv = malloc(sizeof(struct lconv));
1488 locinfo->lconv_intl_refcount = malloc(sizeof(int));
1489 if(!locinfo->lconv || !locinfo->lconv_intl_refcount) {
1490 free(locinfo->lconv);
1491 free(locinfo->lconv_intl_refcount);
1492 locinfo->lconv = NULL;
1493 locinfo->lconv_intl_refcount = NULL;
1494 free_locinfo(locinfo);
1495 return NULL;
1497 memset(locinfo->lconv, 0, sizeof(struct lconv));
1498 *locinfo->lconv_intl_refcount = 1;
1499 } else {
1500 locinfo->lconv = &cloc_lconv;
1503 if(locale_name[LC_MONETARY] &&
1504 !init_category_name(locale_name[LC_MONETARY],
1505 locale_len[LC_MONETARY], locinfo, LC_MONETARY)) {
1506 free_locinfo(locinfo);
1507 return NULL;
1510 if(!category_needs_update(LC_MONETARY, old_locinfo,
1511 lcid[LC_MONETARY], cp[LC_MONETARY])) {
1512 copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY);
1513 locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount;
1514 if(locinfo->lconv_mon_refcount)
1515 InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
1516 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1517 locinfo->lconv->int_curr_symbol = old_locinfo->lconv->int_curr_symbol;
1518 locinfo->lconv->currency_symbol = old_locinfo->lconv->currency_symbol;
1519 locinfo->lconv->mon_decimal_point = old_locinfo->lconv->mon_decimal_point;
1520 locinfo->lconv->mon_thousands_sep = old_locinfo->lconv->mon_thousands_sep;
1521 locinfo->lconv->mon_grouping = old_locinfo->lconv->mon_grouping;
1522 locinfo->lconv->positive_sign = old_locinfo->lconv->positive_sign;
1523 locinfo->lconv->negative_sign = old_locinfo->lconv->negative_sign;
1524 locinfo->lconv->int_frac_digits = old_locinfo->lconv->int_frac_digits;
1525 locinfo->lconv->frac_digits = old_locinfo->lconv->frac_digits;
1526 locinfo->lconv->p_cs_precedes = old_locinfo->lconv->p_cs_precedes;
1527 locinfo->lconv->p_sep_by_space = old_locinfo->lconv->p_sep_by_space;
1528 locinfo->lconv->n_cs_precedes = old_locinfo->lconv->n_cs_precedes;
1529 locinfo->lconv->n_sep_by_space = old_locinfo->lconv->n_sep_by_space;
1530 locinfo->lconv->p_sign_posn = old_locinfo->lconv->p_sign_posn;
1531 locinfo->lconv->n_sign_posn = old_locinfo->lconv->n_sign_posn;
1532 #if _MSVCR_VER >= 100
1533 locinfo->lconv->_W_int_curr_symbol = old_locinfo->lconv->_W_int_curr_symbol;
1534 locinfo->lconv->_W_currency_symbol = old_locinfo->lconv->_W_currency_symbol;
1535 locinfo->lconv->_W_mon_decimal_point = old_locinfo->lconv->_W_mon_decimal_point;
1536 locinfo->lconv->_W_mon_thousands_sep = old_locinfo->lconv->_W_mon_thousands_sep;
1537 locinfo->lconv->_W_positive_sign = old_locinfo->lconv->_W_positive_sign;
1538 locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign;
1539 #endif
1541 } else if(lcid[LC_MONETARY]) {
1542 if(!update_threadlocinfo_category(lcid[LC_MONETARY],
1543 cp[LC_MONETARY], locinfo, LC_MONETARY)) {
1544 free_locinfo(locinfo);
1545 return NULL;
1548 locinfo->lconv_mon_refcount = malloc(sizeof(int));
1549 if(!locinfo->lconv_mon_refcount) {
1550 free_locinfo(locinfo);
1551 return NULL;
1554 *locinfo->lconv_mon_refcount = 1;
1556 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL
1557 |LOCALE_NOUSEROVERRIDE, buf, 256);
1558 if(i && (locinfo->lconv->int_curr_symbol = malloc(i)))
1559 memcpy(locinfo->lconv->int_curr_symbol, buf, i);
1560 else {
1561 free_locinfo(locinfo);
1562 return NULL;
1565 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SCURRENCY
1566 |LOCALE_NOUSEROVERRIDE, buf, 256);
1567 if(i && (locinfo->lconv->currency_symbol = malloc(i)))
1568 memcpy(locinfo->lconv->currency_symbol, buf, i);
1569 else {
1570 free_locinfo(locinfo);
1571 return NULL;
1574 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP
1575 |LOCALE_NOUSEROVERRIDE, buf, 256);
1576 if(i && (locinfo->lconv->mon_decimal_point = malloc(i)))
1577 memcpy(locinfo->lconv->mon_decimal_point, buf, i);
1578 else {
1579 free_locinfo(locinfo);
1580 return NULL;
1583 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1584 |LOCALE_NOUSEROVERRIDE, buf, 256);
1585 if(i && (locinfo->lconv->mon_thousands_sep = malloc(i)))
1586 memcpy(locinfo->lconv->mon_thousands_sep, buf, i);
1587 else {
1588 free_locinfo(locinfo);
1589 return NULL;
1592 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONGROUPING
1593 |LOCALE_NOUSEROVERRIDE, buf, 256);
1594 if(i>1)
1595 i = i/2 + (buf[i-2]=='0'?0:1);
1596 if(i && (locinfo->lconv->mon_grouping = malloc(i))) {
1597 for(i=0; buf[i+1]==';'; i+=2)
1598 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1599 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1600 if(buf[i] != '0')
1601 locinfo->lconv->mon_grouping[i/2+1] = 127;
1602 } else {
1603 free_locinfo(locinfo);
1604 return NULL;
1607 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN
1608 |LOCALE_NOUSEROVERRIDE, buf, 256);
1609 if(i && (locinfo->lconv->positive_sign = malloc(i)))
1610 memcpy(locinfo->lconv->positive_sign, buf, i);
1611 else {
1612 free_locinfo(locinfo);
1613 return NULL;
1616 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN
1617 |LOCALE_NOUSEROVERRIDE, buf, 256);
1618 if(i && (locinfo->lconv->negative_sign = malloc(i)))
1619 memcpy(locinfo->lconv->negative_sign, buf, i);
1620 else {
1621 free_locinfo(locinfo);
1622 return NULL;
1625 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IINTLCURRDIGITS
1626 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1627 locinfo->lconv->int_frac_digits = val;
1628 else {
1629 free_locinfo(locinfo);
1630 return NULL;
1633 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_ICURRDIGITS
1634 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1635 locinfo->lconv->frac_digits = val;
1636 else {
1637 free_locinfo(locinfo);
1638 return NULL;
1641 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSYMPRECEDES
1642 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1643 locinfo->lconv->p_cs_precedes = val;
1644 else {
1645 free_locinfo(locinfo);
1646 return NULL;
1649 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSEPBYSPACE
1650 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1651 locinfo->lconv->p_sep_by_space = val;
1652 else {
1653 free_locinfo(locinfo);
1654 return NULL;
1657 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSYMPRECEDES
1658 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1659 locinfo->lconv->n_cs_precedes = val;
1660 else {
1661 free_locinfo(locinfo);
1662 return NULL;
1665 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSEPBYSPACE
1666 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1667 locinfo->lconv->n_sep_by_space = val;
1668 else {
1669 free_locinfo(locinfo);
1670 return NULL;
1673 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSIGNPOSN
1674 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1675 locinfo->lconv->p_sign_posn = val;
1676 else {
1677 free_locinfo(locinfo);
1678 return NULL;
1681 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSIGNPOSN
1682 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1683 locinfo->lconv->n_sign_posn = val;
1684 else {
1685 free_locinfo(locinfo);
1686 return NULL;
1689 #if _MSVCR_VER >= 100
1690 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL
1691 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1692 if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t))))
1693 memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t));
1694 else {
1695 free_locinfo(locinfo);
1696 return NULL;
1699 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SCURRENCY
1700 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1701 if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t))))
1702 memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t));
1703 else {
1704 free_locinfo(locinfo);
1705 return NULL;
1708 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP
1709 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1710 if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t))))
1711 memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t));
1712 else {
1713 free_locinfo(locinfo);
1714 return NULL;
1717 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1718 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1719 if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t))))
1720 memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t));
1721 else {
1722 free_locinfo(locinfo);
1723 return NULL;
1726 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN
1727 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1728 if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t))))
1729 memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t));
1730 else {
1731 free_locinfo(locinfo);
1732 return NULL;
1735 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN
1736 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1737 if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t))))
1738 memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t));
1739 else {
1740 free_locinfo(locinfo);
1741 return NULL;
1743 #endif
1744 } else {
1745 if (locinfo->lconv != &cloc_lconv) {
1746 locinfo->lconv->int_curr_symbol = cloc_lconv.int_curr_symbol;
1747 locinfo->lconv->currency_symbol = cloc_lconv.currency_symbol;
1748 locinfo->lconv->mon_decimal_point = cloc_lconv.mon_decimal_point;
1749 locinfo->lconv->mon_thousands_sep = cloc_lconv.mon_thousands_sep;
1750 locinfo->lconv->mon_grouping = cloc_lconv.mon_grouping;
1751 locinfo->lconv->positive_sign = cloc_lconv.positive_sign;
1752 locinfo->lconv->negative_sign = cloc_lconv.negative_sign;
1753 locinfo->lconv->int_frac_digits = cloc_lconv.int_frac_digits;
1754 locinfo->lconv->frac_digits = cloc_lconv.frac_digits;
1755 locinfo->lconv->p_cs_precedes = cloc_lconv.p_cs_precedes;
1756 locinfo->lconv->p_sep_by_space = cloc_lconv.p_sep_by_space;
1757 locinfo->lconv->n_cs_precedes = cloc_lconv.n_cs_precedes;
1758 locinfo->lconv->n_sep_by_space = cloc_lconv.n_sep_by_space;
1759 locinfo->lconv->p_sign_posn = cloc_lconv.p_sign_posn;
1760 locinfo->lconv->n_sign_posn = cloc_lconv.n_sign_posn;
1762 #if _MSVCR_VER >= 100
1763 locinfo->lconv->_W_int_curr_symbol = cloc_lconv._W_int_curr_symbol;
1764 locinfo->lconv->_W_currency_symbol = cloc_lconv._W_currency_symbol;
1765 locinfo->lconv->_W_mon_decimal_point = cloc_lconv._W_mon_decimal_point;
1766 locinfo->lconv->_W_mon_thousands_sep = cloc_lconv._W_mon_thousands_sep;
1767 locinfo->lconv->_W_positive_sign = cloc_lconv._W_positive_sign;
1768 locinfo->lconv->_W_negative_sign = cloc_lconv._W_negative_sign;
1769 #endif
1772 if(!init_category_name("C", 1, locinfo, LC_MONETARY)) {
1773 free_locinfo(locinfo);
1774 return NULL;
1778 if(locale_name[LC_NUMERIC] &&
1779 !init_category_name(locale_name[LC_NUMERIC],
1780 locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1781 free_locinfo(locinfo);
1782 return NULL;
1785 if(!category_needs_update(LC_NUMERIC, old_locinfo,
1786 lcid[LC_NUMERIC], cp[LC_NUMERIC])) {
1787 copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC);
1788 locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount;
1789 if(locinfo->lconv_num_refcount)
1790 InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
1791 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1792 locinfo->lconv->decimal_point = old_locinfo->lconv->decimal_point;
1793 locinfo->lconv->thousands_sep = old_locinfo->lconv->thousands_sep;
1794 locinfo->lconv->grouping = old_locinfo->lconv->grouping;
1795 #if _MSVCR_VER >= 100
1796 locinfo->lconv->_W_decimal_point = old_locinfo->lconv->_W_decimal_point;
1797 locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep;
1798 #endif
1800 } else if(lcid[LC_NUMERIC]) {
1801 if(!update_threadlocinfo_category(lcid[LC_NUMERIC],
1802 cp[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1803 free_locinfo(locinfo);
1804 return NULL;
1807 locinfo->lconv_num_refcount = malloc(sizeof(int));
1808 if(!locinfo->lconv_num_refcount) {
1809 free_locinfo(locinfo);
1810 return NULL;
1813 *locinfo->lconv_num_refcount = 1;
1815 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SDECIMAL
1816 |LOCALE_NOUSEROVERRIDE, buf, 256);
1817 if(i && (locinfo->lconv->decimal_point = malloc(i)))
1818 memcpy(locinfo->lconv->decimal_point, buf, i);
1819 else {
1820 free_locinfo(locinfo);
1821 return NULL;
1824 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_STHOUSAND
1825 |LOCALE_NOUSEROVERRIDE, buf, 256);
1826 if(i && (locinfo->lconv->thousands_sep = malloc(i)))
1827 memcpy(locinfo->lconv->thousands_sep, buf, i);
1828 else {
1829 free_locinfo(locinfo);
1830 return NULL;
1833 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SGROUPING
1834 |LOCALE_NOUSEROVERRIDE, buf, 256);
1835 if(i>1)
1836 i = i/2 + (buf[i-2]=='0'?0:1);
1837 if(i && (locinfo->lconv->grouping = malloc(i))) {
1838 for(i=0; buf[i+1]==';'; i+=2)
1839 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1840 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1841 if(buf[i] != '0')
1842 locinfo->lconv->grouping[i/2+1] = 127;
1843 } else {
1844 free_locinfo(locinfo);
1845 return NULL;
1848 #if _MSVCR_VER >= 100
1849 i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_SDECIMAL
1850 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1851 if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t))))
1852 memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t));
1853 else {
1854 free_locinfo(locinfo);
1855 return NULL;
1858 i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_STHOUSAND
1859 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1860 if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t))))
1861 memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t));
1862 else {
1863 free_locinfo(locinfo);
1864 return NULL;
1866 #endif
1867 } else {
1868 if (locinfo->lconv != &cloc_lconv) {
1869 locinfo->lconv->decimal_point = cloc_lconv.decimal_point;
1870 locinfo->lconv->thousands_sep = cloc_lconv.thousands_sep;
1871 locinfo->lconv->grouping = cloc_lconv.grouping;
1873 #if _MSVCR_VER >= 100
1874 locinfo->lconv->_W_decimal_point = cloc_lconv._W_decimal_point;
1875 locinfo->lconv->_W_thousands_sep = cloc_lconv._W_thousands_sep;
1876 #endif
1879 if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) {
1880 free_locinfo(locinfo);
1881 return NULL;
1885 if(locale_name[LC_TIME] &&
1886 !init_category_name(locale_name[LC_TIME],
1887 locale_len[LC_TIME], locinfo, LC_TIME)) {
1888 free_locinfo(locinfo);
1889 return NULL;
1892 if(!category_needs_update(LC_TIME, old_locinfo,
1893 lcid[LC_TIME], cp[LC_TIME])) {
1894 copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME);
1895 locinfo->lc_time_curr = old_locinfo->lc_time_curr;
1896 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1897 } else if(lcid[LC_TIME]) {
1898 if(!update_threadlocinfo_category(lcid[LC_TIME],
1899 cp[LC_TIME], locinfo, LC_TIME)) {
1900 free_locinfo(locinfo);
1901 return NULL;
1904 locinfo->lc_time_curr = create_time_data(lcid[LC_TIME]);
1905 if(!locinfo->lc_time_curr) {
1906 free_locinfo(locinfo);
1907 return NULL;
1909 } else {
1910 if(!init_category_name("C", 1, locinfo, LC_TIME)) {
1911 free_locinfo(locinfo);
1912 return NULL;
1914 locinfo->lc_time_curr = &cloc_time_data;
1915 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1918 return locinfo;
1921 /*********************************************************************
1922 * _create_locale (MSVCRT.@)
1924 _locale_t CDECL _create_locale(int category, const char *locale)
1926 _locale_t loc;
1928 loc = malloc(sizeof(_locale_tstruct));
1929 if(!loc)
1930 return NULL;
1932 loc->locinfo = create_locinfo(category, locale, NULL);
1933 if(!loc->locinfo) {
1934 free(loc);
1935 return NULL;
1938 loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[LC_CTYPE].wCodePage,
1939 loc->locinfo->lc_handle[LC_CTYPE], NULL);
1940 if(!loc->mbcinfo) {
1941 free_locinfo(loc->locinfo);
1942 free(loc);
1943 return NULL;
1945 return loc;
1948 #if _MSVCR_VER >= 110
1949 /*********************************************************************
1950 * _wcreate_locale (MSVCR110.@)
1952 _locale_t CDECL _wcreate_locale(int category, const wchar_t *locale)
1954 _locale_t loc;
1955 size_t len;
1956 char *str;
1958 if(category<LC_MIN || category>LC_MAX || !locale)
1959 return NULL;
1961 len = wcstombs(NULL, locale, 0);
1962 if(len == -1)
1963 return NULL;
1964 if(!(str = malloc(++len)))
1965 return NULL;
1966 wcstombs(str, locale, len);
1968 loc = _create_locale(category, str);
1970 free(str);
1971 return loc;
1973 #endif
1975 /*********************************************************************
1976 * setlocale (MSVCRT.@)
1978 char* CDECL setlocale(int category, const char* locale)
1980 thread_data_t *data = msvcrt_get_thread_data();
1981 pthreadlocinfo locinfo = get_locinfo(), newlocinfo;
1983 if(category<LC_MIN || category>LC_MAX)
1984 return NULL;
1986 if(!locale) {
1987 if(category == LC_ALL)
1988 return construct_lc_all(locinfo);
1990 return locinfo->lc_category[category].locale;
1993 newlocinfo = create_locinfo(category, locale, locinfo);
1994 if(!newlocinfo) {
1995 WARN("%d %s failed\n", category, locale);
1996 return NULL;
1999 if(locale[0] != 'C' || locale[1] != '\0')
2000 initial_locale = FALSE;
2002 if(data->locale_flags & LOCALE_THREAD)
2004 if(data->locale_flags & LOCALE_FREE)
2005 free_locinfo(data->locinfo);
2006 data->locinfo = newlocinfo;
2008 else
2010 int i;
2012 _lock_locales();
2013 free_locinfo(MSVCRT_locale->locinfo);
2014 MSVCRT_locale->locinfo = newlocinfo;
2016 MSVCRT___lc_codepage = newlocinfo->lc_codepage;
2017 MSVCRT___lc_collate_cp = newlocinfo->lc_collate_cp;
2018 MSVCRT___mb_cur_max = newlocinfo->mb_cur_max;
2019 MSVCRT__pctype = newlocinfo->pctype;
2020 for(i=LC_MIN; i<=LC_MAX; i++)
2021 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2022 _unlock_locales();
2023 update_thread_locale(data);
2026 if(category == LC_ALL)
2027 return construct_lc_all(data->locinfo);
2029 return data->locinfo->lc_category[category].locale;
2032 /*********************************************************************
2033 * _wsetlocale (MSVCRT.@)
2035 wchar_t* CDECL _wsetlocale(int category, const wchar_t* wlocale)
2037 static wchar_t current_lc_all[MAX_LOCALE_LENGTH];
2039 char *locale = NULL;
2040 const char *ret;
2041 size_t len;
2043 if(wlocale) {
2044 len = wcstombs(NULL, wlocale, 0);
2045 if(len == -1)
2046 return NULL;
2048 locale = malloc(++len);
2049 if(!locale)
2050 return NULL;
2052 wcstombs(locale, wlocale, len);
2055 _lock_locales();
2056 ret = setlocale(category, locale);
2057 free(locale);
2059 if(ret && mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
2060 ret = NULL;
2062 _unlock_locales();
2063 return ret ? current_lc_all : NULL;
2066 #if _MSVCR_VER >= 80
2067 /*********************************************************************
2068 * _configthreadlocale (MSVCR80.@)
2070 int CDECL _configthreadlocale(int type)
2072 thread_data_t *data = msvcrt_get_thread_data();
2073 int ret;
2075 ret = (data->locale_flags & LOCALE_THREAD ? _ENABLE_PER_THREAD_LOCALE :
2076 _DISABLE_PER_THREAD_LOCALE);
2078 if(type == _ENABLE_PER_THREAD_LOCALE)
2079 data->locale_flags |= LOCALE_THREAD;
2080 else if(type == _DISABLE_PER_THREAD_LOCALE)
2081 data->locale_flags &= ~LOCALE_THREAD;
2082 else if(type)
2083 ret = -1;
2085 return ret;
2087 #endif
2089 BOOL msvcrt_init_locale(void)
2091 int i;
2093 _lock_locales();
2094 MSVCRT_locale = _create_locale(0, "C");
2095 _unlock_locales();
2096 if(!MSVCRT_locale)
2097 return FALSE;
2099 MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
2100 MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
2101 MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
2102 MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
2103 for(i=LC_MIN; i<=LC_MAX; i++)
2104 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2105 _setmbcp(_MB_CP_ANSI);
2106 return TRUE;
2109 #if _MSVCR_VER >= 120
2110 /*********************************************************************
2111 * wctrans (MSVCR120.@)
2113 wctrans_t CDECL wctrans(const char *property)
2115 static const char str_tolower[] = "tolower";
2116 static const char str_toupper[] = "toupper";
2118 if(!strcmp(property, str_tolower))
2119 return 2;
2120 if(!strcmp(property, str_toupper))
2121 return 1;
2122 return 0;
2124 #endif