po: Update Finnish translation.
[wine.git] / dlls / msvcrt / locale.c
blob16ed68cdebd4286642fa14767b4b279d1b36bce1
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( WCHAR *name, DWORD locale_flags, LPARAM lParam )
260 locale_search_t *res = (locale_search_t *)lParam;
261 const LCID lcid = LocaleNameToLCID( name, 0 );
262 char buff[MAX_ELEM_LEN];
263 unsigned int flags = 0;
265 if (lcid == LOCALE_CUSTOM_UNSPECIFIED) return CONTINUE_LOOKING;
267 #if _MSVCR_VER >= 110
268 if (res->allow_sname && compare_info(lcid,LOCALE_SNAME,buff,res->search_language, TRUE))
270 TRACE(":Found locale: %s->%s\n", res->search_language, buff);
271 res->match_flags = FOUND_SNAME;
272 res->found_lang_id = LANGIDFROMLCID(lcid);
273 return STOP_LOOKING;
275 #endif
277 /* Check Language */
278 if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) ||
279 compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) ||
280 compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE))
282 TRACE(":Found language: %s->%s\n", res->search_language, buff);
283 flags |= FOUND_LANGUAGE;
285 else if (res->match_flags & FOUND_LANGUAGE)
287 return CONTINUE_LOOKING;
290 /* Check Country */
291 if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) ||
292 compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) ||
293 compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE))
295 TRACE("Found country:%s->%s\n", res->search_country, buff);
296 flags |= FOUND_COUNTRY;
298 else if (!flags && (res->match_flags & FOUND_COUNTRY))
300 return CONTINUE_LOOKING;
303 if (flags > res->match_flags)
305 /* Found a better match than previously */
306 res->match_flags = flags;
307 res->found_lang_id = LANGIDFROMLCID(lcid);
309 if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY)) ==
310 (FOUND_LANGUAGE | FOUND_COUNTRY))
312 TRACE(":found exact locale match\n");
313 return STOP_LOOKING;
315 return CONTINUE_LOOKING;
318 /* Internal: Find the LCID for a locale specification */
319 LCID locale_to_LCID(const char *locale, unsigned short *codepage, BOOL *sname)
321 thread_data_t *data = msvcrt_get_thread_data();
322 const char *cp, *region;
323 BOOL is_sname = FALSE;
324 DWORD locale_cp;
325 LCID lcid;
327 if (!strcmp(locale, data->cached_locale)) {
328 if (codepage)
329 *codepage = data->cached_cp;
330 if (sname)
331 *sname = data->cached_sname;
332 return data->cached_lcid;
335 cp = strchr(locale, '.');
336 region = strchr(locale, '_');
338 if(!locale[0] || (cp == locale && !region)) {
339 lcid = GetUserDefaultLCID();
340 } else {
341 locale_search_t search;
343 memset(&search, 0, sizeof(locale_search_t));
344 lstrcpynA(search.search_language, locale, MAX_ELEM_LEN);
345 if(region) {
346 lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN);
347 if(region-locale < MAX_ELEM_LEN)
348 search.search_language[region-locale] = '\0';
349 } else
350 search.search_country[0] = '\0';
352 if(cp) {
353 if(region && cp-region-1<MAX_ELEM_LEN)
354 search.search_country[cp-region-1] = '\0';
355 if(cp-locale < MAX_ELEM_LEN)
356 search.search_language[cp-locale] = '\0';
359 if(!cp && !region)
361 remap_synonym(search.search_language);
362 search.allow_sname = TRUE;
365 EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL);
367 if (!search.match_flags)
368 return -1;
370 /* If we were given something that didn't match, fail */
371 if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE)))
372 return -1;
373 if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
374 return -1;
376 lcid = MAKELCID(search.found_lang_id, SORT_DEFAULT);
377 is_sname = (search.match_flags & FOUND_SNAME) != 0;
380 /* Obtain code page */
381 if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) {
382 GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
383 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
384 if (!locale_cp)
385 locale_cp = GetACP();
386 } else if (!_strnicmp(cp, ".OCP", 4)) {
387 GetLocaleInfoW(lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
388 (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR));
389 #if _MSVCR_VER >= 140
390 } else if (!_strnicmp(cp, ".UTF-8", 6)
391 || !_strnicmp(cp, ".UTF8", 5)) {
392 locale_cp = CP_UTF8;
393 #endif
394 } else {
395 locale_cp = atoi(cp + 1);
397 if (!IsValidCodePage(locale_cp))
398 return -1;
400 if (!locale_cp)
401 return -1;
403 if (codepage)
404 *codepage = locale_cp;
405 if (sname)
406 *sname = is_sname;
408 if (strlen(locale) < sizeof(data->cached_locale)) {
409 strcpy(data->cached_locale, locale);
410 data->cached_lcid = lcid;
411 data->cached_cp = locale_cp;
412 data->cached_sname = is_sname;
415 return lcid;
418 static void copy_threadlocinfo_category(pthreadlocinfo locinfo,
419 const threadlocinfo *old_locinfo, int category)
421 locinfo->lc_handle[category] = old_locinfo->lc_handle[category];
422 locinfo->lc_id[category] = old_locinfo->lc_id[category];
423 if(!locinfo->lc_category[category].locale) {
424 locinfo->lc_category[category].locale = old_locinfo->lc_category[category].locale;
425 locinfo->lc_category[category].refcount = old_locinfo->lc_category[category].refcount;
426 InterlockedIncrement((LONG *)locinfo->lc_category[category].refcount);
428 #if _MSVCR_VER >= 110
429 locinfo->lc_name[category] = old_locinfo->lc_name[category];
430 locinfo->lc_category[category].wrefcount = old_locinfo->lc_category[category].wrefcount;
431 if(locinfo->lc_category[category].wrefcount)
432 InterlockedIncrement((LONG *)locinfo->lc_category[category].wrefcount);
433 #endif
436 static BOOL init_category_name(const char *name, int len,
437 pthreadlocinfo locinfo, int category)
439 locinfo->lc_category[category].locale = malloc(len+1);
440 locinfo->lc_category[category].refcount = malloc(sizeof(int));
441 if(!locinfo->lc_category[category].locale
442 || !locinfo->lc_category[category].refcount) {
443 free(locinfo->lc_category[category].locale);
444 free(locinfo->lc_category[category].refcount);
445 locinfo->lc_category[category].locale = NULL;
446 locinfo->lc_category[category].refcount = NULL;
447 return FALSE;
450 memcpy(locinfo->lc_category[category].locale, name, len);
451 locinfo->lc_category[category].locale[len] = 0;
452 *locinfo->lc_category[category].refcount = 1;
453 return TRUE;
456 #if _MSVCR_VER >= 110
457 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat)
459 LCID lcid = locinfo->lc_handle[cat];
460 WCHAR buf[100];
461 int len;
463 locinfo->lc_category[cat].wrefcount = malloc(sizeof(int));
464 if(!locinfo->lc_category[cat].wrefcount)
465 return FALSE;
466 *locinfo->lc_category[cat].wrefcount = 1;
468 len = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME
469 |LOCALE_NOUSEROVERRIDE, buf, 100);
470 if(!len) return FALSE;
472 if(LocaleNameToLCID(buf, 0) != lcid)
473 len = LCIDToLocaleName(lcid, buf, 100, 0);
475 if(!len || !(locinfo->lc_name[cat] = malloc(len*sizeof(wchar_t))))
476 return FALSE;
478 memcpy(locinfo->lc_name[cat], buf, len*sizeof(wchar_t));
479 return TRUE;
481 #else
482 static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat)
484 return TRUE;
486 #endif
488 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
489 static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp,
490 pthreadlocinfo locinfo, int category)
492 char buf[256], *p;
494 if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) {
495 p = buf;
497 locinfo->lc_id[category].wLanguage = 0;
498 while(*p) {
499 locinfo->lc_id[category].wLanguage *= 16;
501 if(*p <= '9')
502 locinfo->lc_id[category].wLanguage += *p-'0';
503 else
504 locinfo->lc_id[category].wLanguage += *p-'a'+10;
506 p++;
509 locinfo->lc_id[category].wCountry =
510 locinfo->lc_id[category].wLanguage;
513 locinfo->lc_id[category].wCodePage = cp;
515 locinfo->lc_handle[category] = lcid;
517 set_lc_locale_name(locinfo, category);
519 if(!locinfo->lc_category[category].locale) {
520 int len = 0;
522 if (lcid == MAKELANGID( LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK ))
524 /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */
525 strcpy( buf, "Norwegian-Nynorsk" );
526 len = strlen( buf ) + 1;
528 else len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256);
529 buf[len-1] = '_';
530 len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY
531 |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len);
532 buf[len-1] = '.';
533 sprintf(buf+len, "%d", cp);
534 len += strlen(buf+len);
536 return init_category_name(buf, len, locinfo, category);
539 return TRUE;
542 /*********************************************************************
543 * _lock_locales (UCRTBASE.@)
545 void CDECL _lock_locales(void)
547 _lock(_SETLOCALE_LOCK);
550 /*********************************************************************
551 * _unlock_locales (UCRTBASE.@)
553 void CDECL _unlock_locales(void)
555 _unlock(_SETLOCALE_LOCK);
558 static void grab_locinfo(pthreadlocinfo locinfo)
560 int i;
562 InterlockedIncrement((LONG *)&locinfo->refcount);
563 for(i=LC_MIN+1; i<=LC_MAX; i++)
565 InterlockedIncrement((LONG *)locinfo->lc_category[i].refcount);
566 if(locinfo->lc_category[i].wrefcount)
567 InterlockedIncrement((LONG *)locinfo->lc_category[i].wrefcount);
569 if(locinfo->lconv_intl_refcount)
570 InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
571 if(locinfo->lconv_num_refcount)
572 InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
573 if(locinfo->lconv_mon_refcount)
574 InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
575 if(locinfo->ctype1_refcount)
576 InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
577 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
580 static void update_thread_locale(thread_data_t *data)
582 if((data->locale_flags & LOCALE_FREE) && ((data->locale_flags & LOCALE_THREAD) ||
583 (data->locinfo == MSVCRT_locale->locinfo && data->mbcinfo == MSVCRT_locale->mbcinfo)))
584 return;
586 if(data->locale_flags & LOCALE_FREE)
588 free_locinfo(data->locinfo);
589 free_mbcinfo(data->mbcinfo);
592 _lock_locales();
593 data->locinfo = MSVCRT_locale->locinfo;
594 grab_locinfo(data->locinfo);
595 _unlock_locales();
597 _lock(_MB_CP_LOCK);
598 data->mbcinfo = MSVCRT_locale->mbcinfo;
599 InterlockedIncrement(&data->mbcinfo->refcount);
600 _unlock(_MB_CP_LOCK);
602 data->locale_flags |= LOCALE_FREE;
605 /* INTERNAL: returns threadlocinfo struct */
606 pthreadlocinfo CDECL get_locinfo(void) {
607 thread_data_t *data = msvcrt_get_thread_data();
608 update_thread_locale(data);
609 return data->locinfo;
612 /* INTERNAL: returns pthreadmbcinfo struct */
613 pthreadmbcinfo CDECL get_mbcinfo(void) {
614 thread_data_t *data = msvcrt_get_thread_data();
615 update_thread_locale(data);
616 return data->mbcinfo;
619 /* INTERNAL: constructs string returned by setlocale */
620 static inline char* construct_lc_all(pthreadlocinfo locinfo) {
621 static char current_lc_all[MAX_LOCALE_LENGTH];
623 int i;
625 for(i=LC_MIN+1; i<LC_MAX; i++) {
626 if(strcmp(locinfo->lc_category[i].locale,
627 locinfo->lc_category[i+1].locale))
628 break;
631 if(i==LC_MAX)
632 return locinfo->lc_category[LC_COLLATE].locale;
634 sprintf(current_lc_all,
635 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
636 locinfo->lc_category[LC_COLLATE].locale,
637 locinfo->lc_category[LC_CTYPE].locale,
638 locinfo->lc_category[LC_MONETARY].locale,
639 locinfo->lc_category[LC_NUMERIC].locale,
640 locinfo->lc_category[LC_TIME].locale);
642 return current_lc_all;
646 /*********************************************************************
647 * _Getdays (MSVCRT.@)
649 char* CDECL _Getdays(void)
651 __lc_time_data *cur = get_locinfo()->lc_time_curr;
652 int i, len, size = 0;
653 char *out;
655 TRACE("\n");
657 for(i=0; i<7; i++) {
658 size += strlen(cur->str.names.short_wday[i]) + 1;
659 size += strlen(cur->str.names.wday[i]) + 1;
661 out = malloc(size+1);
662 if(!out)
663 return NULL;
665 size = 0;
666 for(i=0; i<7; i++) {
667 out[size++] = ':';
668 len = strlen(cur->str.names.short_wday[i]);
669 memcpy(&out[size], cur->str.names.short_wday[i], len);
670 size += len;
672 out[size++] = ':';
673 len = strlen(cur->str.names.wday[i]);
674 memcpy(&out[size], cur->str.names.wday[i], len);
675 size += len;
677 out[size] = '\0';
679 return out;
682 #if _MSVCR_VER >= 110
683 /*********************************************************************
684 * _W_Getdays (MSVCR110.@)
686 wchar_t* CDECL _W_Getdays(void)
688 __lc_time_data *cur = get_locinfo()->lc_time_curr;
689 wchar_t *out;
690 int i, len, size = 0;
692 TRACE("\n");
694 for(i=0; i<7; i++) {
695 size += wcslen(cur->wstr.names.short_wday[i]) + 1;
696 size += wcslen(cur->wstr.names.wday[i]) + 1;
698 out = malloc((size+1)*sizeof(*out));
699 if(!out)
700 return NULL;
702 size = 0;
703 for(i=0; i<7; i++) {
704 out[size++] = ':';
705 len = wcslen(cur->wstr.names.short_wday[i]);
706 memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
707 size += len;
709 out[size++] = ':';
710 len = wcslen(cur->wstr.names.wday[i]);
711 memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
712 size += len;
714 out[size] = '\0';
716 return out;
718 #endif
720 /*********************************************************************
721 * _Getmonths (MSVCRT.@)
723 char* CDECL _Getmonths(void)
725 __lc_time_data *cur = get_locinfo()->lc_time_curr;
726 int i, len, size = 0;
727 char *out;
729 TRACE("\n");
731 for(i=0; i<12; i++) {
732 size += strlen(cur->str.names.short_mon[i]) + 1;
733 size += strlen(cur->str.names.mon[i]) + 1;
735 out = malloc(size+1);
736 if(!out)
737 return NULL;
739 size = 0;
740 for(i=0; i<12; i++) {
741 out[size++] = ':';
742 len = strlen(cur->str.names.short_mon[i]);
743 memcpy(&out[size], cur->str.names.short_mon[i], len);
744 size += len;
746 out[size++] = ':';
747 len = strlen(cur->str.names.mon[i]);
748 memcpy(&out[size], cur->str.names.mon[i], len);
749 size += len;
751 out[size] = '\0';
753 return out;
756 #if _MSVCR_VER >= 110
757 /*********************************************************************
758 * _W_Getmonths (MSVCR110.@)
760 wchar_t* CDECL _W_Getmonths(void)
762 __lc_time_data *cur = get_locinfo()->lc_time_curr;
763 wchar_t *out;
764 int i, len, size = 0;
766 TRACE("\n");
768 for(i=0; i<12; i++) {
769 size += wcslen(cur->wstr.names.short_mon[i]) + 1;
770 size += wcslen(cur->wstr.names.mon[i]) + 1;
772 out = malloc((size+1)*sizeof(*out));
773 if(!out)
774 return NULL;
776 size = 0;
777 for(i=0; i<12; i++) {
778 out[size++] = ':';
779 len = wcslen(cur->wstr.names.short_mon[i]);
780 memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
781 size += len;
783 out[size++] = ':';
784 len = wcslen(cur->wstr.names.mon[i]);
785 memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
786 size += len;
788 out[size] = '\0';
790 return out;
792 #endif
794 /*********************************************************************
795 * _Gettnames (MSVCRT.@)
797 void* CDECL _Gettnames(void)
799 __lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
800 unsigned int i, len, size = sizeof(__lc_time_data);
802 TRACE("\n");
804 for(i=0; i<ARRAY_SIZE(cur->str.str); i++)
805 size += strlen(cur->str.str[i])+1;
806 #if _MSVCR_VER >= 110
807 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++)
808 size += (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
809 #endif
811 ret = malloc(size);
812 if(!ret)
813 return NULL;
814 memcpy(ret, cur, sizeof(*ret));
816 size = 0;
817 for(i=0; i<ARRAY_SIZE(cur->str.str); i++) {
818 len = strlen(cur->str.str[i])+1;
819 memcpy(&ret->data[size], cur->str.str[i], len);
820 ret->str.str[i] = &ret->data[size];
821 size += len;
823 #if _MSVCR_VER >= 110
824 for(i=0; i<ARRAY_SIZE(cur->wstr.wstr); i++) {
825 len = (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t);
826 memcpy(&ret->data[size], cur->wstr.wstr[i], len);
827 ret->wstr.wstr[i] = (wchar_t*)&ret->data[size];
828 size += len;
830 #endif
832 return ret;
835 #if _MSVCR_VER >= 110
836 /*********************************************************************
837 * _W_Gettnames (MSVCR110.@)
839 void* CDECL _W_Gettnames(void)
841 return _Gettnames();
843 #endif
845 /*********************************************************************
846 * __crtLCMapStringA (MSVCRT.@)
848 int CDECL __crtLCMapStringA(
849 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
850 int dstlen, unsigned int codepage, int xflag
852 WCHAR buf_in[32], *in = buf_in;
853 WCHAR buf_out[32], *out = buf_out;
854 int in_len, out_len, r;
856 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
857 lcid, mapflags, src, srclen, dst, dstlen, codepage, xflag);
859 in_len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, NULL, 0);
860 if (!in_len) return 0;
861 if (in_len > ARRAY_SIZE(buf_in))
863 in = malloc(in_len * sizeof(WCHAR));
864 if (!in) return 0;
867 r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len);
868 if (!r) goto done;
870 if (mapflags & LCMAP_SORTKEY)
872 r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen);
873 goto done;
876 r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0);
877 if (!r) goto done;
878 out_len = r;
879 if (r > ARRAY_SIZE(buf_out))
881 out = malloc(r * sizeof(WCHAR));
882 if (!out)
884 r = 0;
885 goto done;
889 r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len);
890 if (!r) goto done;
892 r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL);
894 done:
895 if (in != buf_in) free(in);
896 if (out != buf_out) free(out);
897 return r;
900 /*********************************************************************
901 * __crtLCMapStringW (MSVCRT.@)
903 int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src,
904 int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
906 FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
907 lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
909 return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
912 /*********************************************************************
913 * __crtCompareStringA (MSVCRT.@)
915 int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
916 const char *src2, int len2 )
918 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
919 lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
920 /* FIXME: probably not entirely right */
921 return CompareStringA( lcid, flags, src1, len1, src2, len2 );
924 /*********************************************************************
925 * __crtCompareStringW (MSVCRT.@)
927 int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1,
928 const wchar_t *src2, int len2 )
930 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
931 lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
932 /* FIXME: probably not entirely right */
933 return CompareStringW( lcid, flags, src1, len1, src2, len2 );
936 /*********************************************************************
937 * __crtGetLocaleInfoW (MSVCRT.@)
939 int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len )
941 FIXME("(lcid %lx, type %lx, %p(%d), partial stub\n", lcid, type, buffer, len );
942 /* FIXME: probably not entirely right */
943 return GetLocaleInfoW( lcid, type, buffer, len );
946 #if _MSVCR_VER >= 110
947 /*********************************************************************
948 * __crtGetLocaleInfoEx (MSVC110.@)
950 int CDECL __crtGetLocaleInfoEx( const WCHAR *locale, LCTYPE type, wchar_t *buffer, int len )
952 TRACE("(%s, %lx, %p, %d)\n", debugstr_w(locale), type, buffer, len);
953 return GetLocaleInfoEx(locale, type, buffer, len);
955 #endif
957 /*********************************************************************
958 * __crtGetStringTypeW(MSVCRT.@)
960 * This function was accepting different number of arguments in older
961 * versions of msvcrt.
963 BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
964 wchar_t *buffer, int len, WORD *out)
966 FIXME("(unk %lx, type %lx, wstr %p(%d), %p) partial stub\n",
967 unk, type, buffer, len, out);
969 return GetStringTypeW(type, buffer, len, out);
972 /*********************************************************************
973 * localeconv (MSVCRT.@)
975 struct lconv* CDECL localeconv(void)
977 return get_locinfo()->lconv;
980 /*********************************************************************
981 * __lconv_init (MSVCRT.@)
983 int CDECL __lconv_init(void)
985 /* this is used to make chars unsigned */
986 cloc_lconv.int_frac_digits = (char)UCHAR_MAX;
987 cloc_lconv.frac_digits = (char)UCHAR_MAX;
988 cloc_lconv.p_cs_precedes = (char)UCHAR_MAX;
989 cloc_lconv.p_sep_by_space = (char)UCHAR_MAX;
990 cloc_lconv.n_cs_precedes = (char)UCHAR_MAX;
991 cloc_lconv.n_sep_by_space = (char)UCHAR_MAX;
992 cloc_lconv.p_sign_posn = (char)UCHAR_MAX;
993 cloc_lconv.n_sign_posn = (char)UCHAR_MAX;
994 return 0;
997 /*********************************************************************
998 * ___lc_handle_func (MSVCRT.@)
1000 LCID* CDECL ___lc_handle_func(void)
1002 return (LCID *)get_locinfo()->lc_handle;
1005 #if _MSVCR_VER >= 110
1006 /*********************************************************************
1007 * ___lc_locale_name_func (MSVCR110.@)
1009 wchar_t** CDECL ___lc_locale_name_func(void)
1011 return get_locinfo()->lc_name;
1013 #endif
1015 /*********************************************************************
1016 * ___lc_codepage_func (MSVCRT.@)
1018 unsigned int CDECL ___lc_codepage_func(void)
1020 return get_locinfo()->lc_codepage;
1023 /*********************************************************************
1024 * ___lc_collate_cp_func (MSVCRT.@)
1026 int CDECL ___lc_collate_cp_func(void)
1028 return get_locinfo()->lc_collate_cp;
1031 /* INTERNAL: frees pthreadlocinfo struct */
1032 void free_locinfo(pthreadlocinfo locinfo)
1034 int i;
1036 if(!locinfo)
1037 return;
1039 for(i=LC_MIN+1; i<=LC_MAX; i++) {
1040 if(!locinfo->lc_category[i].refcount
1041 || !InterlockedDecrement((LONG *)locinfo->lc_category[i].refcount)) {
1042 free(locinfo->lc_category[i].locale);
1043 free(locinfo->lc_category[i].refcount);
1045 if(!locinfo->lc_category[i].wrefcount
1046 || !InterlockedDecrement((LONG *)locinfo->lc_category[i].wrefcount)) {
1047 #if _MSVCR_VER >= 110
1048 free(locinfo->lc_name[i]);
1049 #endif
1050 free(locinfo->lc_category[i].wrefcount);
1054 if(locinfo->lconv_num_refcount
1055 && !InterlockedDecrement((LONG *)locinfo->lconv_num_refcount)) {
1056 free(locinfo->lconv->decimal_point);
1057 free(locinfo->lconv->thousands_sep);
1058 free(locinfo->lconv->grouping);
1059 #if _MSVCR_VER >= 100
1060 free(locinfo->lconv->_W_decimal_point);
1061 free(locinfo->lconv->_W_thousands_sep);
1062 #endif
1063 free(locinfo->lconv_num_refcount);
1065 if(locinfo->lconv_mon_refcount
1066 && !InterlockedDecrement((LONG *)locinfo->lconv_mon_refcount)) {
1067 free(locinfo->lconv->int_curr_symbol);
1068 free(locinfo->lconv->currency_symbol);
1069 free(locinfo->lconv->mon_decimal_point);
1070 free(locinfo->lconv->mon_thousands_sep);
1071 free(locinfo->lconv->mon_grouping);
1072 free(locinfo->lconv->positive_sign);
1073 free(locinfo->lconv->negative_sign);
1074 #if _MSVCR_VER >= 100
1075 free(locinfo->lconv->_W_int_curr_symbol);
1076 free(locinfo->lconv->_W_currency_symbol);
1077 free(locinfo->lconv->_W_mon_decimal_point);
1078 free(locinfo->lconv->_W_mon_thousands_sep);
1079 free(locinfo->lconv->_W_positive_sign);
1080 free(locinfo->lconv->_W_negative_sign);
1081 #endif
1082 free(locinfo->lconv_mon_refcount);
1084 if(locinfo->lconv_intl_refcount
1085 && !InterlockedDecrement((LONG *)locinfo->lconv_intl_refcount)) {
1086 free(locinfo->lconv_intl_refcount);
1087 free(locinfo->lconv);
1090 if(locinfo->ctype1_refcount
1091 && !InterlockedDecrement((LONG *)locinfo->ctype1_refcount)) {
1092 free(locinfo->ctype1_refcount);
1093 free(locinfo->ctype1);
1094 free((void*)locinfo->pclmap);
1095 free((void*)locinfo->pcumap);
1098 if(locinfo->lc_time_curr && !InterlockedDecrement(&locinfo->lc_time_curr->refcount)
1099 && locinfo->lc_time_curr != &cloc_time_data)
1100 free(locinfo->lc_time_curr);
1102 if(InterlockedDecrement((LONG *)&locinfo->refcount))
1103 return;
1105 free(locinfo);
1108 /* INTERNAL: frees pthreadmbcinfo struct */
1109 void free_mbcinfo(pthreadmbcinfo mbcinfo)
1111 if(!mbcinfo)
1112 return;
1114 if(InterlockedDecrement(&mbcinfo->refcount))
1115 return;
1117 free(mbcinfo);
1120 _locale_t CDECL get_current_locale_noalloc(_locale_t locale)
1122 thread_data_t *data = msvcrt_get_thread_data();
1124 update_thread_locale(data);
1125 locale->locinfo = data->locinfo;
1126 locale->mbcinfo = data->mbcinfo;
1128 grab_locinfo(locale->locinfo);
1129 InterlockedIncrement(&locale->mbcinfo->refcount);
1130 return locale;
1133 void CDECL free_locale_noalloc(_locale_t locale)
1135 free_locinfo(locale->locinfo);
1136 free_mbcinfo(locale->mbcinfo);
1139 /*********************************************************************
1140 * _get_current_locale (MSVCRT.@)
1142 _locale_t CDECL _get_current_locale(void)
1144 _locale_t loc = malloc(sizeof(_locale_tstruct));
1145 if(!loc)
1146 return NULL;
1148 return get_current_locale_noalloc(loc);
1151 /*********************************************************************
1152 * _free_locale (MSVCRT.@)
1154 void CDECL _free_locale(_locale_t locale)
1156 if (!locale)
1157 return;
1159 free_locale_noalloc(locale);
1160 free(locale);
1163 static inline BOOL category_needs_update(int cat,
1164 const threadlocinfo *locinfo, LCID lcid, unsigned short cp)
1166 if(!locinfo) return TRUE;
1167 return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage;
1170 static __lc_time_data* create_time_data(LCID lcid)
1172 static const DWORD time_data[] = {
1173 LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
1174 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
1175 LOCALE_SABBREVDAYNAME6,
1176 LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
1177 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
1178 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
1179 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
1180 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
1181 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
1182 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
1183 LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
1184 LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
1185 LOCALE_S1159, LOCALE_S2359,
1186 LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
1187 LOCALE_STIMEFORMAT
1190 __lc_time_data *cur;
1191 int i, ret, size;
1193 size = sizeof(__lc_time_data);
1194 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1195 ret = GetLocaleInfoA(lcid, time_data[i], NULL, 0);
1196 if(!ret)
1197 return NULL;
1198 size += ret;
1200 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1201 ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0);
1202 if(!ret)
1203 return NULL;
1204 size += ret*sizeof(wchar_t);
1205 #endif
1207 #if _MSVCR_VER >= 110
1208 size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(wchar_t);
1209 #endif
1211 cur = malloc(size);
1212 if(!cur)
1213 return NULL;
1215 ret = 0;
1216 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1217 cur->str.str[i] = &cur->data[ret];
1218 ret += GetLocaleInfoA(lcid, time_data[i], &cur->data[ret], size-ret);
1220 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1221 for(i=0; i<ARRAY_SIZE(time_data); i++) {
1222 cur->wstr.wstr[i] = (wchar_t*)&cur->data[ret];
1223 ret += GetLocaleInfoW(lcid, time_data[i],
1224 (wchar_t*)&cur->data[ret], size-ret)*sizeof(wchar_t);
1226 #endif
1227 #if _MSVCR_VER >= 110
1228 cur->locname = (wchar_t*)&cur->data[ret];
1229 LCIDToLocaleName(lcid, (wchar_t*)&cur->data[ret], (size-ret)/sizeof(wchar_t), 0);
1230 #else
1231 cur->lcid = lcid;
1232 #endif
1233 cur->unk = 1;
1234 cur->refcount = 1;
1236 return cur;
1239 static pthreadlocinfo create_locinfo(int category,
1240 const char *locale, const threadlocinfo *old_locinfo)
1242 static const char collate[] = "COLLATE=";
1243 static const char ctype[] = "CTYPE=";
1244 static const char monetary[] = "MONETARY=";
1245 static const char numeric[] = "NUMERIC=";
1246 static const char time[] = "TIME=";
1248 pthreadlocinfo locinfo;
1249 LCID lcid[6] = { 0 };
1250 unsigned short cp[6] = { 0 };
1251 const char *locale_name[6] = { 0 };
1252 int val, locale_len[6] = { 0 };
1253 char buf[256];
1254 BOOL sname;
1255 #if _MSVCR_VER >= 100
1256 wchar_t wbuf[256];
1257 #endif
1258 int i;
1260 TRACE("(%d %s)\n", category, locale);
1262 if(category<LC_MIN || category>LC_MAX || !locale)
1263 return NULL;
1265 if(locale[0]=='C' && !locale[1]) {
1266 lcid[0] = 0;
1267 cp[0] = CP_ACP;
1268 } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
1269 const char *p;
1271 while(1) {
1272 locale += 3; /* LC_ */
1273 if(!memcmp(locale, collate, sizeof(collate)-1)) {
1274 i = LC_COLLATE;
1275 locale += sizeof(collate)-1;
1276 } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
1277 i = LC_CTYPE;
1278 locale += sizeof(ctype)-1;
1279 } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
1280 i = LC_MONETARY;
1281 locale += sizeof(monetary)-1;
1282 } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
1283 i = LC_NUMERIC;
1284 locale += sizeof(numeric)-1;
1285 } else if(!memcmp(locale, time, sizeof(time)-1)) {
1286 i = LC_TIME;
1287 locale += sizeof(time)-1;
1288 } else
1289 return NULL;
1291 p = strchr(locale, ';');
1292 if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
1293 lcid[i] = 0;
1294 cp[i] = CP_ACP;
1295 } else if(p) {
1296 memcpy(buf, locale, p-locale);
1297 buf[p-locale] = '\0';
1298 lcid[i] = locale_to_LCID(buf, &cp[i], &sname);
1299 if(sname) {
1300 locale_name[i] = locale;
1301 locale_len[i] = p-locale;
1303 } else {
1304 lcid[i] = locale_to_LCID(locale, &cp[i], &sname);
1305 if(sname) {
1306 locale_name[i] = locale;
1307 locale_len[i] = strlen(locale);
1311 if(lcid[i] == -1)
1312 return NULL;
1314 if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
1315 break;
1317 locale = p+1;
1319 } else {
1320 lcid[0] = locale_to_LCID(locale, &cp[0], &sname);
1321 if(lcid[0] == -1)
1322 return NULL;
1323 if(sname) {
1324 locale_name[0] = locale;
1325 locale_len[0] = strlen(locale);
1328 for(i=1; i<6; i++) {
1329 lcid[i] = lcid[0];
1330 cp[i] = cp[0];
1331 locale_name[i] = locale_name[0];
1332 locale_len[i] = locale_len[0];
1336 for(i=1; i<6; i++) {
1337 #if _MSVCR_VER < 140
1338 if(i==LC_CTYPE && cp[i]==CP_UTF8) {
1339 locale_name[i] = NULL;
1340 locale_len[i] = 0;
1341 lcid[i] = old_locinfo ? old_locinfo->lc_handle[i] : 0;
1342 cp[i] = old_locinfo ? old_locinfo->lc_id[i].wCodePage : 0;
1344 #endif
1345 if(category!=LC_ALL && category!=i) {
1346 if(old_locinfo) {
1347 lcid[i] = old_locinfo->lc_handle[i];
1348 cp[i] = old_locinfo->lc_id[i].wCodePage;
1349 } else {
1350 lcid[i] = 0;
1351 cp[i] = 0;
1356 locinfo = malloc(sizeof(threadlocinfo));
1357 if(!locinfo)
1358 return NULL;
1360 memset(locinfo, 0, sizeof(threadlocinfo));
1361 locinfo->refcount = 1;
1363 if(locale_name[LC_COLLATE] &&
1364 !init_category_name(locale_name[LC_COLLATE],
1365 locale_len[LC_COLLATE], locinfo, LC_COLLATE)) {
1366 free_locinfo(locinfo);
1367 return NULL;
1370 if(!category_needs_update(LC_COLLATE, old_locinfo,
1371 lcid[LC_COLLATE], cp[LC_COLLATE])) {
1372 copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE);
1373 locinfo->lc_collate_cp = old_locinfo->lc_collate_cp;
1374 } else if(lcid[LC_COLLATE]) {
1375 if(!update_threadlocinfo_category(lcid[LC_COLLATE],
1376 cp[LC_COLLATE], locinfo, LC_COLLATE)) {
1377 free_locinfo(locinfo);
1378 return NULL;
1381 locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage;
1382 } else {
1383 if(!init_category_name("C", 1, locinfo, LC_COLLATE)) {
1384 free_locinfo(locinfo);
1385 return NULL;
1389 if(locale_name[LC_CTYPE] &&
1390 !init_category_name(locale_name[LC_CTYPE],
1391 locale_len[LC_CTYPE], locinfo, LC_CTYPE)) {
1392 free_locinfo(locinfo);
1393 return NULL;
1396 if(!category_needs_update(LC_CTYPE, old_locinfo,
1397 lcid[LC_CTYPE], cp[LC_CTYPE])) {
1398 copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE);
1399 locinfo->lc_codepage = old_locinfo->lc_codepage;
1400 locinfo->lc_clike = old_locinfo->lc_clike;
1401 locinfo->mb_cur_max = old_locinfo->mb_cur_max;
1402 locinfo->ctype1 = old_locinfo->ctype1;
1403 locinfo->ctype1_refcount = old_locinfo->ctype1_refcount;
1404 locinfo->pctype = old_locinfo->pctype;
1405 locinfo->pclmap = old_locinfo->pclmap;
1406 locinfo->pcumap = old_locinfo->pcumap;
1407 if(locinfo->ctype1_refcount)
1408 InterlockedIncrement((LONG *)locinfo->ctype1_refcount);
1409 } else if(lcid[LC_CTYPE]) {
1410 CPINFO cp_info;
1411 int j;
1413 if(!update_threadlocinfo_category(lcid[LC_CTYPE],
1414 cp[LC_CTYPE], locinfo, LC_CTYPE)) {
1415 free_locinfo(locinfo);
1416 return NULL;
1419 locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage;
1420 locinfo->lc_clike = 1;
1421 if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) {
1422 free_locinfo(locinfo);
1423 return NULL;
1425 locinfo->mb_cur_max = cp_info.MaxCharSize;
1427 locinfo->ctype1_refcount = malloc(sizeof(int));
1428 if(!locinfo->ctype1_refcount) {
1429 free_locinfo(locinfo);
1430 return NULL;
1432 *locinfo->ctype1_refcount = 1;
1434 locinfo->ctype1 = malloc(sizeof(short[257]));
1435 locinfo->pclmap = malloc(sizeof(char[256]));
1436 locinfo->pcumap = malloc(sizeof(char[256]));
1437 if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) {
1438 free_locinfo(locinfo);
1439 return NULL;
1442 locinfo->ctype1[0] = 0;
1443 locinfo->pctype = locinfo->ctype1+1;
1445 buf[1] = buf[2] = '\0';
1446 for(i=1; i<257; i++) {
1447 buf[0] = i-1;
1449 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1450 locinfo->ctype1[i] = 0;
1452 GetStringTypeA(lcid[LC_CTYPE], CT_CTYPE1, buf,
1453 1, locinfo->ctype1+i);
1456 for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
1457 for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
1458 locinfo->ctype1[j+1] |= _LEADBYTE;
1460 for(i=0; i<256; i++) {
1461 if(locinfo->pctype[i] & _LEADBYTE)
1462 buf[i] = ' ';
1463 else
1464 buf[i] = i;
1467 LCMapStringA(lcid[LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
1468 (char*)locinfo->pclmap, 256);
1469 LCMapStringA(lcid[LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
1470 (char*)locinfo->pcumap, 256);
1471 } else {
1472 locinfo->lc_clike = 1;
1473 locinfo->mb_cur_max = 1;
1474 locinfo->pctype = MSVCRT__ctype+1;
1475 locinfo->pclmap = cloc_clmap;
1476 locinfo->pcumap = cloc_cumap;
1477 if(!init_category_name("C", 1, locinfo, LC_CTYPE)) {
1478 free_locinfo(locinfo);
1479 return NULL;
1483 if(!category_needs_update(LC_MONETARY, old_locinfo,
1484 lcid[LC_MONETARY], cp[LC_MONETARY]) &&
1485 !category_needs_update(LC_NUMERIC, old_locinfo,
1486 lcid[LC_NUMERIC], cp[LC_NUMERIC])) {
1487 locinfo->lconv = old_locinfo->lconv;
1488 locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount;
1489 if(locinfo->lconv_intl_refcount)
1490 InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount);
1491 } else if(lcid[LC_MONETARY] || lcid[LC_NUMERIC]) {
1492 locinfo->lconv = malloc(sizeof(struct lconv));
1493 locinfo->lconv_intl_refcount = malloc(sizeof(int));
1494 if(!locinfo->lconv || !locinfo->lconv_intl_refcount) {
1495 free(locinfo->lconv);
1496 free(locinfo->lconv_intl_refcount);
1497 locinfo->lconv = NULL;
1498 locinfo->lconv_intl_refcount = NULL;
1499 free_locinfo(locinfo);
1500 return NULL;
1502 memset(locinfo->lconv, 0, sizeof(struct lconv));
1503 *locinfo->lconv_intl_refcount = 1;
1504 } else {
1505 locinfo->lconv = &cloc_lconv;
1508 if(locale_name[LC_MONETARY] &&
1509 !init_category_name(locale_name[LC_MONETARY],
1510 locale_len[LC_MONETARY], locinfo, LC_MONETARY)) {
1511 free_locinfo(locinfo);
1512 return NULL;
1515 if(!category_needs_update(LC_MONETARY, old_locinfo,
1516 lcid[LC_MONETARY], cp[LC_MONETARY])) {
1517 copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY);
1518 locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount;
1519 if(locinfo->lconv_mon_refcount)
1520 InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount);
1521 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1522 locinfo->lconv->int_curr_symbol = old_locinfo->lconv->int_curr_symbol;
1523 locinfo->lconv->currency_symbol = old_locinfo->lconv->currency_symbol;
1524 locinfo->lconv->mon_decimal_point = old_locinfo->lconv->mon_decimal_point;
1525 locinfo->lconv->mon_thousands_sep = old_locinfo->lconv->mon_thousands_sep;
1526 locinfo->lconv->mon_grouping = old_locinfo->lconv->mon_grouping;
1527 locinfo->lconv->positive_sign = old_locinfo->lconv->positive_sign;
1528 locinfo->lconv->negative_sign = old_locinfo->lconv->negative_sign;
1529 locinfo->lconv->int_frac_digits = old_locinfo->lconv->int_frac_digits;
1530 locinfo->lconv->frac_digits = old_locinfo->lconv->frac_digits;
1531 locinfo->lconv->p_cs_precedes = old_locinfo->lconv->p_cs_precedes;
1532 locinfo->lconv->p_sep_by_space = old_locinfo->lconv->p_sep_by_space;
1533 locinfo->lconv->n_cs_precedes = old_locinfo->lconv->n_cs_precedes;
1534 locinfo->lconv->n_sep_by_space = old_locinfo->lconv->n_sep_by_space;
1535 locinfo->lconv->p_sign_posn = old_locinfo->lconv->p_sign_posn;
1536 locinfo->lconv->n_sign_posn = old_locinfo->lconv->n_sign_posn;
1537 #if _MSVCR_VER >= 100
1538 locinfo->lconv->_W_int_curr_symbol = old_locinfo->lconv->_W_int_curr_symbol;
1539 locinfo->lconv->_W_currency_symbol = old_locinfo->lconv->_W_currency_symbol;
1540 locinfo->lconv->_W_mon_decimal_point = old_locinfo->lconv->_W_mon_decimal_point;
1541 locinfo->lconv->_W_mon_thousands_sep = old_locinfo->lconv->_W_mon_thousands_sep;
1542 locinfo->lconv->_W_positive_sign = old_locinfo->lconv->_W_positive_sign;
1543 locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign;
1544 #endif
1546 } else if(lcid[LC_MONETARY]) {
1547 if(!update_threadlocinfo_category(lcid[LC_MONETARY],
1548 cp[LC_MONETARY], locinfo, LC_MONETARY)) {
1549 free_locinfo(locinfo);
1550 return NULL;
1553 locinfo->lconv_mon_refcount = malloc(sizeof(int));
1554 if(!locinfo->lconv_mon_refcount) {
1555 free_locinfo(locinfo);
1556 return NULL;
1559 *locinfo->lconv_mon_refcount = 1;
1561 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL
1562 |LOCALE_NOUSEROVERRIDE, buf, 256);
1563 if(i && (locinfo->lconv->int_curr_symbol = malloc(i)))
1564 memcpy(locinfo->lconv->int_curr_symbol, buf, i);
1565 else {
1566 free_locinfo(locinfo);
1567 return NULL;
1570 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SCURRENCY
1571 |LOCALE_NOUSEROVERRIDE, buf, 256);
1572 if(i && (locinfo->lconv->currency_symbol = malloc(i)))
1573 memcpy(locinfo->lconv->currency_symbol, buf, i);
1574 else {
1575 free_locinfo(locinfo);
1576 return NULL;
1579 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP
1580 |LOCALE_NOUSEROVERRIDE, buf, 256);
1581 if(i && (locinfo->lconv->mon_decimal_point = malloc(i)))
1582 memcpy(locinfo->lconv->mon_decimal_point, buf, i);
1583 else {
1584 free_locinfo(locinfo);
1585 return NULL;
1588 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1589 |LOCALE_NOUSEROVERRIDE, buf, 256);
1590 if(i && (locinfo->lconv->mon_thousands_sep = malloc(i)))
1591 memcpy(locinfo->lconv->mon_thousands_sep, buf, i);
1592 else {
1593 free_locinfo(locinfo);
1594 return NULL;
1597 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONGROUPING
1598 |LOCALE_NOUSEROVERRIDE, buf, 256);
1599 if(i>1)
1600 i = i/2 + (buf[i-2]=='0'?0:1);
1601 if(i && (locinfo->lconv->mon_grouping = malloc(i))) {
1602 for(i=0; buf[i+1]==';'; i+=2)
1603 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1604 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1605 if(buf[i] != '0')
1606 locinfo->lconv->mon_grouping[i/2+1] = 127;
1607 } else {
1608 free_locinfo(locinfo);
1609 return NULL;
1612 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN
1613 |LOCALE_NOUSEROVERRIDE, buf, 256);
1614 if(i && (locinfo->lconv->positive_sign = malloc(i)))
1615 memcpy(locinfo->lconv->positive_sign, buf, i);
1616 else {
1617 free_locinfo(locinfo);
1618 return NULL;
1621 i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN
1622 |LOCALE_NOUSEROVERRIDE, buf, 256);
1623 if(i && (locinfo->lconv->negative_sign = malloc(i)))
1624 memcpy(locinfo->lconv->negative_sign, buf, i);
1625 else {
1626 free_locinfo(locinfo);
1627 return NULL;
1630 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IINTLCURRDIGITS
1631 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1632 locinfo->lconv->int_frac_digits = val;
1633 else {
1634 free_locinfo(locinfo);
1635 return NULL;
1638 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_ICURRDIGITS
1639 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1640 locinfo->lconv->frac_digits = val;
1641 else {
1642 free_locinfo(locinfo);
1643 return NULL;
1646 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSYMPRECEDES
1647 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1648 locinfo->lconv->p_cs_precedes = val;
1649 else {
1650 free_locinfo(locinfo);
1651 return NULL;
1654 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSEPBYSPACE
1655 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1656 locinfo->lconv->p_sep_by_space = val;
1657 else {
1658 free_locinfo(locinfo);
1659 return NULL;
1662 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSYMPRECEDES
1663 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1664 locinfo->lconv->n_cs_precedes = val;
1665 else {
1666 free_locinfo(locinfo);
1667 return NULL;
1670 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSEPBYSPACE
1671 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1672 locinfo->lconv->n_sep_by_space = val;
1673 else {
1674 free_locinfo(locinfo);
1675 return NULL;
1678 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSIGNPOSN
1679 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1680 locinfo->lconv->p_sign_posn = val;
1681 else {
1682 free_locinfo(locinfo);
1683 return NULL;
1686 if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSIGNPOSN
1687 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1688 locinfo->lconv->n_sign_posn = val;
1689 else {
1690 free_locinfo(locinfo);
1691 return NULL;
1694 #if _MSVCR_VER >= 100
1695 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL
1696 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1697 if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t))))
1698 memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t));
1699 else {
1700 free_locinfo(locinfo);
1701 return NULL;
1704 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SCURRENCY
1705 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1706 if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t))))
1707 memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t));
1708 else {
1709 free_locinfo(locinfo);
1710 return NULL;
1713 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP
1714 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1715 if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t))))
1716 memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t));
1717 else {
1718 free_locinfo(locinfo);
1719 return NULL;
1722 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1723 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1724 if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t))))
1725 memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t));
1726 else {
1727 free_locinfo(locinfo);
1728 return NULL;
1731 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN
1732 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1733 if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t))))
1734 memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t));
1735 else {
1736 free_locinfo(locinfo);
1737 return NULL;
1740 i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN
1741 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1742 if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t))))
1743 memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t));
1744 else {
1745 free_locinfo(locinfo);
1746 return NULL;
1748 #endif
1749 } else {
1750 if (locinfo->lconv != &cloc_lconv) {
1751 locinfo->lconv->int_curr_symbol = cloc_lconv.int_curr_symbol;
1752 locinfo->lconv->currency_symbol = cloc_lconv.currency_symbol;
1753 locinfo->lconv->mon_decimal_point = cloc_lconv.mon_decimal_point;
1754 locinfo->lconv->mon_thousands_sep = cloc_lconv.mon_thousands_sep;
1755 locinfo->lconv->mon_grouping = cloc_lconv.mon_grouping;
1756 locinfo->lconv->positive_sign = cloc_lconv.positive_sign;
1757 locinfo->lconv->negative_sign = cloc_lconv.negative_sign;
1758 locinfo->lconv->int_frac_digits = cloc_lconv.int_frac_digits;
1759 locinfo->lconv->frac_digits = cloc_lconv.frac_digits;
1760 locinfo->lconv->p_cs_precedes = cloc_lconv.p_cs_precedes;
1761 locinfo->lconv->p_sep_by_space = cloc_lconv.p_sep_by_space;
1762 locinfo->lconv->n_cs_precedes = cloc_lconv.n_cs_precedes;
1763 locinfo->lconv->n_sep_by_space = cloc_lconv.n_sep_by_space;
1764 locinfo->lconv->p_sign_posn = cloc_lconv.p_sign_posn;
1765 locinfo->lconv->n_sign_posn = cloc_lconv.n_sign_posn;
1767 #if _MSVCR_VER >= 100
1768 locinfo->lconv->_W_int_curr_symbol = cloc_lconv._W_int_curr_symbol;
1769 locinfo->lconv->_W_currency_symbol = cloc_lconv._W_currency_symbol;
1770 locinfo->lconv->_W_mon_decimal_point = cloc_lconv._W_mon_decimal_point;
1771 locinfo->lconv->_W_mon_thousands_sep = cloc_lconv._W_mon_thousands_sep;
1772 locinfo->lconv->_W_positive_sign = cloc_lconv._W_positive_sign;
1773 locinfo->lconv->_W_negative_sign = cloc_lconv._W_negative_sign;
1774 #endif
1777 if(!init_category_name("C", 1, locinfo, LC_MONETARY)) {
1778 free_locinfo(locinfo);
1779 return NULL;
1783 if(locale_name[LC_NUMERIC] &&
1784 !init_category_name(locale_name[LC_NUMERIC],
1785 locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1786 free_locinfo(locinfo);
1787 return NULL;
1790 if(!category_needs_update(LC_NUMERIC, old_locinfo,
1791 lcid[LC_NUMERIC], cp[LC_NUMERIC])) {
1792 copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC);
1793 locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount;
1794 if(locinfo->lconv_num_refcount)
1795 InterlockedIncrement((LONG *)locinfo->lconv_num_refcount);
1796 if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) {
1797 locinfo->lconv->decimal_point = old_locinfo->lconv->decimal_point;
1798 locinfo->lconv->thousands_sep = old_locinfo->lconv->thousands_sep;
1799 locinfo->lconv->grouping = old_locinfo->lconv->grouping;
1800 #if _MSVCR_VER >= 100
1801 locinfo->lconv->_W_decimal_point = old_locinfo->lconv->_W_decimal_point;
1802 locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep;
1803 #endif
1805 } else if(lcid[LC_NUMERIC]) {
1806 if(!update_threadlocinfo_category(lcid[LC_NUMERIC],
1807 cp[LC_NUMERIC], locinfo, LC_NUMERIC)) {
1808 free_locinfo(locinfo);
1809 return NULL;
1812 locinfo->lconv_num_refcount = malloc(sizeof(int));
1813 if(!locinfo->lconv_num_refcount) {
1814 free_locinfo(locinfo);
1815 return NULL;
1818 *locinfo->lconv_num_refcount = 1;
1820 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SDECIMAL
1821 |LOCALE_NOUSEROVERRIDE, buf, 256);
1822 if(i && (locinfo->lconv->decimal_point = malloc(i)))
1823 memcpy(locinfo->lconv->decimal_point, buf, i);
1824 else {
1825 free_locinfo(locinfo);
1826 return NULL;
1829 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_STHOUSAND
1830 |LOCALE_NOUSEROVERRIDE, buf, 256);
1831 if(i && (locinfo->lconv->thousands_sep = malloc(i)))
1832 memcpy(locinfo->lconv->thousands_sep, buf, i);
1833 else {
1834 free_locinfo(locinfo);
1835 return NULL;
1838 i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SGROUPING
1839 |LOCALE_NOUSEROVERRIDE, buf, 256);
1840 if(i>1)
1841 i = i/2 + (buf[i-2]=='0'?0:1);
1842 if(i && (locinfo->lconv->grouping = malloc(i))) {
1843 for(i=0; buf[i+1]==';'; i+=2)
1844 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1845 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1846 if(buf[i] != '0')
1847 locinfo->lconv->grouping[i/2+1] = 127;
1848 } else {
1849 free_locinfo(locinfo);
1850 return NULL;
1853 #if _MSVCR_VER >= 100
1854 i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_SDECIMAL
1855 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1856 if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t))))
1857 memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t));
1858 else {
1859 free_locinfo(locinfo);
1860 return NULL;
1863 i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_STHOUSAND
1864 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1865 if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t))))
1866 memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t));
1867 else {
1868 free_locinfo(locinfo);
1869 return NULL;
1871 #endif
1872 } else {
1873 if (locinfo->lconv != &cloc_lconv) {
1874 locinfo->lconv->decimal_point = cloc_lconv.decimal_point;
1875 locinfo->lconv->thousands_sep = cloc_lconv.thousands_sep;
1876 locinfo->lconv->grouping = cloc_lconv.grouping;
1878 #if _MSVCR_VER >= 100
1879 locinfo->lconv->_W_decimal_point = cloc_lconv._W_decimal_point;
1880 locinfo->lconv->_W_thousands_sep = cloc_lconv._W_thousands_sep;
1881 #endif
1884 if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) {
1885 free_locinfo(locinfo);
1886 return NULL;
1890 if(locale_name[LC_TIME] &&
1891 !init_category_name(locale_name[LC_TIME],
1892 locale_len[LC_TIME], locinfo, LC_TIME)) {
1893 free_locinfo(locinfo);
1894 return NULL;
1897 if(!category_needs_update(LC_TIME, old_locinfo,
1898 lcid[LC_TIME], cp[LC_TIME])) {
1899 copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME);
1900 locinfo->lc_time_curr = old_locinfo->lc_time_curr;
1901 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1902 } else if(lcid[LC_TIME]) {
1903 if(!update_threadlocinfo_category(lcid[LC_TIME],
1904 cp[LC_TIME], locinfo, LC_TIME)) {
1905 free_locinfo(locinfo);
1906 return NULL;
1909 locinfo->lc_time_curr = create_time_data(lcid[LC_TIME]);
1910 if(!locinfo->lc_time_curr) {
1911 free_locinfo(locinfo);
1912 return NULL;
1914 } else {
1915 if(!init_category_name("C", 1, locinfo, LC_TIME)) {
1916 free_locinfo(locinfo);
1917 return NULL;
1919 locinfo->lc_time_curr = &cloc_time_data;
1920 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1923 return locinfo;
1926 /*********************************************************************
1927 * _create_locale (MSVCRT.@)
1929 _locale_t CDECL _create_locale(int category, const char *locale)
1931 _locale_t loc;
1933 loc = malloc(sizeof(_locale_tstruct));
1934 if(!loc)
1935 return NULL;
1937 loc->locinfo = create_locinfo(category, locale, NULL);
1938 if(!loc->locinfo) {
1939 free(loc);
1940 return NULL;
1943 loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[LC_CTYPE].wCodePage,
1944 loc->locinfo->lc_handle[LC_CTYPE], NULL);
1945 if(!loc->mbcinfo) {
1946 free_locinfo(loc->locinfo);
1947 free(loc);
1948 return NULL;
1950 return loc;
1953 #if _MSVCR_VER >= 110
1954 /*********************************************************************
1955 * _wcreate_locale (MSVCR110.@)
1957 _locale_t CDECL _wcreate_locale(int category, const wchar_t *locale)
1959 _locale_t loc;
1960 size_t len;
1961 char *str;
1963 if(category<LC_MIN || category>LC_MAX || !locale)
1964 return NULL;
1966 len = wcstombs(NULL, locale, 0);
1967 if(len == -1)
1968 return NULL;
1969 if(!(str = malloc(++len)))
1970 return NULL;
1971 wcstombs(str, locale, len);
1973 loc = _create_locale(category, str);
1975 free(str);
1976 return loc;
1978 #endif
1980 /*********************************************************************
1981 * setlocale (MSVCRT.@)
1983 char* CDECL setlocale(int category, const char* locale)
1985 thread_data_t *data = msvcrt_get_thread_data();
1986 pthreadlocinfo locinfo = get_locinfo(), newlocinfo;
1988 if(category<LC_MIN || category>LC_MAX)
1989 return NULL;
1991 if(!locale) {
1992 if(category == LC_ALL)
1993 return construct_lc_all(locinfo);
1995 return locinfo->lc_category[category].locale;
1998 newlocinfo = create_locinfo(category, locale, locinfo);
1999 if(!newlocinfo) {
2000 WARN("%d %s failed\n", category, locale);
2001 return NULL;
2004 if(locale[0] != 'C' || locale[1] != '\0')
2005 initial_locale = FALSE;
2007 if(data->locale_flags & LOCALE_THREAD)
2009 if(data->locale_flags & LOCALE_FREE)
2010 free_locinfo(data->locinfo);
2011 data->locinfo = newlocinfo;
2013 else
2015 int i;
2017 _lock_locales();
2018 free_locinfo(MSVCRT_locale->locinfo);
2019 MSVCRT_locale->locinfo = newlocinfo;
2021 MSVCRT___lc_codepage = newlocinfo->lc_codepage;
2022 MSVCRT___lc_collate_cp = newlocinfo->lc_collate_cp;
2023 MSVCRT___mb_cur_max = newlocinfo->mb_cur_max;
2024 MSVCRT__pctype = newlocinfo->pctype;
2025 for(i=LC_MIN; i<=LC_MAX; i++)
2026 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2027 _unlock_locales();
2028 update_thread_locale(data);
2031 if(category == LC_ALL)
2032 return construct_lc_all(data->locinfo);
2034 return data->locinfo->lc_category[category].locale;
2037 /*********************************************************************
2038 * _wsetlocale (MSVCRT.@)
2040 wchar_t* CDECL _wsetlocale(int category, const wchar_t* wlocale)
2042 static wchar_t current_lc_all[MAX_LOCALE_LENGTH];
2044 char *locale = NULL;
2045 const char *ret;
2046 size_t len;
2048 if(wlocale) {
2049 len = wcstombs(NULL, wlocale, 0);
2050 if(len == -1)
2051 return NULL;
2053 locale = malloc(++len);
2054 if(!locale)
2055 return NULL;
2057 wcstombs(locale, wlocale, len);
2060 _lock_locales();
2061 ret = setlocale(category, locale);
2062 free(locale);
2064 if(ret && mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
2065 ret = NULL;
2067 _unlock_locales();
2068 return ret ? current_lc_all : NULL;
2071 #if _MSVCR_VER >= 80
2072 /*********************************************************************
2073 * _configthreadlocale (MSVCR80.@)
2075 int CDECL _configthreadlocale(int type)
2077 thread_data_t *data = msvcrt_get_thread_data();
2078 int ret;
2080 ret = (data->locale_flags & LOCALE_THREAD ? _ENABLE_PER_THREAD_LOCALE :
2081 _DISABLE_PER_THREAD_LOCALE);
2083 if(type == _ENABLE_PER_THREAD_LOCALE)
2084 data->locale_flags |= LOCALE_THREAD;
2085 else if(type == _DISABLE_PER_THREAD_LOCALE)
2086 data->locale_flags &= ~LOCALE_THREAD;
2087 else if(type)
2088 ret = -1;
2090 return ret;
2092 #endif
2094 BOOL msvcrt_init_locale(void)
2096 int i;
2098 _lock_locales();
2099 MSVCRT_locale = _create_locale(0, "C");
2100 _unlock_locales();
2101 if(!MSVCRT_locale)
2102 return FALSE;
2104 MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
2105 MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
2106 MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
2107 MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
2108 for(i=LC_MIN; i<=LC_MAX; i++)
2109 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
2110 _setmbcp(_MB_CP_ANSI);
2111 return TRUE;
2114 #if _MSVCR_VER >= 120
2115 /*********************************************************************
2116 * wctrans (MSVCR120.@)
2118 wctrans_t CDECL wctrans(const char *property)
2120 static const char str_tolower[] = "tolower";
2121 static const char str_toupper[] = "toupper";
2123 if(!strcmp(property, str_tolower))
2124 return 2;
2125 if(!strcmp(property, str_toupper))
2126 return 1;
2127 return 0;
2129 #endif