include/roapi.h: Add further typedefs.
[wine.git] / dlls / msvcrt / locale.c
blob6314840489f2ddbb68564e558a5097ae455bdec9
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 "config.h"
22 #include "wine/port.h"
24 #include <limits.h>
25 #include <locale.h>
26 #include <stdarg.h>
27 #include <stdio.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"
38 #include "wine/unicode.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
42 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
43 #define MAX_LOCALE_LENGTH 256
44 MSVCRT__locale_t MSVCRT_locale = NULL;
45 unsigned short *MSVCRT__pctype = NULL;
46 unsigned int MSVCRT___lc_codepage = 0;
47 int MSVCRT___lc_collate_cp = 0;
48 LCID MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1] = { 0 };
49 int MSVCRT___mb_cur_max = 1;
50 static unsigned char charmax = CHAR_MAX;
52 #define MSVCRT_LEADBYTE 0x8000
53 #define MSVCRT_C1_DEFINED 0x200
55 /* Friendly country strings & language names abbreviations. */
56 static const char * const _country_synonyms[] =
58 "american", "enu",
59 "american english", "enu",
60 "american-english", "enu",
61 "english-american", "enu",
62 "english-us", "enu",
63 "english-usa", "enu",
64 "us", "enu",
65 "usa", "enu",
66 "australian", "ena",
67 "english-aus", "ena",
68 "belgian", "nlb",
69 "french-belgian", "frb",
70 "canadian", "enc",
71 "english-can", "enc",
72 "french-canadian", "frc",
73 "chinese", "chs",
74 "chinese-simplified", "chs",
75 "chinese-traditional", "cht",
76 "dutch-belgian", "nlb",
77 "english-nz", "enz",
78 "uk", "eng",
79 "english-uk", "eng",
80 "french-swiss", "frs",
81 "swiss", "des",
82 "german-swiss", "des",
83 "italian-swiss", "its",
84 "german-austrian", "dea",
85 "portuguese", "ptb",
86 "portuguese-brazil", "ptb",
87 "spanish-mexican", "esm",
88 "norwegian-bokmal", "nor",
89 "norwegian-nynorsk", "non",
90 "spanish-modern", "esn"
93 /* INTERNAL: Map a synonym to an ISO code */
94 static void remap_synonym(char *name)
96 unsigned int i;
97 for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
99 if (!strcasecmp(_country_synonyms[i],name))
101 TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
102 strcpy(name, _country_synonyms[i+1]);
103 return;
108 /* Note: Flags are weighted in order of matching importance */
109 #define FOUND_LANGUAGE 0x4
110 #define FOUND_COUNTRY 0x2
111 #define FOUND_CODEPAGE 0x1
113 typedef struct {
114 char search_language[MAX_ELEM_LEN];
115 char search_country[MAX_ELEM_LEN];
116 char search_codepage[MAX_ELEM_LEN];
117 char found_codepage[MAX_ELEM_LEN];
118 unsigned int match_flags;
119 LANGID found_lang_id;
120 } locale_search_t;
122 #define CONTINUE_LOOKING TRUE
123 #define STOP_LOOKING FALSE
125 /* INTERNAL: Get and compare locale info with a given string */
126 static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp, BOOL exact)
128 int len;
130 if(!cmp[0])
131 return 0;
133 buff[0] = 0;
134 GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN);
135 if (!buff[0])
136 return 0;
138 /* Partial matches are only allowed on language/country names */
139 len = strlen(cmp);
140 if(exact || len<=3)
141 return !strcasecmp(cmp, buff);
142 else
143 return !strncasecmp(cmp, buff, len);
146 static BOOL CALLBACK
147 find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam)
149 locale_search_t *res = (locale_search_t *)lParam;
150 const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
151 char buff[MAX_ELEM_LEN];
152 unsigned int flags = 0;
154 if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
155 return CONTINUE_LOOKING;
157 /* Check Language */
158 if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) ||
159 compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) ||
160 compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE))
162 TRACE(":Found language: %s->%s\n", res->search_language, buff);
163 flags |= FOUND_LANGUAGE;
165 else if (res->match_flags & FOUND_LANGUAGE)
167 return CONTINUE_LOOKING;
170 /* Check Country */
171 if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) ||
172 compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) ||
173 compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE))
175 TRACE("Found country:%s->%s\n", res->search_country, buff);
176 flags |= FOUND_COUNTRY;
178 else if (!flags && (res->match_flags & FOUND_COUNTRY))
180 return CONTINUE_LOOKING;
183 /* Check codepage */
184 if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage, TRUE) ||
185 (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage, TRUE)))
187 TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
188 flags |= FOUND_CODEPAGE;
189 memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
191 else if (!flags && (res->match_flags & FOUND_CODEPAGE))
193 return CONTINUE_LOOKING;
196 if (flags > res->match_flags)
198 /* Found a better match than previously */
199 res->match_flags = flags;
200 res->found_lang_id = LangID;
202 if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE)) ==
203 (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE))
205 TRACE(":found exact locale match\n");
206 return STOP_LOOKING;
208 return CONTINUE_LOOKING;
211 extern int atoi(const char *);
213 /* Internal: Find the LCID for a locale specification */
214 LCID MSVCRT_locale_to_LCID(const char *locale, unsigned short *codepage)
216 thread_data_t *data = msvcrt_get_thread_data();
217 LCID lcid;
218 locale_search_t search;
219 const char *cp, *region;
221 if (!strcmp(locale, data->cached_locale)) {
222 if (codepage)
223 *codepage = data->cached_cp;
224 return data->cached_lcid;
227 memset(&search, 0, sizeof(locale_search_t));
229 cp = strchr(locale, '.');
230 region = strchr(locale, '_');
232 lstrcpynA(search.search_language, locale, MAX_ELEM_LEN);
233 if(region) {
234 lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN);
235 if(region-locale < MAX_ELEM_LEN)
236 search.search_language[region-locale] = '\0';
237 } else
238 search.search_country[0] = '\0';
240 if(cp) {
241 lstrcpynA(search.search_codepage, cp+1, MAX_ELEM_LEN);
242 if(region && cp-region-1<MAX_ELEM_LEN)
243 search.search_country[cp-region-1] = '\0';
244 if(cp-locale < MAX_ELEM_LEN)
245 search.search_language[cp-locale] = '\0';
246 } else
247 search.search_codepage[0] = '\0';
249 if(!search.search_country[0] && !search.search_codepage[0])
250 remap_synonym(search.search_language);
252 if(!strcasecmp(search.search_country, "China"))
253 strcpy(search.search_country, "People's Republic of China");
255 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
256 (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
257 (LONG_PTR)&search);
259 if (!search.match_flags)
260 return -1;
262 /* If we were given something that didn't match, fail */
263 if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
264 return -1;
266 lcid = MAKELCID(search.found_lang_id, SORT_DEFAULT);
268 /* Populate partial locale, translating LCID to locale string elements */
269 if (!(search.match_flags & FOUND_CODEPAGE)) {
270 /* Even if a codepage is not enumerated for a locale
271 * it can be set if valid */
272 if (search.search_codepage[0]) {
273 if (IsValidCodePage(atoi(search.search_codepage)))
274 memcpy(search.found_codepage,search.search_codepage,MAX_ELEM_LEN);
275 else {
276 /* Special codepage values: OEM & ANSI */
277 if (!strcasecmp(search.search_codepage,"OCP")) {
278 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
279 search.found_codepage, MAX_ELEM_LEN);
280 } else if (!strcasecmp(search.search_codepage,"ACP")) {
281 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
282 search.found_codepage, MAX_ELEM_LEN);
283 } else
284 return -1;
286 if (!atoi(search.found_codepage))
287 return -1;
289 } else {
290 /* Prefer ANSI codepages if present */
291 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
292 search.found_codepage, MAX_ELEM_LEN);
293 if (!search.found_codepage[0] || !atoi(search.found_codepage))
294 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
295 search.found_codepage, MAX_ELEM_LEN);
298 if (codepage)
299 *codepage = atoi(search.found_codepage);
301 if (strlen(locale) < sizeof(data->cached_locale)) {
302 strcpy(data->cached_locale, locale);
303 data->cached_lcid = lcid;
304 data->cached_cp = codepage ? *codepage : atoi(search.found_codepage);
307 return lcid;
310 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
311 static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp,
312 MSVCRT_pthreadlocinfo locinfo, int category)
314 char buf[256], *p;
315 int len;
317 if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) {
318 p = buf;
320 locinfo->lc_id[category].wLanguage = 0;
321 while(*p) {
322 locinfo->lc_id[category].wLanguage *= 16;
324 if(*p <= '9')
325 locinfo->lc_id[category].wLanguage += *p-'0';
326 else
327 locinfo->lc_id[category].wLanguage += *p-'a'+10;
329 p++;
332 locinfo->lc_id[category].wCountry =
333 locinfo->lc_id[category].wLanguage;
336 locinfo->lc_id[category].wCodePage = cp;
338 locinfo->lc_handle[category] = lcid;
340 len = 0;
341 len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE
342 |LOCALE_NOUSEROVERRIDE, buf, 256);
343 buf[len-1] = '_';
344 len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY
345 |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len);
346 buf[len-1] = '.';
347 sprintf(buf+len, "%d", cp);
348 len += strlen(buf+len)+1;
350 locinfo->lc_category[category].locale = MSVCRT_malloc(len);
351 locinfo->lc_category[category].refcount = MSVCRT_malloc(sizeof(int));
352 if(!locinfo->lc_category[category].locale
353 || !locinfo->lc_category[category].refcount) {
354 MSVCRT_free(locinfo->lc_category[category].locale);
355 MSVCRT_free(locinfo->lc_category[category].refcount);
356 locinfo->lc_category[category].locale = NULL;
357 locinfo->lc_category[category].refcount = NULL;
358 return TRUE;
360 memcpy(locinfo->lc_category[category].locale, buf, len);
361 *locinfo->lc_category[category].refcount = 1;
363 return FALSE;
366 /* INTERNAL: swap pointers values */
367 static inline void swap_pointers(void **p1, void **p2) {
368 void *hlp;
370 hlp = *p1;
371 *p1 = *p2;
372 *p2 = hlp;
375 /* INTERNAL: returns pthreadlocinfo struct */
376 MSVCRT_pthreadlocinfo get_locinfo(void) {
377 thread_data_t *data = msvcrt_get_thread_data();
379 if(!data || !data->have_locale)
380 return MSVCRT_locale->locinfo;
382 return data->locinfo;
385 /* INTERNAL: returns pthreadlocinfo struct */
386 MSVCRT_pthreadmbcinfo get_mbcinfo(void) {
387 thread_data_t *data = msvcrt_get_thread_data();
389 if(!data || !data->have_locale)
390 return MSVCRT_locale->mbcinfo;
392 return data->mbcinfo;
395 /* INTERNAL: constructs string returned by setlocale */
396 static inline char* construct_lc_all(MSVCRT_pthreadlocinfo locinfo) {
397 static char current_lc_all[MAX_LOCALE_LENGTH];
399 int i;
401 for(i=MSVCRT_LC_MIN+1; i<MSVCRT_LC_MAX; i++) {
402 if(strcmp(locinfo->lc_category[i].locale,
403 locinfo->lc_category[i+1].locale))
404 break;
407 if(i==MSVCRT_LC_MAX)
408 return locinfo->lc_category[MSVCRT_LC_COLLATE].locale;
410 sprintf(current_lc_all,
411 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
412 locinfo->lc_category[MSVCRT_LC_COLLATE].locale,
413 locinfo->lc_category[MSVCRT_LC_CTYPE].locale,
414 locinfo->lc_category[MSVCRT_LC_MONETARY].locale,
415 locinfo->lc_category[MSVCRT_LC_NUMERIC].locale,
416 locinfo->lc_category[MSVCRT_LC_TIME].locale);
418 return current_lc_all;
422 /*********************************************************************
423 * _Getdays (MSVCRT.@)
425 char* CDECL _Getdays(void)
427 MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr;
428 int i, len, size;
429 char *out;
431 TRACE("\n");
433 size = cur->str.names.short_mon[0]-cur->str.names.short_wday[0];
434 out = MSVCRT_malloc(size+1);
435 if(!out)
436 return NULL;
438 size = 0;
439 for(i=0; i<7; i++) {
440 out[size++] = ':';
441 len = strlen(cur->str.names.short_wday[i]);
442 memcpy(&out[size], cur->str.names.short_wday[i], len);
443 size += len;
445 out[size++] = ':';
446 len = strlen(cur->str.names.wday[i]);
447 memcpy(&out[size], cur->str.names.wday[i], len);
448 size += len;
450 out[size] = '\0';
452 return out;
455 #if _MSVCR_VER >= 110
456 /*********************************************************************
457 * _W_Getdays (MSVCR110.@)
459 MSVCRT_wchar_t* CDECL _W_Getdays(void)
461 MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr;
462 MSVCRT_wchar_t *out;
463 int i, len, size;
465 TRACE("\n");
467 size = cur->wstr.names.short_mon[0]-cur->wstr.names.short_wday[0];
468 out = MSVCRT_malloc((size+1)*sizeof(*out));
469 if(!out)
470 return NULL;
472 size = 0;
473 for(i=0; i<7; i++) {
474 out[size++] = ':';
475 len = strlenW(cur->wstr.names.short_wday[i]);
476 memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
477 size += len;
479 out[size++] = ':';
480 len = strlenW(cur->wstr.names.wday[i]);
481 memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
482 size += len;
484 out[size] = '\0';
486 return out;
488 #endif
490 /*********************************************************************
491 * _Getmonths (MSVCRT.@)
493 char* CDECL _Getmonths(void)
495 MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr;
496 int i, len, size;
497 char *out;
499 TRACE("\n");
501 size = cur->str.names.am-cur->str.names.short_mon[0];
502 out = MSVCRT_malloc(size+1);
503 if(!out)
504 return NULL;
506 size = 0;
507 for(i=0; i<12; i++) {
508 out[size++] = ':';
509 len = strlen(cur->str.names.short_mon[i]);
510 memcpy(&out[size], cur->str.names.short_mon[i], len);
511 size += len;
513 out[size++] = ':';
514 len = strlen(cur->str.names.mon[i]);
515 memcpy(&out[size], cur->str.names.mon[i], len);
516 size += len;
518 out[size] = '\0';
520 return out;
523 #if _MSVCR_VER >= 110
524 /*********************************************************************
525 * _W_Getmonths (MSVCR110.@)
527 MSVCRT_wchar_t* CDECL _W_Getmonths(void)
529 MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr;
530 MSVCRT_wchar_t *out;
531 int i, len, size;
533 TRACE("\n");
535 size = cur->wstr.names.am-cur->wstr.names.short_mon[0];
536 out = MSVCRT_malloc((size+1)*sizeof(*out));
537 if(!out)
538 return NULL;
540 size = 0;
541 for(i=0; i<12; i++) {
542 out[size++] = ':';
543 len = strlenW(cur->wstr.names.short_mon[i]);
544 memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
545 size += len;
547 out[size++] = ':';
548 len = strlenW(cur->wstr.names.mon[i]);
549 memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
550 size += len;
552 out[size] = '\0';
554 return out;
556 #endif
558 /*********************************************************************
559 * _Gettnames (MSVCRT.@)
561 void* CDECL _Gettnames(void)
563 MSVCRT___lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
564 unsigned int i, size = sizeof(MSVCRT___lc_time_data);
566 TRACE("\n");
568 for(i=0; i<sizeof(cur->str.str)/sizeof(cur->str.str[0]); i++)
569 size += strlen(cur->str.str[i])+1;
571 ret = MSVCRT_malloc(size);
572 if(!ret)
573 return NULL;
574 memcpy(ret, cur, size);
576 size = 0;
577 for(i=0; i<sizeof(cur->str.str)/sizeof(cur->str.str[0]); i++) {
578 ret->str.str[i] = &ret->data[size];
579 size += strlen(&ret->data[size])+1;
582 return ret;
585 #if _MSVCR_VER >= 110
586 /*********************************************************************
587 * _W_Gettnames (MSVCR110.@)
589 void* CDECL _W_Gettnames(void)
591 return _Gettnames();
593 #endif
595 /*********************************************************************
596 * __crtLCMapStringA (MSVCRT.@)
598 int CDECL __crtLCMapStringA(
599 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
600 int dstlen, unsigned int codepage, int xflag
602 FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
603 lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
604 /* FIXME: A bit incorrect. But msvcrt itself just converts its
605 * arguments to wide strings and then calls LCMapStringW
607 return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
610 /*********************************************************************
611 * __crtLCMapStringW (MSVCRT.@)
613 int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const MSVCRT_wchar_t *src,
614 int srclen, MSVCRT_wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
616 FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
617 lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
619 return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
622 /*********************************************************************
623 * __crtCompareStringA (MSVCRT.@)
625 int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
626 const char *src2, int len2 )
628 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
629 lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
630 /* FIXME: probably not entirely right */
631 return CompareStringA( lcid, flags, src1, len1, src2, len2 );
634 /*********************************************************************
635 * __crtCompareStringW (MSVCRT.@)
637 int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const MSVCRT_wchar_t *src1, int len1,
638 const MSVCRT_wchar_t *src2, int len2 )
640 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
641 lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
642 /* FIXME: probably not entirely right */
643 return CompareStringW( lcid, flags, src1, len1, src2, len2 );
646 /*********************************************************************
647 * __crtGetLocaleInfoW (MSVCRT.@)
649 int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, MSVCRT_wchar_t *buffer, int len )
651 FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid, type, buffer, len );
652 /* FIXME: probably not entirely right */
653 return GetLocaleInfoW( lcid, type, buffer, len );
656 #if _MSVCR_VER >= 110
657 /*********************************************************************
658 * __crtGetLocaleInfoEx (MSVC110.@)
660 int CDECL __crtGetLocaleInfoEx( const WCHAR *locale, LCTYPE type, MSVCRT_wchar_t *buffer, int len )
662 TRACE("(%s, %x, %p, %d)\n", debugstr_w(locale), type, buffer, len);
663 return GetLocaleInfoEx(locale, type, buffer, len);
665 #endif
667 /*********************************************************************
668 * btowc(MSVCRT.@)
670 MSVCRT_wint_t CDECL MSVCRT_btowc(int c)
672 unsigned char letter = c;
673 MSVCRT_wchar_t ret;
675 if(!MultiByteToWideChar(get_locinfo()->lc_handle[MSVCRT_LC_CTYPE],
676 0, (LPCSTR)&letter, 1, &ret, 1))
677 return 0;
679 return ret;
682 /*********************************************************************
683 * __crtGetStringTypeW(MSVCRT.@)
685 * This function was accepting different number of arguments in older
686 * versions of msvcrt.
688 BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
689 MSVCRT_wchar_t *buffer, int len, WORD *out)
691 FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
692 unk, type, buffer, len, out);
694 return GetStringTypeW(type, buffer, len, out);
697 /*********************************************************************
698 * localeconv (MSVCRT.@)
700 struct MSVCRT_lconv * CDECL MSVCRT_localeconv(void)
702 return get_locinfo()->lconv;
705 /*********************************************************************
706 * __lconv_init (MSVCRT.@)
708 int CDECL __lconv_init(void)
710 /* this is used to make chars unsigned */
711 charmax = 255;
712 return 0;
715 /*********************************************************************
716 * ___lc_handle_func (MSVCRT.@)
718 LCID* CDECL ___lc_handle_func(void)
720 return get_locinfo()->lc_handle;
723 #if _MSVCR_VER >= 110
724 /*********************************************************************
725 * ___lc_locale_name_func (MSVCR110.@)
727 MSVCRT_wchar_t** CDECL ___lc_locale_name_func(void)
729 return get_locinfo()->lc_name;
731 #endif
733 /*********************************************************************
734 * ___lc_codepage_func (MSVCRT.@)
736 unsigned int CDECL ___lc_codepage_func(void)
738 return get_locinfo()->lc_codepage;
741 /*********************************************************************
742 * ___lc_collate_cp_func (MSVCRT.@)
744 int CDECL ___lc_collate_cp_func(void)
746 return get_locinfo()->lc_collate_cp;
749 /* INTERNAL: frees MSVCRT_pthreadlocinfo struct */
750 void free_locinfo(MSVCRT_pthreadlocinfo locinfo)
752 int i;
754 if(!locinfo)
755 return;
757 if(InterlockedDecrement(&locinfo->refcount))
758 return;
760 for(i=MSVCRT_LC_MIN+1; i<=MSVCRT_LC_MAX; i++) {
761 MSVCRT_free(locinfo->lc_category[i].locale);
762 MSVCRT_free(locinfo->lc_category[i].refcount);
763 #if _MSVCR_VER >= 110
764 MSVCRT_free(locinfo->lc_name[i]);
765 #endif
768 if(locinfo->lconv) {
769 MSVCRT_free(locinfo->lconv->decimal_point);
770 MSVCRT_free(locinfo->lconv->thousands_sep);
771 MSVCRT_free(locinfo->lconv->grouping);
772 MSVCRT_free(locinfo->lconv->int_curr_symbol);
773 MSVCRT_free(locinfo->lconv->currency_symbol);
774 MSVCRT_free(locinfo->lconv->mon_decimal_point);
775 MSVCRT_free(locinfo->lconv->mon_thousands_sep);
776 MSVCRT_free(locinfo->lconv->mon_grouping);
777 MSVCRT_free(locinfo->lconv->positive_sign);
778 MSVCRT_free(locinfo->lconv->negative_sign);
779 #if _MSVCR_VER >= 100
780 MSVCRT_free(locinfo->lconv->_W_decimal_point);
781 MSVCRT_free(locinfo->lconv->_W_thousands_sep);
782 MSVCRT_free(locinfo->lconv->_W_int_curr_symbol);
783 MSVCRT_free(locinfo->lconv->_W_currency_symbol);
784 MSVCRT_free(locinfo->lconv->_W_mon_decimal_point);
785 MSVCRT_free(locinfo->lconv->_W_mon_thousands_sep);
786 MSVCRT_free(locinfo->lconv->_W_positive_sign);
787 MSVCRT_free(locinfo->lconv->_W_negative_sign);
788 #endif
790 MSVCRT_free(locinfo->lconv_intl_refcount);
791 MSVCRT_free(locinfo->lconv_num_refcount);
792 MSVCRT_free(locinfo->lconv_mon_refcount);
793 MSVCRT_free(locinfo->lconv);
795 MSVCRT_free(locinfo->ctype1_refcount);
796 MSVCRT_free(locinfo->ctype1);
798 MSVCRT_free(locinfo->pclmap);
799 MSVCRT_free(locinfo->pcumap);
801 MSVCRT_free(locinfo->lc_time_curr);
803 MSVCRT_free(locinfo);
806 /* INTERNAL: frees MSVCRT_pthreadmbcinfo struct */
807 void free_mbcinfo(MSVCRT_pthreadmbcinfo mbcinfo)
809 if(!mbcinfo)
810 return;
812 if(InterlockedDecrement(&mbcinfo->refcount))
813 return;
815 MSVCRT_free(mbcinfo);
818 /*********************************************************************
819 * _get_current_locale (MSVCRT.@)
821 MSVCRT__locale_t CDECL MSVCRT__get_current_locale(void)
823 MSVCRT__locale_t loc = MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct));
824 if(!loc)
825 return NULL;
827 loc->locinfo = get_locinfo();
828 loc->mbcinfo = get_mbcinfo();
829 InterlockedIncrement(&loc->locinfo->refcount);
830 InterlockedIncrement(&loc->mbcinfo->refcount);
831 return loc;
834 /*********************************************************************
835 * _free_locale (MSVCRT.@)
837 void CDECL MSVCRT__free_locale(MSVCRT__locale_t locale)
839 if (!locale)
840 return;
842 free_locinfo(locale->locinfo);
843 free_mbcinfo(locale->mbcinfo);
844 MSVCRT_free(locale);
847 #if _MSVCR_VER >= 110
848 static inline BOOL set_lc_locale_name(MSVCRT_pthreadlocinfo locinfo, int cat)
850 LCID lcid = locinfo->lc_handle[cat];
851 WCHAR buf[100];
852 int len;
854 len = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME
855 |LOCALE_NOUSEROVERRIDE, buf, 100);
856 if(!len) return FALSE;
858 if(LocaleNameToLCID(buf, 0) != lcid)
859 len = LCIDToLocaleName(lcid, buf, 100, 0);
861 if(!len || !(locinfo->lc_name[cat] = MSVCRT_malloc(len*sizeof(MSVCRT_wchar_t))))
862 return FALSE;
864 memcpy(locinfo->lc_name[cat], buf, len*sizeof(MSVCRT_wchar_t));
865 return TRUE;
867 #else
868 static inline BOOL set_lc_locale_name(MSVCRT_pthreadlocinfo locinfo, int cat)
870 return TRUE;
872 #endif
874 static inline BOOL category_needs_update(int cat, int user_cat,
875 MSVCRT_pthreadlocinfo locinfo, LCID lcid, unsigned short cp)
877 if(!locinfo) return TRUE;
878 if(user_cat!=cat && user_cat!=MSVCRT_LC_ALL) return FALSE;
879 return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage;
882 static MSVCRT_pthreadlocinfo create_locinfo(int category,
883 const char *locale, MSVCRT_pthreadlocinfo old_locinfo)
885 static const DWORD time_data[] = {
886 LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
887 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
888 LOCALE_SABBREVDAYNAME6,
889 LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
890 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
891 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
892 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
893 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
894 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
895 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
896 LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
897 LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
898 LOCALE_S1159, LOCALE_S2359,
899 LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
900 LOCALE_STIMEFORMAT
902 static const char collate[] = "COLLATE=";
903 static const char ctype[] = "CTYPE=";
904 static const char monetary[] = "MONETARY=";
905 static const char numeric[] = "NUMERIC=";
906 static const char time[] = "TIME=";
907 static const char cloc_short_date[] = "MM/dd/yy";
908 static const MSVCRT_wchar_t cloc_short_dateW[] = {'M','M','/','d','d','/','y','y',0};
909 static const char cloc_long_date[] = "dddd, MMMM dd, yyyy";
910 static const MSVCRT_wchar_t cloc_long_dateW[] = {'d','d','d','d',',',' ','M','M','M','M',' ','d','d',',',' ','y','y','y','y',0};
911 static const char cloc_time[] = "HH:mm:ss";
912 static const MSVCRT_wchar_t cloc_timeW[] = {'H','H',':','m','m',':','s','s',0};
914 MSVCRT_pthreadlocinfo locinfo;
915 LCID lcid[6] = { 0 }, lcid_tmp;
916 unsigned short cp[6] = { 0 };
917 char buf[256];
918 #if _MSVCR_VER >= 100
919 MSVCRT_wchar_t wbuf[256];
920 #endif
921 int i, ret, size;
923 TRACE("(%d %s)\n", category, locale);
925 if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
926 return NULL;
928 if(locale[0]=='C' && !locale[1]) {
929 lcid[0] = 0;
930 cp[0] = CP_ACP;
931 } else if(!locale[0]) {
932 lcid[0] = GetSystemDefaultLCID();
933 GetLocaleInfoA(lcid[0], LOCALE_IDEFAULTANSICODEPAGE
934 |LOCALE_NOUSEROVERRIDE, buf, sizeof(buf));
935 cp[0] = atoi(buf);
937 for(i=1; i<6; i++) {
938 lcid[i] = lcid[0];
939 cp[i] = cp[0];
941 } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
942 const char *p;
944 while(1) {
945 locale += 3; /* LC_ */
946 if(!memcmp(locale, collate, sizeof(collate)-1)) {
947 i = MSVCRT_LC_COLLATE;
948 locale += sizeof(collate)-1;
949 } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
950 i = MSVCRT_LC_CTYPE;
951 locale += sizeof(ctype)-1;
952 } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
953 i = MSVCRT_LC_MONETARY;
954 locale += sizeof(monetary)-1;
955 } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
956 i = MSVCRT_LC_NUMERIC;
957 locale += sizeof(numeric)-1;
958 } else if(!memcmp(locale, time, sizeof(time)-1)) {
959 i = MSVCRT_LC_TIME;
960 locale += sizeof(time)-1;
961 } else
962 return NULL;
964 p = strchr(locale, ';');
965 if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
966 lcid[i] = 0;
967 cp[i] = CP_ACP;
968 } else if(p) {
969 memcpy(buf, locale, p-locale);
970 buf[p-locale] = '\0';
971 lcid[i] = MSVCRT_locale_to_LCID(buf, &cp[i]);
972 } else
973 lcid[i] = MSVCRT_locale_to_LCID(locale, &cp[i]);
975 if(lcid[i] == -1)
976 return NULL;
978 if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
979 break;
981 locale = p+1;
983 } else {
984 lcid[0] = MSVCRT_locale_to_LCID(locale, &cp[0]);
985 if(lcid[0] == -1)
986 return NULL;
988 for(i=1; i<6; i++) {
989 lcid[i] = lcid[0];
990 cp[i] = cp[0];
994 locinfo = MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo));
995 if(!locinfo)
996 return NULL;
998 memset(locinfo, 0, sizeof(MSVCRT_threadlocinfo));
999 locinfo->refcount = 1;
1001 locinfo->lconv = MSVCRT_malloc(sizeof(struct MSVCRT_lconv));
1002 if(!locinfo->lconv) {
1003 free_locinfo(locinfo);
1004 return NULL;
1006 memset(locinfo->lconv, 0, sizeof(struct MSVCRT_lconv));
1008 locinfo->pclmap = MSVCRT_malloc(sizeof(char[256]));
1009 locinfo->pcumap = MSVCRT_malloc(sizeof(char[256]));
1010 if(!locinfo->pclmap || !locinfo->pcumap) {
1011 free_locinfo(locinfo);
1012 return NULL;
1015 if(!category_needs_update(MSVCRT_LC_COLLATE, category, old_locinfo,
1016 lcid[MSVCRT_LC_COLLATE], cp[MSVCRT_LC_COLLATE])) {
1017 locinfo->lc_handle[MSVCRT_LC_COLLATE] = old_locinfo->lc_handle[MSVCRT_LC_COLLATE];
1018 locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage = old_locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage;
1019 } else if(lcid[MSVCRT_LC_COLLATE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_COLLATE)) {
1020 if(update_threadlocinfo_category(lcid[MSVCRT_LC_COLLATE],
1021 cp[MSVCRT_LC_COLLATE], locinfo, MSVCRT_LC_COLLATE)) {
1022 free_locinfo(locinfo);
1023 return NULL;
1026 locinfo->lc_collate_cp = locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage;
1028 if(!set_lc_locale_name(locinfo, MSVCRT_LC_COLLATE)) {
1029 free_locinfo(locinfo);
1030 return NULL;
1032 } else
1033 locinfo->lc_category[MSVCRT_LC_COLLATE].locale = MSVCRT__strdup("C");
1035 if(!category_needs_update(MSVCRT_LC_CTYPE, category, old_locinfo,
1036 lcid[MSVCRT_LC_CTYPE], cp[MSVCRT_LC_CTYPE])) {
1037 locinfo->lc_handle[MSVCRT_LC_CTYPE] = old_locinfo->lc_handle[MSVCRT_LC_CTYPE];
1038 locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage = old_locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage;
1039 } else if(lcid[MSVCRT_LC_CTYPE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_CTYPE)) {
1040 CPINFO cp_info;
1041 int j;
1043 if(update_threadlocinfo_category(lcid[MSVCRT_LC_CTYPE],
1044 cp[MSVCRT_LC_CTYPE], locinfo, MSVCRT_LC_CTYPE)) {
1045 free_locinfo(locinfo);
1046 return NULL;
1049 locinfo->lc_codepage = locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage;
1050 locinfo->lc_clike = 1;
1051 if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) {
1052 free_locinfo(locinfo);
1053 return NULL;
1055 locinfo->mb_cur_max = cp_info.MaxCharSize;
1057 locinfo->ctype1_refcount = MSVCRT_malloc(sizeof(int));
1058 locinfo->ctype1 = MSVCRT_malloc(sizeof(short[257]));
1059 if(!locinfo->ctype1_refcount || !locinfo->ctype1) {
1060 free_locinfo(locinfo);
1061 return NULL;
1064 *locinfo->ctype1_refcount = 1;
1065 locinfo->ctype1[0] = 0;
1066 locinfo->pctype = locinfo->ctype1+1;
1068 buf[1] = buf[2] = '\0';
1069 for(i=1; i<257; i++) {
1070 buf[0] = i-1;
1072 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1073 locinfo->ctype1[i] = 0;
1075 GetStringTypeA(lcid[MSVCRT_LC_CTYPE], CT_CTYPE1, buf,
1076 1, locinfo->ctype1+i);
1079 for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
1080 for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
1081 locinfo->ctype1[j+1] |= MSVCRT__LEADBYTE;
1083 if(!set_lc_locale_name(locinfo, MSVCRT_LC_CTYPE)) {
1084 free_locinfo(locinfo);
1085 return NULL;
1088 for(i=0; i<256; i++) {
1089 if(locinfo->pctype[i] & MSVCRT__LEADBYTE)
1090 buf[i] = ' ';
1091 else
1092 buf[i] = i;
1095 LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
1096 (char*)locinfo->pclmap, 256);
1097 LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
1098 (char*)locinfo->pcumap, 256);
1099 } else {
1100 locinfo->lc_clike = 1;
1101 locinfo->mb_cur_max = 1;
1102 locinfo->pctype = MSVCRT__ctype+1;
1103 locinfo->lc_category[MSVCRT_LC_CTYPE].locale = MSVCRT__strdup("C");
1105 for(i=0; i<256; i++) {
1106 if(locinfo->pctype[i] & MSVCRT__LEADBYTE)
1107 buf[i] = ' ';
1108 else
1109 buf[i] = i;
1112 for(i=0; i<256; i++) {
1113 locinfo->pclmap[i] = (i>='A' && i<='Z' ? i-'A'+'a' : i);
1114 locinfo->pcumap[i] = (i>='a' && i<='z' ? i-'a'+'A' : i);
1118 if(!category_needs_update(MSVCRT_LC_MONETARY, category, old_locinfo,
1119 lcid[MSVCRT_LC_MONETARY], cp[MSVCRT_LC_MONETARY])) {
1120 locinfo->lc_handle[MSVCRT_LC_MONETARY] = old_locinfo->lc_handle[MSVCRT_LC_MONETARY];
1121 locinfo->lc_id[MSVCRT_LC_MONETARY].wCodePage = old_locinfo->lc_id[MSVCRT_LC_MONETARY].wCodePage;
1122 } else if(lcid[MSVCRT_LC_MONETARY] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_MONETARY)) {
1123 if(update_threadlocinfo_category(lcid[MSVCRT_LC_MONETARY],
1124 cp[MSVCRT_LC_MONETARY], locinfo, MSVCRT_LC_MONETARY)) {
1125 free_locinfo(locinfo);
1126 return NULL;
1129 locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
1130 locinfo->lconv_mon_refcount = MSVCRT_malloc(sizeof(int));
1131 if(!locinfo->lconv_intl_refcount || !locinfo->lconv_mon_refcount) {
1132 free_locinfo(locinfo);
1133 return NULL;
1136 *locinfo->lconv_intl_refcount = 1;
1137 *locinfo->lconv_mon_refcount = 1;
1139 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SINTLSYMBOL
1140 |LOCALE_NOUSEROVERRIDE, buf, 256);
1141 if(i && (locinfo->lconv->int_curr_symbol = MSVCRT_malloc(i)))
1142 memcpy(locinfo->lconv->int_curr_symbol, buf, i);
1143 else {
1144 free_locinfo(locinfo);
1145 return NULL;
1148 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SCURRENCY
1149 |LOCALE_NOUSEROVERRIDE, buf, 256);
1150 if(i && (locinfo->lconv->currency_symbol = MSVCRT_malloc(i)))
1151 memcpy(locinfo->lconv->currency_symbol, buf, i);
1152 else {
1153 free_locinfo(locinfo);
1154 return NULL;
1157 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONDECIMALSEP
1158 |LOCALE_NOUSEROVERRIDE, buf, 256);
1159 if(i && (locinfo->lconv->mon_decimal_point = MSVCRT_malloc(i)))
1160 memcpy(locinfo->lconv->mon_decimal_point, buf, i);
1161 else {
1162 free_locinfo(locinfo);
1163 return NULL;
1166 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1167 |LOCALE_NOUSEROVERRIDE, buf, 256);
1168 if(i && (locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(i)))
1169 memcpy(locinfo->lconv->mon_thousands_sep, buf, i);
1170 else {
1171 free_locinfo(locinfo);
1172 return NULL;
1175 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONGROUPING
1176 |LOCALE_NOUSEROVERRIDE, buf, 256);
1177 if(i>1)
1178 i = i/2 + (buf[i-2]=='0'?0:1);
1179 if(i && (locinfo->lconv->mon_grouping = MSVCRT_malloc(i))) {
1180 for(i=0; buf[i+1]==';'; i+=2)
1181 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1182 locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1183 if(buf[i] != '0')
1184 locinfo->lconv->mon_grouping[i/2+1] = 127;
1185 } else {
1186 free_locinfo(locinfo);
1187 return NULL;
1190 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SPOSITIVESIGN
1191 |LOCALE_NOUSEROVERRIDE, buf, 256);
1192 if(i && (locinfo->lconv->positive_sign = MSVCRT_malloc(i)))
1193 memcpy(locinfo->lconv->positive_sign, buf, i);
1194 else {
1195 free_locinfo(locinfo);
1196 return NULL;
1199 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SNEGATIVESIGN
1200 |LOCALE_NOUSEROVERRIDE, buf, 256);
1201 if(i && (locinfo->lconv->negative_sign = MSVCRT_malloc(i)))
1202 memcpy(locinfo->lconv->negative_sign, buf, i);
1203 else {
1204 free_locinfo(locinfo);
1205 return NULL;
1208 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IINTLCURRDIGITS
1209 |LOCALE_NOUSEROVERRIDE, buf, 256))
1210 locinfo->lconv->int_frac_digits = atoi(buf);
1211 else {
1212 free_locinfo(locinfo);
1213 return NULL;
1216 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_ICURRDIGITS
1217 |LOCALE_NOUSEROVERRIDE, buf, 256))
1218 locinfo->lconv->frac_digits = atoi(buf);
1219 else {
1220 free_locinfo(locinfo);
1221 return NULL;
1224 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSYMPRECEDES
1225 |LOCALE_NOUSEROVERRIDE, buf, 256))
1226 locinfo->lconv->p_cs_precedes = atoi(buf);
1227 else {
1228 free_locinfo(locinfo);
1229 return NULL;
1232 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSEPBYSPACE
1233 |LOCALE_NOUSEROVERRIDE, buf, 256))
1234 locinfo->lconv->p_sep_by_space = atoi(buf);
1235 else {
1236 free_locinfo(locinfo);
1237 return NULL;
1240 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSYMPRECEDES
1241 |LOCALE_NOUSEROVERRIDE, buf, 256))
1242 locinfo->lconv->n_cs_precedes = atoi(buf);
1243 else {
1244 free_locinfo(locinfo);
1245 return NULL;
1248 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSEPBYSPACE
1249 |LOCALE_NOUSEROVERRIDE, buf, 256))
1250 locinfo->lconv->n_sep_by_space = atoi(buf);
1251 else {
1252 free_locinfo(locinfo);
1253 return NULL;
1256 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSIGNPOSN
1257 |LOCALE_NOUSEROVERRIDE, buf, 256))
1258 locinfo->lconv->p_sign_posn = atoi(buf);
1259 else {
1260 free_locinfo(locinfo);
1261 return NULL;
1264 if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSIGNPOSN
1265 |LOCALE_NOUSEROVERRIDE, buf, 256))
1266 locinfo->lconv->n_sign_posn = atoi(buf);
1267 else {
1268 free_locinfo(locinfo);
1269 return NULL;
1272 #if _MSVCR_VER >= 100
1273 i = GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_SINTLSYMBOL
1274 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1275 if(i && (locinfo->lconv->_W_int_curr_symbol = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1276 memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(MSVCRT_wchar_t));
1277 else {
1278 free_locinfo(locinfo);
1279 return NULL;
1282 i = GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_SCURRENCY
1283 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1284 if(i && (locinfo->lconv->_W_currency_symbol = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1285 memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(MSVCRT_wchar_t));
1286 else {
1287 free_locinfo(locinfo);
1288 return NULL;
1291 i = GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONDECIMALSEP
1292 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1293 if(i && (locinfo->lconv->_W_mon_decimal_point = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1294 memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(MSVCRT_wchar_t));
1295 else {
1296 free_locinfo(locinfo);
1297 return NULL;
1300 i = GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1301 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1302 if(i && (locinfo->lconv->_W_mon_thousands_sep = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1303 memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(MSVCRT_wchar_t));
1304 else {
1305 free_locinfo(locinfo);
1306 return NULL;
1309 i = GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_SPOSITIVESIGN
1310 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1311 if(i && (locinfo->lconv->_W_positive_sign = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1312 memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(MSVCRT_wchar_t));
1313 else {
1314 free_locinfo(locinfo);
1315 return NULL;
1318 i = GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_SNEGATIVESIGN
1319 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1320 if(i && (locinfo->lconv->_W_negative_sign = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1321 memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(MSVCRT_wchar_t));
1322 else {
1323 free_locinfo(locinfo);
1324 return NULL;
1326 #endif
1328 if(!set_lc_locale_name(locinfo, MSVCRT_LC_MONETARY)) {
1329 free_locinfo(locinfo);
1330 return NULL;
1332 } else {
1333 locinfo->lconv->int_curr_symbol = MSVCRT_malloc(sizeof(char));
1334 locinfo->lconv->currency_symbol = MSVCRT_malloc(sizeof(char));
1335 locinfo->lconv->mon_decimal_point = MSVCRT_malloc(sizeof(char));
1336 locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(sizeof(char));
1337 locinfo->lconv->mon_grouping = MSVCRT_malloc(sizeof(char));
1338 locinfo->lconv->positive_sign = MSVCRT_malloc(sizeof(char));
1339 locinfo->lconv->negative_sign = MSVCRT_malloc(sizeof(char));
1341 if(!locinfo->lconv->int_curr_symbol || !locinfo->lconv->currency_symbol
1342 || !locinfo->lconv->mon_decimal_point || !locinfo->lconv->mon_thousands_sep
1343 || !locinfo->lconv->mon_grouping || !locinfo->lconv->positive_sign
1344 || !locinfo->lconv->negative_sign) {
1345 free_locinfo(locinfo);
1346 return NULL;
1349 locinfo->lconv->int_curr_symbol[0] = '\0';
1350 locinfo->lconv->currency_symbol[0] = '\0';
1351 locinfo->lconv->mon_decimal_point[0] = '\0';
1352 locinfo->lconv->mon_thousands_sep[0] = '\0';
1353 locinfo->lconv->mon_grouping[0] = '\0';
1354 locinfo->lconv->positive_sign[0] = '\0';
1355 locinfo->lconv->negative_sign[0] = '\0';
1356 locinfo->lconv->int_frac_digits = charmax;
1357 locinfo->lconv->frac_digits = charmax;
1358 locinfo->lconv->p_cs_precedes = charmax;
1359 locinfo->lconv->p_sep_by_space = charmax;
1360 locinfo->lconv->n_cs_precedes = charmax;
1361 locinfo->lconv->n_sep_by_space = charmax;
1362 locinfo->lconv->p_sign_posn = charmax;
1363 locinfo->lconv->n_sign_posn = charmax;
1365 #if _MSVCR_VER >= 100
1366 locinfo->lconv->_W_int_curr_symbol = MSVCRT_malloc(sizeof(MSVCRT_wchar_t));
1367 locinfo->lconv->_W_currency_symbol = MSVCRT_malloc(sizeof(MSVCRT_wchar_t));
1368 locinfo->lconv->_W_mon_decimal_point = MSVCRT_malloc(sizeof(MSVCRT_wchar_t));
1369 locinfo->lconv->_W_mon_thousands_sep = MSVCRT_malloc(sizeof(MSVCRT_wchar_t));
1370 locinfo->lconv->_W_positive_sign = MSVCRT_malloc(sizeof(MSVCRT_wchar_t));
1371 locinfo->lconv->_W_negative_sign = MSVCRT_malloc(sizeof(MSVCRT_wchar_t));
1373 if(!locinfo->lconv->_W_int_curr_symbol || !locinfo->lconv->_W_currency_symbol
1374 || !locinfo->lconv->_W_mon_decimal_point || !locinfo->lconv->_W_mon_thousands_sep
1375 || !locinfo->lconv->positive_sign || !locinfo->lconv->negative_sign) {
1376 free_locinfo(locinfo);
1377 return NULL;
1380 locinfo->lconv->_W_int_curr_symbol[0] = '\0';
1381 locinfo->lconv->_W_currency_symbol[0] = '\0';
1382 locinfo->lconv->_W_mon_decimal_point[0] = '\0';
1383 locinfo->lconv->_W_mon_thousands_sep[0] = '\0';
1384 locinfo->lconv->_W_positive_sign[0] = '\0';
1385 locinfo->lconv->_W_negative_sign[0] = '\0';
1386 #endif
1388 locinfo->lc_category[MSVCRT_LC_MONETARY].locale = MSVCRT__strdup("C");
1391 if(!category_needs_update(MSVCRT_LC_NUMERIC, category, old_locinfo,
1392 lcid[MSVCRT_LC_NUMERIC], cp[MSVCRT_LC_NUMERIC])) {
1393 locinfo->lc_handle[MSVCRT_LC_NUMERIC] = old_locinfo->lc_handle[MSVCRT_LC_NUMERIC];
1394 locinfo->lc_id[MSVCRT_LC_NUMERIC].wCodePage = old_locinfo->lc_id[MSVCRT_LC_NUMERIC].wCodePage;
1395 } else if(lcid[MSVCRT_LC_NUMERIC] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_NUMERIC)) {
1396 if(update_threadlocinfo_category(lcid[MSVCRT_LC_NUMERIC],
1397 cp[MSVCRT_LC_NUMERIC], locinfo, MSVCRT_LC_NUMERIC)) {
1398 free_locinfo(locinfo);
1399 return NULL;
1402 if(!locinfo->lconv_intl_refcount)
1403 locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
1404 locinfo->lconv_num_refcount = MSVCRT_malloc(sizeof(int));
1405 if(!locinfo->lconv_intl_refcount || !locinfo->lconv_num_refcount) {
1406 free_locinfo(locinfo);
1407 return NULL;
1410 *locinfo->lconv_intl_refcount = 1;
1411 *locinfo->lconv_num_refcount = 1;
1413 i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SDECIMAL
1414 |LOCALE_NOUSEROVERRIDE, buf, 256);
1415 if(i && (locinfo->lconv->decimal_point = MSVCRT_malloc(i)))
1416 memcpy(locinfo->lconv->decimal_point, buf, i);
1417 else {
1418 free_locinfo(locinfo);
1419 return NULL;
1422 i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_STHOUSAND
1423 |LOCALE_NOUSEROVERRIDE, buf, 256);
1424 if(i && (locinfo->lconv->thousands_sep = MSVCRT_malloc(i)))
1425 memcpy(locinfo->lconv->thousands_sep, buf, i);
1426 else {
1427 free_locinfo(locinfo);
1428 return NULL;
1431 i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SGROUPING
1432 |LOCALE_NOUSEROVERRIDE, buf, 256);
1433 if(i>1)
1434 i = i/2 + (buf[i-2]=='0'?0:1);
1435 if(i && (locinfo->lconv->grouping = MSVCRT_malloc(i))) {
1436 for(i=0; buf[i+1]==';'; i+=2)
1437 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1438 locinfo->lconv->grouping[i/2] = buf[i]-'0';
1439 if(buf[i] != '0')
1440 locinfo->lconv->grouping[i/2+1] = 127;
1441 } else {
1442 free_locinfo(locinfo);
1443 return NULL;
1446 #if _MSVCR_VER >= 100
1447 i = GetLocaleInfoW(lcid[MSVCRT_LC_NUMERIC], LOCALE_SDECIMAL
1448 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1449 if(i && (locinfo->lconv->_W_decimal_point = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1450 memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(MSVCRT_wchar_t));
1451 else {
1452 free_locinfo(locinfo);
1453 return NULL;
1456 i = GetLocaleInfoW(lcid[MSVCRT_LC_NUMERIC], LOCALE_STHOUSAND
1457 |LOCALE_NOUSEROVERRIDE, wbuf, 256);
1458 if(i && (locinfo->lconv->_W_thousands_sep = MSVCRT_malloc(i * sizeof(MSVCRT_wchar_t))))
1459 memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(MSVCRT_wchar_t));
1460 else {
1461 free_locinfo(locinfo);
1462 return NULL;
1464 #endif
1466 if(!set_lc_locale_name(locinfo, MSVCRT_LC_NUMERIC)) {
1467 free_locinfo(locinfo);
1468 return NULL;
1470 } else {
1471 locinfo->lconv->decimal_point = MSVCRT_malloc(sizeof(char[2]));
1472 locinfo->lconv->thousands_sep = MSVCRT_malloc(sizeof(char));
1473 locinfo->lconv->grouping = MSVCRT_malloc(sizeof(char));
1474 if(!locinfo->lconv->decimal_point || !locinfo->lconv->thousands_sep
1475 || !locinfo->lconv->grouping) {
1476 free_locinfo(locinfo);
1477 return NULL;
1480 locinfo->lconv->decimal_point[0] = '.';
1481 locinfo->lconv->decimal_point[1] = '\0';
1482 locinfo->lconv->thousands_sep[0] = '\0';
1483 locinfo->lconv->grouping[0] = '\0';
1485 #if _MSVCR_VER >= 100
1486 locinfo->lconv->_W_decimal_point = MSVCRT_malloc(sizeof(MSVCRT_wchar_t[2]));
1487 locinfo->lconv->_W_thousands_sep = MSVCRT_malloc(sizeof(MSVCRT_wchar_t));
1489 if(!locinfo->lconv->_W_decimal_point || !locinfo->lconv->_W_thousands_sep) {
1490 free_locinfo(locinfo);
1491 return NULL;
1494 locinfo->lconv->_W_decimal_point[0] = '.';
1495 locinfo->lconv->_W_decimal_point[1] = '\0';
1496 locinfo->lconv->_W_thousands_sep[0] = '\0';
1497 #endif
1499 locinfo->lc_category[MSVCRT_LC_NUMERIC].locale = MSVCRT__strdup("C");
1502 if(!category_needs_update(MSVCRT_LC_TIME, category, old_locinfo,
1503 lcid[MSVCRT_LC_TIME], cp[MSVCRT_LC_TIME])) {
1504 locinfo->lc_handle[MSVCRT_LC_TIME] = old_locinfo->lc_handle[MSVCRT_LC_TIME];
1505 locinfo->lc_id[MSVCRT_LC_TIME].wCodePage = old_locinfo->lc_id[MSVCRT_LC_TIME].wCodePage;
1506 } else {
1507 DWORD flags = lcid[MSVCRT_LC_TIME] ? 0 : LOCALE_NOUSEROVERRIDE;
1509 if(lcid[MSVCRT_LC_TIME] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_TIME)) {
1510 if(update_threadlocinfo_category(lcid[MSVCRT_LC_TIME],
1511 cp[MSVCRT_LC_TIME], locinfo, MSVCRT_LC_TIME)) {
1512 free_locinfo(locinfo);
1513 return NULL;
1516 if(!set_lc_locale_name(locinfo, MSVCRT_LC_TIME)) {
1517 free_locinfo(locinfo);
1518 return NULL;
1520 } else
1521 locinfo->lc_category[MSVCRT_LC_TIME].locale = MSVCRT__strdup("C");
1523 size = sizeof(MSVCRT___lc_time_data);
1524 lcid_tmp = lcid[MSVCRT_LC_TIME] ? lcid[MSVCRT_LC_TIME] : MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
1525 for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1526 if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1527 size += sizeof(cloc_short_date) + sizeof(cloc_short_dateW);
1528 }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
1529 size += sizeof(cloc_long_date) + sizeof(cloc_long_dateW);
1530 }else {
1531 ret = GetLocaleInfoA(lcid_tmp, time_data[i]|flags, NULL, 0);
1532 if(!ret) {
1533 free_locinfo(locinfo);
1534 return NULL;
1536 size += ret;
1538 ret = GetLocaleInfoW(lcid_tmp, time_data[i]|flags, NULL, 0);
1539 if(!ret) {
1540 free_locinfo(locinfo);
1541 return NULL;
1543 size += ret*sizeof(MSVCRT_wchar_t);
1546 #if _MSVCR_VER >= 110
1547 size += LCIDToLocaleName(lcid[MSVCRT_LC_TIME], NULL, 0, 0)*sizeof(MSVCRT_wchar_t);
1548 #endif
1550 locinfo->lc_time_curr = MSVCRT_malloc(size);
1551 if(!locinfo->lc_time_curr) {
1552 free_locinfo(locinfo);
1553 return NULL;
1556 ret = 0;
1557 for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1558 locinfo->lc_time_curr->str.str[i] = &locinfo->lc_time_curr->data[ret];
1559 if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1560 memcpy(&locinfo->lc_time_curr->data[ret], cloc_short_date, sizeof(cloc_short_date));
1561 ret += sizeof(cloc_short_date);
1562 }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
1563 memcpy(&locinfo->lc_time_curr->data[ret], cloc_long_date, sizeof(cloc_long_date));
1564 ret += sizeof(cloc_long_date);
1565 }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
1566 memcpy(&locinfo->lc_time_curr->data[ret], cloc_time, sizeof(cloc_time));
1567 ret += sizeof(cloc_time);
1568 }else {
1569 ret += GetLocaleInfoA(lcid_tmp, time_data[i]|flags,
1570 &locinfo->lc_time_curr->data[ret], size-ret);
1573 for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1574 locinfo->lc_time_curr->wstr.wstr[i] = (MSVCRT_wchar_t*)&locinfo->lc_time_curr->data[ret];
1575 if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1576 memcpy(&locinfo->lc_time_curr->data[ret], cloc_short_dateW, sizeof(cloc_short_dateW));
1577 ret += sizeof(cloc_short_dateW);
1578 }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
1579 memcpy(&locinfo->lc_time_curr->data[ret], cloc_long_dateW, sizeof(cloc_long_dateW));
1580 ret += sizeof(cloc_long_dateW);
1581 }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
1582 memcpy(&locinfo->lc_time_curr->data[ret], cloc_timeW, sizeof(cloc_timeW));
1583 ret += sizeof(cloc_timeW);
1584 }else {
1585 ret += GetLocaleInfoW(lcid_tmp, time_data[i]|flags,
1586 (MSVCRT_wchar_t*)&locinfo->lc_time_curr->data[ret], size-ret)*sizeof(MSVCRT_wchar_t);
1589 #if _MSVCR_VER >= 110
1590 locinfo->lc_time_curr->locname = (MSVCRT_wchar_t*)&locinfo->lc_time_curr->data[ret];
1591 LCIDToLocaleName(lcid[MSVCRT_LC_TIME], locinfo->lc_time_curr->locname,
1592 (size-ret)/sizeof(MSVCRT_wchar_t), 0);
1593 #else
1594 locinfo->lc_time_curr->lcid = lcid[MSVCRT_LC_TIME];
1595 #endif
1598 return locinfo;
1601 /*********************************************************************
1602 * _lock_locales (UCRTBASE.@)
1604 void CDECL _lock_locales(void)
1606 _mlock(_SETLOCALE_LOCK);
1609 /*********************************************************************
1610 * _unlock_locales (UCRTBASE.@)
1612 void CDECL _unlock_locales(void)
1614 _munlock(_SETLOCALE_LOCK);
1617 /*********************************************************************
1618 * _create_locale (MSVCRT.@)
1620 MSVCRT__locale_t CDECL MSVCRT__create_locale(int category, const char *locale)
1622 MSVCRT__locale_t loc;
1624 loc = MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct));
1625 if(!loc)
1626 return NULL;
1628 loc->locinfo = create_locinfo(category, locale, NULL);
1629 if(!loc->locinfo) {
1630 MSVCRT_free(loc);
1631 return NULL;
1634 loc->mbcinfo = MSVCRT_malloc(sizeof(MSVCRT_threadmbcinfo));
1635 if(!loc->mbcinfo) {
1636 free_locinfo(loc->locinfo);
1637 MSVCRT_free(loc);
1638 return NULL;
1641 loc->mbcinfo->refcount = 1;
1642 _setmbcp_l(loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage,
1643 loc->locinfo->lc_handle[MSVCRT_LC_CTYPE], loc->mbcinfo);
1644 return loc;
1647 #if _MSVCR_VER >= 110
1648 /*********************************************************************
1649 * _wcreate_locale (MSVCR110.@)
1651 MSVCRT__locale_t CDECL MSVCRT__wcreate_locale(int category, const MSVCRT_wchar_t *locale)
1653 MSVCRT__locale_t loc;
1654 MSVCRT_size_t len;
1655 char *str;
1657 if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
1658 return NULL;
1660 len = MSVCRT_wcstombs(NULL, locale, 0);
1661 if(len == -1)
1662 return NULL;
1663 if(!(str = MSVCRT_malloc(++len)))
1664 return NULL;
1665 MSVCRT_wcstombs(str, locale, len);
1667 loc = MSVCRT__create_locale(category, str);
1669 MSVCRT_free(str);
1670 return loc;
1672 #endif
1674 /*********************************************************************
1675 * setlocale (MSVCRT.@)
1677 char* CDECL MSVCRT_setlocale(int category, const char* locale)
1679 MSVCRT_pthreadlocinfo locinfo = get_locinfo();
1680 MSVCRT_pthreadlocinfo newlocinfo;
1682 if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX)
1683 return NULL;
1685 if(!locale) {
1686 if(category == MSVCRT_LC_ALL)
1687 return construct_lc_all(locinfo);
1689 return locinfo->lc_category[category].locale;
1692 newlocinfo = create_locinfo(category, locale, locinfo);
1693 if(!newlocinfo) {
1694 WARN("%d %s failed\n", category, locale);
1695 return NULL;
1698 _lock_locales();
1700 if(locinfo->lc_handle[MSVCRT_LC_COLLATE]!=newlocinfo->lc_handle[MSVCRT_LC_COLLATE]
1701 || locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage!=newlocinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage) {
1702 locinfo->lc_collate_cp = newlocinfo->lc_collate_cp;
1703 locinfo->lc_handle[MSVCRT_LC_COLLATE] =
1704 newlocinfo->lc_handle[MSVCRT_LC_COLLATE];
1705 locinfo->lc_id[MSVCRT_LC_COLLATE] =
1706 newlocinfo->lc_id[MSVCRT_LC_COLLATE];
1707 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_COLLATE].locale,
1708 (void**)&newlocinfo->lc_category[MSVCRT_LC_COLLATE].locale);
1709 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_COLLATE].refcount,
1710 (void**)&newlocinfo->lc_category[MSVCRT_LC_COLLATE].refcount);
1712 #if _MSVCR_VER >= 110
1713 swap_pointers((void**)&locinfo->lc_name[MSVCRT_LC_COLLATE],
1714 (void**)&newlocinfo->lc_name[MSVCRT_LC_COLLATE]);
1715 #endif
1718 if(locinfo->lc_handle[MSVCRT_LC_CTYPE]!=newlocinfo->lc_handle[MSVCRT_LC_CTYPE]
1719 || locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage!=newlocinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage) {
1720 locinfo->lc_handle[MSVCRT_LC_CTYPE] =
1721 newlocinfo->lc_handle[MSVCRT_LC_CTYPE];
1722 locinfo->lc_id[MSVCRT_LC_CTYPE] =
1723 newlocinfo->lc_id[MSVCRT_LC_CTYPE];
1724 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_CTYPE].locale,
1725 (void**)&newlocinfo->lc_category[MSVCRT_LC_CTYPE].locale);
1726 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_CTYPE].refcount,
1727 (void**)&newlocinfo->lc_category[MSVCRT_LC_CTYPE].refcount);
1729 locinfo->lc_codepage = newlocinfo->lc_codepage;
1730 locinfo->lc_clike = newlocinfo->lc_clike;
1731 locinfo->mb_cur_max = newlocinfo->mb_cur_max;
1733 swap_pointers((void**)&locinfo->ctype1_refcount,
1734 (void**)&newlocinfo->ctype1_refcount);
1735 swap_pointers((void**)&locinfo->ctype1, (void**)&newlocinfo->ctype1);
1736 swap_pointers((void**)&locinfo->pctype, (void**)&newlocinfo->pctype);
1737 swap_pointers((void**)&locinfo->pclmap, (void**)&newlocinfo->pclmap);
1738 swap_pointers((void**)&locinfo->pcumap, (void**)&newlocinfo->pcumap);
1740 #if _MSVCR_VER >= 110
1741 swap_pointers((void**)&locinfo->lc_name[MSVCRT_LC_CTYPE],
1742 (void**)&newlocinfo->lc_name[MSVCRT_LC_CTYPE]);
1743 #endif
1746 if(locinfo->lc_handle[MSVCRT_LC_MONETARY]!=newlocinfo->lc_handle[MSVCRT_LC_MONETARY]
1747 || locinfo->lc_id[MSVCRT_LC_MONETARY].wCodePage!=newlocinfo->lc_id[MSVCRT_LC_MONETARY].wCodePage) {
1748 locinfo->lc_handle[MSVCRT_LC_MONETARY] =
1749 newlocinfo->lc_handle[MSVCRT_LC_MONETARY];
1750 locinfo->lc_id[MSVCRT_LC_MONETARY] =
1751 newlocinfo->lc_id[MSVCRT_LC_MONETARY];
1752 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_MONETARY].locale,
1753 (void**)&newlocinfo->lc_category[MSVCRT_LC_MONETARY].locale);
1754 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_MONETARY].refcount,
1755 (void**)&newlocinfo->lc_category[MSVCRT_LC_MONETARY].refcount);
1757 swap_pointers((void**)&locinfo->lconv->int_curr_symbol,
1758 (void**)&newlocinfo->lconv->int_curr_symbol);
1759 swap_pointers((void**)&locinfo->lconv->currency_symbol,
1760 (void**)&newlocinfo->lconv->currency_symbol);
1761 swap_pointers((void**)&locinfo->lconv->mon_decimal_point,
1762 (void**)&newlocinfo->lconv->mon_decimal_point);
1763 swap_pointers((void**)&locinfo->lconv->mon_thousands_sep,
1764 (void**)&newlocinfo->lconv->mon_thousands_sep);
1765 swap_pointers((void**)&locinfo->lconv->mon_grouping,
1766 (void**)&newlocinfo->lconv->mon_grouping);
1767 swap_pointers((void**)&locinfo->lconv->positive_sign,
1768 (void**)&newlocinfo->lconv->positive_sign);
1769 swap_pointers((void**)&locinfo->lconv->negative_sign,
1770 (void**)&newlocinfo->lconv->negative_sign);
1772 #if _MSVCR_VER >= 100
1773 swap_pointers((void**)&locinfo->lconv->_W_int_curr_symbol,
1774 (void**)&newlocinfo->lconv->_W_int_curr_symbol);
1775 swap_pointers((void**)&locinfo->lconv->_W_currency_symbol,
1776 (void**)&newlocinfo->lconv->_W_currency_symbol);
1777 swap_pointers((void**)&locinfo->lconv->_W_mon_decimal_point,
1778 (void**)&newlocinfo->lconv->_W_mon_decimal_point);
1779 swap_pointers((void**)&locinfo->lconv->_W_mon_thousands_sep,
1780 (void**)&newlocinfo->lconv->_W_mon_thousands_sep);
1781 swap_pointers((void**)&locinfo->lconv->_W_positive_sign,
1782 (void**)&newlocinfo->lconv->_W_positive_sign);
1783 swap_pointers((void**)&locinfo->lconv->_W_negative_sign,
1784 (void**)&newlocinfo->lconv->_W_negative_sign);
1785 #endif
1787 locinfo->lconv->int_frac_digits = newlocinfo->lconv->int_frac_digits;
1788 locinfo->lconv->frac_digits = newlocinfo->lconv->frac_digits;
1789 locinfo->lconv->p_cs_precedes = newlocinfo->lconv->p_cs_precedes;
1790 locinfo->lconv->p_sep_by_space = newlocinfo->lconv->p_sep_by_space;
1791 locinfo->lconv->n_cs_precedes = newlocinfo->lconv->n_cs_precedes;
1792 locinfo->lconv->n_sep_by_space = newlocinfo->lconv->n_sep_by_space;
1793 locinfo->lconv->p_sign_posn = newlocinfo->lconv->p_sign_posn;
1794 locinfo->lconv->n_sign_posn = newlocinfo->lconv->n_sign_posn;
1796 #if _MSVCR_VER >= 110
1797 swap_pointers((void**)&locinfo->lc_name[MSVCRT_LC_MONETARY],
1798 (void**)&newlocinfo->lc_name[MSVCRT_LC_MONETARY]);
1799 #endif
1802 if(locinfo->lc_handle[MSVCRT_LC_NUMERIC]!=newlocinfo->lc_handle[MSVCRT_LC_NUMERIC]
1803 || locinfo->lc_id[MSVCRT_LC_NUMERIC].wCodePage!=newlocinfo->lc_id[MSVCRT_LC_NUMERIC].wCodePage) {
1804 locinfo->lc_handle[MSVCRT_LC_NUMERIC] =
1805 newlocinfo->lc_handle[MSVCRT_LC_NUMERIC];
1806 locinfo->lc_id[MSVCRT_LC_NUMERIC] =
1807 newlocinfo->lc_id[MSVCRT_LC_NUMERIC];
1808 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_NUMERIC].locale,
1809 (void**)&newlocinfo->lc_category[MSVCRT_LC_NUMERIC].locale);
1810 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_NUMERIC].refcount,
1811 (void**)&newlocinfo->lc_category[MSVCRT_LC_NUMERIC].refcount);
1813 swap_pointers((void**)&locinfo->lconv->decimal_point,
1814 (void**)&newlocinfo->lconv->decimal_point);
1815 swap_pointers((void**)&locinfo->lconv->thousands_sep,
1816 (void**)&newlocinfo->lconv->thousands_sep);
1817 swap_pointers((void**)&locinfo->lconv->grouping,
1818 (void**)&newlocinfo->lconv->grouping);
1820 #if _MSVCR_VER >= 100
1821 swap_pointers((void**)&locinfo->lconv->_W_decimal_point,
1822 (void**)&newlocinfo->lconv->_W_decimal_point);
1823 swap_pointers((void**)&locinfo->lconv->_W_thousands_sep,
1824 (void**)&newlocinfo->lconv->_W_thousands_sep);
1825 #endif
1827 #if _MSVCR_VER >= 110
1828 swap_pointers((void**)&locinfo->lc_name[MSVCRT_LC_NUMERIC],
1829 (void**)&newlocinfo->lc_name[MSVCRT_LC_NUMERIC]);
1830 #endif
1833 if(locinfo->lc_handle[MSVCRT_LC_TIME]!=newlocinfo->lc_handle[MSVCRT_LC_TIME]
1834 || locinfo->lc_id[MSVCRT_LC_TIME].wCodePage!=newlocinfo->lc_id[MSVCRT_LC_TIME].wCodePage) {
1835 locinfo->lc_handle[MSVCRT_LC_TIME] =
1836 newlocinfo->lc_handle[MSVCRT_LC_TIME];
1837 locinfo->lc_id[MSVCRT_LC_TIME] =
1838 newlocinfo->lc_id[MSVCRT_LC_TIME];
1839 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_TIME].locale,
1840 (void**)&newlocinfo->lc_category[MSVCRT_LC_TIME].locale);
1841 swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_TIME].refcount,
1842 (void**)&newlocinfo->lc_category[MSVCRT_LC_TIME].refcount);
1843 swap_pointers((void**)&locinfo->lc_time_curr,
1844 (void**)&newlocinfo->lc_time_curr);
1846 #if _MSVCR_VER >= 110
1847 swap_pointers((void**)&locinfo->lc_name[MSVCRT_LC_TIME],
1848 (void**)&newlocinfo->lc_name[MSVCRT_LC_TIME]);
1849 #endif
1852 free_locinfo(newlocinfo);
1853 _unlock_locales();
1855 if(locinfo == MSVCRT_locale->locinfo) {
1856 int i;
1858 MSVCRT___lc_codepage = locinfo->lc_codepage;
1859 MSVCRT___lc_collate_cp = locinfo->lc_collate_cp;
1860 MSVCRT___mb_cur_max = locinfo->mb_cur_max;
1861 MSVCRT__pctype = locinfo->pctype;
1862 for(i=MSVCRT_LC_MIN; i<=MSVCRT_LC_MAX; i++)
1863 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
1866 if(category == MSVCRT_LC_ALL)
1867 return construct_lc_all(locinfo);
1869 return locinfo->lc_category[category].locale;
1872 /*********************************************************************
1873 * _wsetlocale (MSVCRT.@)
1875 MSVCRT_wchar_t* CDECL MSVCRT__wsetlocale(int category, const MSVCRT_wchar_t* wlocale)
1877 static MSVCRT_wchar_t current_lc_all[MAX_LOCALE_LENGTH];
1879 char *locale = NULL;
1880 const char *ret;
1881 MSVCRT_size_t len;
1883 if(wlocale) {
1884 len = MSVCRT_wcstombs(NULL, wlocale, 0);
1885 if(len == -1)
1886 return NULL;
1888 locale = MSVCRT_malloc(++len);
1889 if(!locale)
1890 return NULL;
1892 MSVCRT_wcstombs(locale, wlocale, len);
1895 _lock_locales();
1896 ret = MSVCRT_setlocale(category, locale);
1897 MSVCRT_free(locale);
1899 if(ret && MSVCRT_mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
1900 ret = NULL;
1902 _unlock_locales();
1903 return ret ? current_lc_all : NULL;
1906 #if _MSVCR_VER >= 80
1907 /*********************************************************************
1908 * _configthreadlocale (MSVCR80.@)
1910 int CDECL _configthreadlocale(int type)
1912 thread_data_t *data = msvcrt_get_thread_data();
1913 MSVCRT__locale_t locale;
1914 int ret;
1916 if(!data)
1917 return -1;
1919 ret = (data->have_locale ? MSVCRT__ENABLE_PER_THREAD_LOCALE : MSVCRT__DISABLE_PER_THREAD_LOCALE);
1921 if(type == MSVCRT__ENABLE_PER_THREAD_LOCALE) {
1922 if(!data->have_locale) {
1923 /* Copy current global locale */
1924 locale = MSVCRT__create_locale(MSVCRT_LC_ALL, MSVCRT_setlocale(MSVCRT_LC_ALL, NULL));
1925 if(!locale)
1926 return -1;
1928 data->locinfo = locale->locinfo;
1929 data->mbcinfo = locale->mbcinfo;
1930 data->have_locale = TRUE;
1931 MSVCRT_free(locale);
1934 return ret;
1937 if(type == MSVCRT__DISABLE_PER_THREAD_LOCALE) {
1938 if(data->have_locale) {
1939 free_locinfo(data->locinfo);
1940 free_mbcinfo(data->mbcinfo);
1941 data->locinfo = MSVCRT_locale->locinfo;
1942 data->mbcinfo = MSVCRT_locale->mbcinfo;
1943 data->have_locale = FALSE;
1946 return ret;
1949 if(!type)
1950 return ret;
1952 return -1;
1954 #endif
1956 BOOL msvcrt_init_locale(void)
1958 int i;
1960 _lock_locales();
1961 MSVCRT_locale = MSVCRT__create_locale(0, "C");
1962 _unlock_locales();
1963 if(!MSVCRT_locale)
1964 return FALSE;
1966 MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
1967 MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
1968 MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
1969 MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
1970 for(i=MSVCRT_LC_MIN; i<=MSVCRT_LC_MAX; i++)
1971 MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
1972 _setmbcp(_MB_CP_ANSI);
1973 return TRUE;