kernel32: Remove superfluous heap reallocation calls in FormatMessageA/W.
[wine] / dlls / msvcrt / locale.c
1 /*
2  * msvcrt.dll locale functions
3  *
4  * Copyright 2000 Jon Griffiths
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <limits.h>
25 #include <locale.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "winnls.h"
33
34 #include "msvcrt.h"
35 #include "mtdll.h"
36
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
40
41 /* FIXME: Need to hold locale for each LC_* type and aggregate
42  * string to produce lc_all.
43  */
44 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
45 #define MAX_LOCALE_LENGTH 256
46 MSVCRT__locale_t MSVCRT_locale = NULL;
47 int MSVCRT___lc_codepage = 0;
48 int MSVCRT___lc_collate_cp = 0;
49 HANDLE MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1] = { 0 };
50 unsigned char charmax = CHAR_MAX;
51
52 /* MT */
53 #define LOCK_LOCALE   _mlock(_SETLOCALE_LOCK);
54 #define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK);
55
56 #define MSVCRT_LEADBYTE  0x8000
57 #define MSVCRT_C1_DEFINED 0x200
58
59 /* Friendly country strings & iso codes for synonym support.
60  * Based on MS documentation for setlocale().
61  */
62 static const char * const _country_synonyms[] =
63 {
64   "Hong Kong","HK",
65   "Hong-Kong","HK",
66   "New Zealand","NZ",
67   "New-Zealand","NZ",
68   "PR China","CN",
69   "PR-China","CN",
70   "United Kingdom","GB",
71   "United-Kingdom","GB",
72   "Britain","GB",
73   "England","GB",
74   "Great Britain","GB",
75   "United States","US",
76   "United-States","US",
77   "America","US"
78 };
79
80 /* INTERNAL: Map a synonym to an ISO code */
81 static void remap_synonym(char *name)
82 {
83   unsigned int i;
84   for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
85   {
86     if (!strcasecmp(_country_synonyms[i],name))
87     {
88       TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
89       name[0] = _country_synonyms[i+1][0];
90       name[1] = _country_synonyms[i+1][1];
91       name[2] = '\0';
92       return;
93     }
94   }
95 }
96
97 /* Note: Flags are weighted in order of matching importance */
98 #define FOUND_LANGUAGE         0x4
99 #define FOUND_COUNTRY          0x2
100 #define FOUND_CODEPAGE         0x1
101
102 typedef struct {
103   char search_language[MAX_ELEM_LEN];
104   char search_country[MAX_ELEM_LEN];
105   char search_codepage[MAX_ELEM_LEN];
106   char found_language[MAX_ELEM_LEN];
107   char found_country[MAX_ELEM_LEN];
108   char found_codepage[MAX_ELEM_LEN];
109   unsigned int match_flags;
110   LANGID found_lang_id;
111 } locale_search_t;
112
113 #define CONTINUE_LOOKING TRUE
114 #define STOP_LOOKING     FALSE
115
116 /* INTERNAL: Get and compare locale info with a given string */
117 static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp)
118 {
119   buff[0] = 0;
120   GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN);
121   if (!buff[0] || !cmp[0])
122     return 0;
123   /* Partial matches are allowed, e.g. "Germ" matches "Germany" */
124   return !strncasecmp(cmp, buff, strlen(cmp));
125 }
126
127 static BOOL CALLBACK
128 find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam)
129 {
130   locale_search_t *res = (locale_search_t *)lParam;
131   const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
132   char buff[MAX_ELEM_LEN];
133   unsigned int flags = 0;
134
135   if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
136     return CONTINUE_LOOKING;
137
138   /* Check Language */
139   if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language) ||
140       compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language) ||
141       compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language))
142   {
143     TRACE(":Found language: %s->%s\n", res->search_language, buff);
144     flags |= FOUND_LANGUAGE;
145     memcpy(res->found_language,res->search_language,MAX_ELEM_LEN);
146   }
147   else if (res->match_flags & FOUND_LANGUAGE)
148   {
149     return CONTINUE_LOOKING;
150   }
151
152   /* Check Country */
153   if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country) ||
154       compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country) ||
155       compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country))
156   {
157     TRACE("Found country:%s->%s\n", res->search_country, buff);
158     flags |= FOUND_COUNTRY;
159     memcpy(res->found_country,res->search_country,MAX_ELEM_LEN);
160   }
161   else if (res->match_flags & FOUND_COUNTRY)
162   {
163     return CONTINUE_LOOKING;
164   }
165
166   /* Check codepage */
167   if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) ||
168       (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage)))
169   {
170     TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
171     flags |= FOUND_CODEPAGE;
172     memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
173   }
174   else if (res->match_flags & FOUND_CODEPAGE)
175   {
176     return CONTINUE_LOOKING;
177   }
178
179   if (flags > res->match_flags)
180   {
181     /* Found a better match than previously */
182     res->match_flags = flags;
183     res->found_lang_id = LangID;
184   }
185   if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE)) ==
186         (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE))
187   {
188     TRACE(":found exact locale match\n");
189     return STOP_LOOKING;
190   }
191   return CONTINUE_LOOKING;
192 }
193
194 extern int atoi(const char *);
195
196 /* Internal: Find the LCID for a locale specification */
197 static LCID MSVCRT_locale_to_LCID(const char *locale)
198 {
199     LCID lcid;
200     locale_search_t search;
201     char *cp, *region;
202
203     memset(&search, 0, sizeof(locale_search_t));
204
205     cp = strchr(locale, '.');
206     region = strchr(locale, '_');
207
208     lstrcpynA(search.search_language, locale, MAX_ELEM_LEN);
209     if(region) {
210         lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN);
211         if(region-locale < MAX_ELEM_LEN)
212             search.search_language[region-locale] = '\0';
213     } else
214         search.search_country[0] = '\0';
215
216     if(cp) {
217         lstrcpynA(search.search_codepage, cp+1, MAX_ELEM_LEN);
218         if(cp-region-1 < MAX_ELEM_LEN)
219           search.search_country[cp-region-1] = '\0';
220         if(cp-locale < MAX_ELEM_LEN)
221             search.search_language[cp-locale] = '\0';
222     } else
223         search.search_codepage[0] = '\0';
224
225     /* FIXME:  MSVCRT_locale_to_LCID is not finding remaped values */
226     remap_synonym(search.search_country);
227
228     EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
229             (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
230             (LONG_PTR)&search);
231
232     if (!search.match_flags)
233         return -1;
234
235     /* If we were given something that didn't match, fail */
236     if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
237         return -1;
238
239     lcid =  MAKELCID(search.found_lang_id, SORT_DEFAULT);
240
241     /* Populate partial locale, translating LCID to locale string elements */
242     if (!search.found_codepage[0]) {
243         /* Even if a codepage is not enumerated for a locale
244          * it can be set if valid */
245         if (search.search_codepage[0]) {
246             if (IsValidCodePage(atoi(search.search_codepage)))
247                 memcpy(search.found_codepage,search.search_codepage,MAX_ELEM_LEN);
248             else {
249                 /* Special codepage values: OEM & ANSI */
250                 if (strcasecmp(search.search_codepage,"OCP")) {
251                     GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
252                             search.found_codepage, MAX_ELEM_LEN);
253                 } else if (strcasecmp(search.search_codepage,"ACP")) {
254                     GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
255                             search.found_codepage, MAX_ELEM_LEN);
256                 } else
257                     return -1;
258
259                 if (!atoi(search.found_codepage))
260                     return -1;
261             }
262         } else {
263             /* Prefer ANSI codepages if present */
264             GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
265                     search.found_codepage, MAX_ELEM_LEN);
266             if (!search.found_codepage[0] || !atoi(search.found_codepage))
267                 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
268                         search.found_codepage, MAX_ELEM_LEN);
269         }
270     }
271
272     GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE,
273             search.found_language, MAX_ELEM_LEN);
274     GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY|LOCALE_NOUSEROVERRIDE,
275             search.found_country, MAX_ELEM_LEN);
276     return lcid;
277 }
278
279 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
280 static BOOL update_threadlocinfo_category(LCID lcid, MSVCRT__locale_t loc, int category)
281 {
282     char buf[256], *p;
283     int len;
284
285     if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE, buf, 256)) {
286         p = buf;
287
288         loc->locinfo->lc_id[category].wLanguage = 0;
289         while(*p) {
290             loc->locinfo->lc_id[category].wLanguage *= 16;
291
292             if(*p <= '9')
293                 loc->locinfo->lc_id[category].wLanguage += *p-'0';
294             else
295                 loc->locinfo->lc_id[category].wLanguage += *p-'a'+10;
296
297             p++;
298         }
299
300         loc->locinfo->lc_id[category].wCountry =
301             loc->locinfo->lc_id[category].wLanguage;
302     }
303
304     if(GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, buf, 256))
305         loc->locinfo->lc_id[category].wCodePage = atoi(buf);
306
307     loc->locinfo->lc_handle[category] = lcid;
308
309     len = 0;
310     len += GetLocaleInfoA(lcid, LOCALE_SLANGUAGE, buf, 256);
311     buf[len-1] = '_';
312     len += GetLocaleInfoA(lcid, LOCALE_SCOUNTRY, &buf[len], 256-len);
313     buf[len-1] = '.';
314     len += GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, &buf[len], 256-len);
315
316     loc->locinfo->lc_category[category].locale = MSVCRT_malloc(sizeof(char[len]));
317     loc->locinfo->lc_category[category].refcount = MSVCRT_malloc(sizeof(int));
318     if(!loc->locinfo->lc_category[category].locale
319             || !loc->locinfo->lc_category[category].refcount) {
320         MSVCRT_free(loc->locinfo->lc_category[category].locale);
321         MSVCRT_free(loc->locinfo->lc_category[category].refcount);
322         loc->locinfo->lc_category[category].locale = NULL;
323         loc->locinfo->lc_category[category].refcount = NULL;
324         return TRUE;
325     }
326     memcpy(loc->locinfo->lc_category[category].locale, buf, sizeof(char[len]));
327     *loc->locinfo->lc_category[category].refcount = 1;
328
329     return FALSE;
330 }
331
332 /* INTERNAL: swap pointers values */
333 static inline void swap_pointers(void **p1, void **p2) {
334     void *hlp;
335
336     hlp = *p1;
337     *p1 = *p2;
338     *p2 = hlp;
339 }
340
341 /* INTERNAL: returns _locale_t struct for current locale */
342 MSVCRT__locale_t get_locale(void) {
343     thread_data_t *data = msvcrt_get_thread_data();
344
345     if(!data || !data->locale)
346         return MSVCRT_locale;
347
348     return data->locale;
349 }
350
351 /* INTERNAL: constructs string returned by setlocale */
352 static inline char* construct_lc_all(MSVCRT__locale_t cur) {
353     static char current_lc_all[MAX_LOCALE_LENGTH];
354
355     int i;
356
357     for(i=MSVCRT_LC_MIN+1; i<MSVCRT_LC_MAX; i++) {
358         if(strcmp(cur->locinfo->lc_category[i].locale,
359                     cur->locinfo->lc_category[i+1].locale))
360             break;
361     }
362
363     if(i==MSVCRT_LC_MAX)
364         return cur->locinfo->lc_category[MSVCRT_LC_COLLATE].locale;
365
366     sprintf(current_lc_all,
367             "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
368             cur->locinfo->lc_category[MSVCRT_LC_COLLATE].locale,
369             cur->locinfo->lc_category[MSVCRT_LC_CTYPE].locale,
370             cur->locinfo->lc_category[MSVCRT_LC_MONETARY].locale,
371             cur->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale,
372             cur->locinfo->lc_category[MSVCRT_LC_TIME].locale);
373
374     return current_lc_all;
375 }
376
377
378 /*********************************************************************
379  *              wsetlocale (MSVCRT.@)
380  */
381 MSVCRT_wchar_t* CDECL MSVCRT__wsetlocale(int category, const MSVCRT_wchar_t* locale)
382 {
383   static MSVCRT_wchar_t fake[] = {
384     'E','n','g','l','i','s','h','_','U','n','i','t','e','d',' ',
385     'S','t','a','t','e','s','.','1','2','5','2',0 };
386
387   FIXME("%d %s\n", category, debugstr_w(locale));
388
389   return fake;
390 }
391
392 /*********************************************************************
393  *              _Getdays (MSVCRT.@)
394  */
395 const char* CDECL _Getdays(void)
396 {
397   static const char MSVCRT_days[] = ":Sun:Sunday:Mon:Monday:Tue:Tuesday:Wed:"
398                             "Wednesday:Thu:Thursday:Fri:Friday:Sat:Saturday";
399   /* FIXME: Use locale */
400   TRACE("(void) semi-stub\n");
401   return MSVCRT_days;
402 }
403
404 /*********************************************************************
405  *              _Getmonths (MSVCRT.@)
406  */
407 const char* CDECL _Getmonths(void)
408 {
409   static const char MSVCRT_months[] = ":Jan:January:Feb:February:Mar:March:Apr:"
410                 "April:May:May:Jun:June:Jul:July:Aug:August:Sep:September:Oct:"
411                 "October:Nov:November:Dec:December";
412   /* FIXME: Use locale */
413   TRACE("(void) semi-stub\n");
414   return MSVCRT_months;
415 }
416
417 /*********************************************************************
418  *              _Gettnames (MSVCRT.@)
419  */
420 const char* CDECL _Gettnames(void)
421 {
422   /* FIXME: */
423   TRACE("(void) stub\n");
424   return "";
425 }
426
427 /*********************************************************************
428  *              _Strftime (MSVCRT.@)
429  */
430 const char* CDECL _Strftime(char *out, unsigned int len, const char *fmt,
431                             const void *tm, void *foo)
432 {
433   /* FIXME: */
434   TRACE("(%p %d %s %p %p) stub\n", out, len, fmt, tm, foo);
435   return "";
436 }
437
438 /*********************************************************************
439  *              __crtLCMapStringA (MSVCRT.@)
440  */
441 int CDECL __crtLCMapStringA(
442   LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
443   int dstlen, unsigned int codepage, int xflag
444 ) {
445   FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
446         lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
447   /* FIXME: A bit incorrect. But msvcrt itself just converts its
448    * arguments to wide strings and then calls LCMapStringW
449    */
450   return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
451 }
452
453 /*********************************************************************
454  *              __crtCompareStringA (MSVCRT.@)
455  */
456 int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
457                                const char *src2, int len2 )
458 {
459     FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
460           lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
461     /* FIXME: probably not entirely right */
462     return CompareStringA( lcid, flags, src1, len1, src2, len2 );
463 }
464
465 /*********************************************************************
466  *              __crtCompareStringW (MSVCRT.@)
467  */
468 int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const MSVCRT_wchar_t *src1, int len1,
469                                const MSVCRT_wchar_t *src2, int len2 )
470 {
471     FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
472           lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
473     /* FIXME: probably not entirely right */
474     return CompareStringW( lcid, flags, src1, len1, src2, len2 );
475 }
476
477 /*********************************************************************
478  *              __crtGetLocaleInfoW (MSVCRT.@)
479  */
480 int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, MSVCRT_wchar_t *buffer, int len )
481 {
482     FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid, type, buffer, len );
483     /* FIXME: probably not entirely right */
484     return GetLocaleInfoW( lcid, type, buffer, len );
485 }
486
487 /*********************************************************************
488  *              __crtGetStringTypeW(MSVCRT.@)
489  *
490  * This function was accepting different number of arguments in older
491  * versions of msvcrt.
492  */
493 BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
494         MSVCRT_wchar_t *buffer, int len, WORD *out)
495 {
496     FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
497             unk, type, buffer, len, out);
498
499     return GetStringTypeW(type, buffer, len, out);
500 }
501
502 /*********************************************************************
503  *              localeconv (MSVCRT.@)
504  */
505 struct MSVCRT_lconv * CDECL MSVCRT_localeconv(void)
506 {
507     return get_locale()->locinfo->lconv;
508 }
509
510 /*********************************************************************
511  *              __lconv_init (MSVCRT.@)
512  */
513 void CDECL __lconv_init(void)
514 {
515     /* this is used to make chars unsigned */
516     charmax = 255;
517 }
518
519 /*********************************************************************
520  *      ___lc_handle_func (MSVCRT.@)
521  */
522 HANDLE * CDECL ___lc_handle_func(void)
523 {
524     return MSVCRT___lc_handle;
525 }
526
527 /*********************************************************************
528  *      ___lc_codepage_func (MSVCRT.@)
529  */
530 int CDECL ___lc_codepage_func(void)
531 {
532     return MSVCRT___lc_codepage;
533 }
534
535 /*********************************************************************
536  *      ___lc_collate_cp_func (MSVCRT.@)
537  */
538 int CDECL ___lc_collate_cp_func(void)
539 {
540     return get_locale()->locinfo->lc_collate_cp;
541 }
542
543 /* _free_locale - not exported in native msvcrt */
544 void CDECL _free_locale(MSVCRT__locale_t locale)
545 {
546     int i;
547
548     for(i=MSVCRT_LC_MIN+1; i<=MSVCRT_LC_MAX; i++) {
549         MSVCRT_free(locale->locinfo->lc_category[i].locale);
550         MSVCRT_free(locale->locinfo->lc_category[i].refcount);
551     }
552
553     if(locale->locinfo->lconv) {
554         MSVCRT_free(locale->locinfo->lconv->decimal_point);
555         MSVCRT_free(locale->locinfo->lconv->thousands_sep);
556         MSVCRT_free(locale->locinfo->lconv->grouping);
557         MSVCRT_free(locale->locinfo->lconv->int_curr_symbol);
558         MSVCRT_free(locale->locinfo->lconv->currency_symbol);
559         MSVCRT_free(locale->locinfo->lconv->mon_decimal_point);
560         MSVCRT_free(locale->locinfo->lconv->mon_thousands_sep);
561         MSVCRT_free(locale->locinfo->lconv->mon_grouping);
562         MSVCRT_free(locale->locinfo->lconv->positive_sign);
563         MSVCRT_free(locale->locinfo->lconv->negative_sign);
564     }
565     MSVCRT_free(locale->locinfo->lconv_intl_refcount);
566     MSVCRT_free(locale->locinfo->lconv_num_refcount);
567     MSVCRT_free(locale->locinfo->lconv_mon_refcount);
568     MSVCRT_free(locale->locinfo->lconv);
569
570     MSVCRT_free(locale->locinfo->ctype1_refcount);
571     MSVCRT_free(locale->locinfo->ctype1);
572
573     MSVCRT_free(locale->locinfo->pclmap);
574     MSVCRT_free(locale->locinfo->pcumap);
575
576     MSVCRT_free(locale->locinfo);
577     MSVCRT_free(locale->mbcinfo);
578     MSVCRT_free(locale);
579 }
580
581 /* _create_locale - not exported in native msvcrt */
582 MSVCRT__locale_t _create_locale(int category, const char *locale)
583 {
584     static const char collate[] = "COLLATE=";
585     static const char ctype[] = "CTYPE=";
586     static const char monetary[] = "MONETARY=";
587     static const char numeric[] = "NUMERIC=";
588     static const char time[] = "TIME=";
589
590     MSVCRT__locale_t loc;
591     LCID lcid[6] = { 0 };
592     char buf[256];
593     int i;
594
595     TRACE("(%d %s)\n", category, locale);
596
597     if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
598         return NULL;
599
600     if(locale[0]=='C' && !locale[1])
601         lcid[0] = CP_ACP;
602     else if(!locale[0])
603         lcid[0] = GetSystemDefaultLCID();
604     else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
605         char *p;
606
607         while(1) {
608             locale += 3; /* LC_ */
609             if(!memcmp(locale, collate, sizeof(collate)-1)) {
610                 i = MSVCRT_LC_COLLATE;
611                 locale += sizeof(collate)-1;
612             } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
613                 i = MSVCRT_LC_CTYPE;
614                 locale += sizeof(ctype)-1;
615             } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
616                 i = MSVCRT_LC_MONETARY;
617                 locale += sizeof(monetary)-1;
618             } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
619                 i = MSVCRT_LC_NUMERIC;
620                 locale += sizeof(numeric)-1;
621             } else if(!memcmp(locale, time, sizeof(time)-1)) {
622                 i = MSVCRT_LC_TIME;
623                 locale += sizeof(time)-1;
624             } else
625                 return NULL;
626
627             p = strchr(locale, ';');
628             if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0'))
629                 lcid[i] = 0;
630             else if(p) {
631                 memcpy(buf, locale, p-locale);
632                 lcid[i] = MSVCRT_locale_to_LCID(buf);
633             } else
634                 lcid[i] = MSVCRT_locale_to_LCID(locale);
635
636             if(lcid[i] == -1)
637                 return NULL;
638
639             if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
640                 break;
641
642             locale = p+1;
643         }
644     } else {
645         lcid[0] = MSVCRT_locale_to_LCID(locale);
646         if(lcid[0] == -1)
647             return NULL;
648     }
649
650     for(i=1; i<6; i++) {
651         if(!lcid[i])
652             lcid[i] = lcid[0];
653     }
654
655     loc = MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct));
656     if(!loc)
657         return NULL;
658
659     loc->locinfo = MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo));
660     if(!loc->locinfo) {
661         MSVCRT_free(loc);
662         return NULL;
663     }
664
665     loc->mbcinfo = MSVCRT_malloc(sizeof(MSVCRT_threadmbcinfo));
666     if(!loc->mbcinfo) {
667         MSVCRT_free(loc->locinfo);
668         MSVCRT_free(loc);
669         return NULL;
670     }
671
672     memset(loc->locinfo, 0, sizeof(MSVCRT_threadlocinfo));
673     memset(loc->mbcinfo, 0, sizeof(MSVCRT_threadmbcinfo));
674
675     loc->locinfo->lconv = MSVCRT_malloc(sizeof(struct MSVCRT_lconv));
676     if(!loc->locinfo->lconv) {
677         _free_locale(loc);
678         return NULL;
679     }
680     memset(loc->locinfo->lconv, 0, sizeof(struct MSVCRT_lconv));
681
682     loc->locinfo->pclmap = MSVCRT_malloc(sizeof(char[256]));
683     loc->locinfo->pcumap = MSVCRT_malloc(sizeof(char[256]));
684     if(!loc->locinfo->pclmap || !loc->locinfo->pcumap) {
685         _free_locale(loc);
686         return NULL;
687     }
688
689     loc->locinfo->refcount = 1;
690
691     if(lcid[MSVCRT_LC_COLLATE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_COLLATE)) {
692         if(update_threadlocinfo_category(lcid[MSVCRT_LC_COLLATE], loc, MSVCRT_LC_COLLATE)) {
693             _free_locale(loc);
694             return NULL;
695         }
696     } else
697         loc->locinfo->lc_category[MSVCRT_LC_COLLATE].locale = strdup("C");
698
699     if(lcid[MSVCRT_LC_CTYPE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_CTYPE)) {
700         CPINFO cp;
701
702         if(update_threadlocinfo_category(lcid[MSVCRT_LC_CTYPE], loc, MSVCRT_LC_CTYPE)) {
703             _free_locale(loc);
704             return NULL;
705         }
706
707         loc->locinfo->lc_codepage = loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage;
708         loc->locinfo->lc_collate_cp = loc->locinfo->lc_codepage;
709         loc->locinfo->lc_clike = 1;
710         if(!GetCPInfo(loc->locinfo->lc_codepage, &cp)) {
711             _free_locale(loc);
712             return NULL;
713         }
714         loc->locinfo->mb_cur_max = cp.MaxCharSize;
715
716         loc->locinfo->ctype1_refcount = MSVCRT_malloc(sizeof(int));
717         loc->locinfo->ctype1 = MSVCRT_malloc(sizeof(short[257]));
718         if(!loc->locinfo->ctype1_refcount || !loc->locinfo->ctype1) {
719             _free_locale(loc);
720             return NULL;
721         }
722
723         *loc->locinfo->ctype1_refcount = 1;
724         loc->locinfo->ctype1[0] = 0;
725         loc->locinfo->pctype = loc->locinfo->ctype1+1;
726
727         buf[1] = buf[2] = '\0';
728         for(i=1; i<257; i++) {
729             buf[0] = i-1;
730
731             GetStringTypeA(lcid[MSVCRT_LC_CTYPE], CT_CTYPE1, buf,
732                     1, loc->locinfo->ctype1+i);
733             loc->locinfo->ctype1[i] |= 0x200;
734         }
735     } else {
736         loc->locinfo->lc_clike = 1;
737         loc->locinfo->mb_cur_max = 1;
738         loc->locinfo->pctype = MSVCRT__ctype+1;
739         loc->locinfo->lc_category[MSVCRT_LC_CTYPE].locale = strdup("C");
740     }
741
742     for(i=0; i<256; i++)
743         buf[i] = i;
744
745     LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
746             (char*)loc->locinfo->pclmap, 256);
747     LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
748             (char*)loc->locinfo->pcumap, 256);
749
750     loc->mbcinfo->refcount = 1;
751     loc->mbcinfo->mbcodepage = loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage;
752
753     for(i=0; i<256; i++) {
754         if(loc->locinfo->pclmap[i] != i) {
755             loc->mbcinfo->mbctype[i+1] |= 0x10;
756             loc->mbcinfo->mbcasemap[i] = loc->locinfo->pclmap[i];
757         } else if(loc->locinfo->pcumap[i] != i) {
758             loc->mbcinfo->mbctype[i+1] |= 0x20;
759             loc->mbcinfo->mbcasemap[i] = loc->locinfo->pcumap[i];
760         }
761     }
762
763     if(lcid[MSVCRT_LC_MONETARY] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_MONETARY)) {
764         if(update_threadlocinfo_category(lcid[MSVCRT_LC_MONETARY], loc, MSVCRT_LC_MONETARY)) {
765             _free_locale(loc);
766             return NULL;
767         }
768
769         loc->locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
770         loc->locinfo->lconv_mon_refcount = MSVCRT_malloc(sizeof(int));
771         if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_mon_refcount) {
772             _free_locale(loc);
773             return NULL;
774         }
775
776         *loc->locinfo->lconv_intl_refcount = 1;
777         *loc->locinfo->lconv_mon_refcount = 1;
778
779         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SINTLSYMBOL, buf, 256);
780         if(i && (loc->locinfo->lconv->int_curr_symbol = MSVCRT_malloc(sizeof(char[i]))))
781             memcpy(loc->locinfo->lconv->int_curr_symbol, buf, sizeof(char[i]));
782         else {
783             _free_locale(loc);
784             return NULL;
785         }
786
787         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SCURRENCY, buf, 256);
788         if(i && (loc->locinfo->lconv->currency_symbol = MSVCRT_malloc(sizeof(char[i]))))
789             memcpy(loc->locinfo->lconv->currency_symbol, buf, sizeof(char[i]));
790         else {
791             _free_locale(loc);
792             return NULL;
793         }
794
795         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONDECIMALSEP, buf, 256);
796         if(i && (loc->locinfo->lconv->mon_decimal_point = MSVCRT_malloc(sizeof(char[i]))))
797             memcpy(loc->locinfo->lconv->mon_decimal_point, buf, sizeof(char[i]));
798         else {
799             _free_locale(loc);
800             return NULL;
801         }
802
803         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONTHOUSANDSEP, buf, 256);
804         if(i && (loc->locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(sizeof(char[i]))))
805             memcpy(loc->locinfo->lconv->mon_thousands_sep, buf, sizeof(char[i]));
806         else {
807             _free_locale(loc);
808             return NULL;
809         }
810
811         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONGROUPING, buf, 256);
812         if(i>1)
813             i = i/2 + (buf[i-2]=='0'?0:1);
814         if(i && (loc->locinfo->lconv->mon_grouping = MSVCRT_malloc(sizeof(char[i])))) {
815             for(i=0; buf[i+1]==';'; i+=2)
816                 loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
817             loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
818             if(buf[i] != '0')
819                 loc->locinfo->lconv->mon_grouping[i/2+1] = 127;
820         } else {
821             _free_locale(loc);
822             return NULL;
823         }
824
825         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SPOSITIVESIGN, buf, 256);
826         if(i && (loc->locinfo->lconv->positive_sign = MSVCRT_malloc(sizeof(char[i]))))
827             memcpy(loc->locinfo->lconv->positive_sign, buf, sizeof(char[i]));
828         else {
829             _free_locale(loc);
830             return NULL;
831         }
832
833         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SNEGATIVESIGN, buf, 256);
834         if(i && (loc->locinfo->lconv->negative_sign = MSVCRT_malloc(sizeof(char[i]))))
835             memcpy(loc->locinfo->lconv->negative_sign, buf, sizeof(char[i]));
836         else {
837             _free_locale(loc);
838             return NULL;
839         }
840
841         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IINTLCURRDIGITS, buf, 256))
842             loc->locinfo->lconv->int_frac_digits = atoi(buf);
843         else {
844             _free_locale(loc);
845             return NULL;
846         }
847
848         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_ICURRDIGITS, buf, 256))
849             loc->locinfo->lconv->frac_digits = atoi(buf);
850         else {
851             _free_locale(loc);
852             return NULL;
853         }
854
855         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSYMPRECEDES, buf, 256))
856             loc->locinfo->lconv->p_cs_precedes = atoi(buf);
857         else {
858             _free_locale(loc);
859             return NULL;
860         }
861
862         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSEPBYSPACE, buf, 256))
863             loc->locinfo->lconv->p_sep_by_space = atoi(buf);
864         else {
865             _free_locale(loc);
866             return NULL;
867         }
868
869         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSYMPRECEDES, buf, 256))
870             loc->locinfo->lconv->n_cs_precedes = atoi(buf);
871         else {
872             _free_locale(loc);
873             return NULL;
874         }
875
876         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSEPBYSPACE, buf, 256))
877             loc->locinfo->lconv->n_sep_by_space = atoi(buf);
878         else {
879             _free_locale(loc);
880             return NULL;
881         }
882
883         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSIGNPOSN, buf, 256))
884             loc->locinfo->lconv->p_sign_posn = atoi(buf);
885         else {
886             _free_locale(loc);
887             return NULL;
888         }
889
890         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSIGNPOSN, buf, 256))
891             loc->locinfo->lconv->n_sign_posn = atoi(buf);
892         else {
893             _free_locale(loc);
894             return NULL;
895         }
896     } else {
897         loc->locinfo->lconv->int_curr_symbol = MSVCRT_malloc(sizeof(char));
898         loc->locinfo->lconv->currency_symbol = MSVCRT_malloc(sizeof(char));
899         loc->locinfo->lconv->mon_decimal_point = MSVCRT_malloc(sizeof(char));
900         loc->locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(sizeof(char));
901         loc->locinfo->lconv->mon_grouping = MSVCRT_malloc(sizeof(char));
902         loc->locinfo->lconv->positive_sign = MSVCRT_malloc(sizeof(char));
903         loc->locinfo->lconv->negative_sign = MSVCRT_malloc(sizeof(char));
904
905         if(!loc->locinfo->lconv->int_curr_symbol || !loc->locinfo->lconv->currency_symbol
906                 || !loc->locinfo->lconv->mon_decimal_point || !loc->locinfo->lconv->mon_thousands_sep
907                 || !loc->locinfo->lconv->mon_grouping || !loc->locinfo->lconv->positive_sign
908                 || !loc->locinfo->lconv->negative_sign) {
909             _free_locale(loc);
910             return NULL;
911         }
912
913         loc->locinfo->lconv->int_curr_symbol[0] = '\0';
914         loc->locinfo->lconv->currency_symbol[0] = '\0';
915         loc->locinfo->lconv->mon_decimal_point[0] = '\0';
916         loc->locinfo->lconv->mon_thousands_sep[0] = '\0';
917         loc->locinfo->lconv->mon_grouping[0] = '\0';
918         loc->locinfo->lconv->positive_sign[0] = '\0';
919         loc->locinfo->lconv->negative_sign[0] = '\0';
920         loc->locinfo->lconv->int_frac_digits = 127;
921         loc->locinfo->lconv->frac_digits = 127;
922         loc->locinfo->lconv->p_cs_precedes = 127;
923         loc->locinfo->lconv->p_sep_by_space = 127;
924         loc->locinfo->lconv->n_cs_precedes = 127;
925         loc->locinfo->lconv->n_sep_by_space = 127;
926         loc->locinfo->lconv->p_sign_posn = 127;
927         loc->locinfo->lconv->n_sign_posn = 127;
928
929         loc->locinfo->lc_category[MSVCRT_LC_MONETARY].locale = strdup("C");
930     }
931
932     if(lcid[MSVCRT_LC_NUMERIC] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_NUMERIC)) {
933         if(update_threadlocinfo_category(lcid[MSVCRT_LC_NUMERIC], loc, MSVCRT_LC_NUMERIC)) {
934             _free_locale(loc);
935             return NULL;
936         }
937
938         if(!loc->locinfo->lconv_intl_refcount)
939             loc->locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
940         loc->locinfo->lconv_num_refcount = MSVCRT_malloc(sizeof(int));
941         if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_num_refcount) {
942             _free_locale(loc);
943             return NULL;
944         }
945
946         *loc->locinfo->lconv_intl_refcount = 1;
947         *loc->locinfo->lconv_num_refcount = 1;
948
949         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SDECIMAL, buf, 256);
950         if(i && (loc->locinfo->lconv->decimal_point = MSVCRT_malloc(sizeof(char[i]))))
951             memcpy(loc->locinfo->lconv->decimal_point, buf, sizeof(char[i]));
952         else {
953             _free_locale(loc);
954             return NULL;
955         }
956
957         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_STHOUSAND, buf, 256);
958         if(i && (loc->locinfo->lconv->thousands_sep = MSVCRT_malloc(sizeof(char[i]))))
959             memcpy(loc->locinfo->lconv->thousands_sep, buf, sizeof(char[i]));
960         else {
961             _free_locale(loc);
962             return NULL;
963         }
964
965         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SGROUPING, buf, 256);
966         if(i>1)
967             i = i/2 + (buf[i-2]=='0'?0:1);
968         if(i && (loc->locinfo->lconv->grouping = MSVCRT_malloc(sizeof(char[i])))) {
969             for(i=0; buf[i+1]==';'; i+=2)
970                 loc->locinfo->lconv->grouping[i/2] = buf[i]-'0';
971             loc->locinfo->lconv->grouping[i/2] = buf[i]-'0';
972             if(buf[i] != '0')
973                 loc->locinfo->lconv->grouping[i/2+1] = 127;
974         } else {
975             _free_locale(loc);
976             return NULL;
977         }
978     } else {
979         loc->locinfo->lconv->decimal_point = MSVCRT_malloc(sizeof(char[2]));
980         loc->locinfo->lconv->thousands_sep = MSVCRT_malloc(sizeof(char));
981         loc->locinfo->lconv->grouping = MSVCRT_malloc(sizeof(char));
982         if(!loc->locinfo->lconv->decimal_point || !loc->locinfo->lconv->thousands_sep
983                 || !loc->locinfo->lconv->grouping) {
984             _free_locale(loc);
985             return NULL;
986         }
987
988         loc->locinfo->lconv->decimal_point[0] = '.';
989         loc->locinfo->lconv->decimal_point[1] = '\0';
990         loc->locinfo->lconv->thousands_sep[0] = '\0';
991         loc->locinfo->lconv->grouping[0] = '\0';
992
993         loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale = strdup("C");
994     }
995
996     if(lcid[MSVCRT_LC_TIME] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_TIME)) {
997         if(update_threadlocinfo_category(lcid[MSVCRT_LC_TIME], loc, MSVCRT_LC_TIME)) {
998             _free_locale(loc);
999             return NULL;
1000         }
1001     } else
1002         loc->locinfo->lc_category[MSVCRT_LC_TIME].locale = strdup("C");
1003
1004     return loc;
1005 }
1006
1007 /* _configthreadlocale - not exported in native msvcrt */
1008 int CDECL _configthreadlocale(int type)
1009 {
1010     thread_data_t *data = msvcrt_get_thread_data();
1011     int ret;
1012
1013     if(!data)
1014         return -1;
1015
1016     ret = (data->locale ? MSVCRT__ENABLE_PER_THREAD_LOCALE : MSVCRT__DISABLE_PER_THREAD_LOCALE);
1017
1018     if(type == MSVCRT__ENABLE_PER_THREAD_LOCALE) {
1019         if(!data->locale) {
1020             /* Copy current global locale */
1021             data->locale = _create_locale(MSVCRT_LC_ALL, MSVCRT_setlocale(MSVCRT_LC_ALL, NULL));
1022             if(!data->locale)
1023                 return -1;
1024         }
1025
1026         return ret;
1027     }
1028
1029     if(type == MSVCRT__DISABLE_PER_THREAD_LOCALE) {
1030         if(data->locale) {
1031             _free_locale(data->locale);
1032             data->locale = NULL;
1033         }
1034
1035         return ret;
1036     }
1037
1038     if(!type)
1039         return ret;
1040
1041     return -1;
1042 }
1043
1044 /*********************************************************************
1045  *             setlocale (MSVCRT.@)
1046  */
1047 char* CDECL MSVCRT_setlocale(int category, const char* locale)
1048 {
1049     MSVCRT__locale_t loc, cur;
1050
1051     cur = get_locale();
1052
1053     if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX)
1054         return NULL;
1055
1056     if(!locale) {
1057         if(category == MSVCRT_LC_ALL)
1058             return construct_lc_all(cur);
1059
1060         return cur->locinfo->lc_category[category].locale;
1061     }
1062
1063     loc = _create_locale(category, locale);
1064     if(!loc) {
1065         WARN("%d %s failed\n", category, locale);
1066         return NULL;
1067     }
1068
1069     LOCK_LOCALE;
1070
1071     switch(category) {
1072         case MSVCRT_LC_ALL:
1073             if(!cur)
1074                 break;
1075         case MSVCRT_LC_COLLATE:
1076             cur->locinfo->lc_handle[MSVCRT_LC_COLLATE] =
1077                 loc->locinfo->lc_handle[MSVCRT_LC_COLLATE];
1078             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_COLLATE].locale,
1079                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_COLLATE].locale);
1080             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_COLLATE].refcount,
1081                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_COLLATE].refcount);
1082
1083             if(category != MSVCRT_LC_ALL)
1084                 break;
1085         case MSVCRT_LC_CTYPE:
1086             cur->locinfo->lc_handle[MSVCRT_LC_CTYPE] =
1087                 loc->locinfo->lc_handle[MSVCRT_LC_CTYPE];
1088             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_CTYPE].locale,
1089                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_CTYPE].locale);
1090             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_CTYPE].refcount,
1091                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_CTYPE].refcount);
1092
1093             cur->locinfo->lc_codepage = loc->locinfo->lc_codepage;
1094             cur->locinfo->lc_collate_cp = loc->locinfo->lc_collate_cp;
1095             cur->locinfo->lc_clike = loc->locinfo->lc_clike;
1096             cur->locinfo->mb_cur_max = loc->locinfo->mb_cur_max;
1097
1098             swap_pointers((void**)&cur->locinfo->ctype1_refcount,
1099                     (void**)&loc->locinfo->ctype1_refcount);
1100             swap_pointers((void**)&cur->locinfo->ctype1, (void**)&loc->locinfo->ctype1);
1101             swap_pointers((void**)&cur->locinfo->pctype, (void**)&loc->locinfo->pctype);
1102             swap_pointers((void**)&cur->locinfo->pclmap, (void**)&loc->locinfo->pclmap);
1103             swap_pointers((void**)&cur->locinfo->pcumap, (void**)&loc->locinfo->pcumap);
1104
1105             memcpy(cur->mbcinfo, loc->mbcinfo, sizeof(MSVCRT_threadmbcinfo));
1106
1107             if(category != MSVCRT_LC_ALL)
1108                 break;
1109         case MSVCRT_LC_MONETARY:
1110             cur->locinfo->lc_handle[MSVCRT_LC_MONETARY] =
1111                 loc->locinfo->lc_handle[MSVCRT_LC_MONETARY];
1112             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_MONETARY].locale,
1113                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_MONETARY].locale);
1114             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_MONETARY].refcount,
1115                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_MONETARY].refcount);
1116
1117             swap_pointers((void**)&cur->locinfo->lconv->int_curr_symbol,
1118                     (void**)&loc->locinfo->lconv->int_curr_symbol);
1119             swap_pointers((void**)&cur->locinfo->lconv->currency_symbol,
1120                     (void**)&loc->locinfo->lconv->currency_symbol);
1121             swap_pointers((void**)&cur->locinfo->lconv->mon_decimal_point,
1122                     (void**)&loc->locinfo->lconv->mon_decimal_point);
1123             swap_pointers((void**)&cur->locinfo->lconv->mon_thousands_sep,
1124                     (void**)&loc->locinfo->lconv->mon_thousands_sep);
1125             swap_pointers((void**)&cur->locinfo->lconv->mon_grouping,
1126                     (void**)&loc->locinfo->lconv->mon_grouping);
1127             swap_pointers((void**)&cur->locinfo->lconv->positive_sign,
1128                     (void**)&loc->locinfo->lconv->positive_sign);
1129             swap_pointers((void**)&cur->locinfo->lconv->negative_sign,
1130                     (void**)&loc->locinfo->lconv->negative_sign);
1131             cur->locinfo->lconv->int_frac_digits = loc->locinfo->lconv->int_frac_digits;
1132             cur->locinfo->lconv->frac_digits = loc->locinfo->lconv->frac_digits;
1133             cur->locinfo->lconv->p_cs_precedes = loc->locinfo->lconv->p_cs_precedes;
1134             cur->locinfo->lconv->p_sep_by_space = loc->locinfo->lconv->p_sep_by_space;
1135             cur->locinfo->lconv->n_cs_precedes = loc->locinfo->lconv->n_cs_precedes;
1136             cur->locinfo->lconv->n_sep_by_space = loc->locinfo->lconv->n_sep_by_space;
1137             cur->locinfo->lconv->p_sign_posn = loc->locinfo->lconv->p_sign_posn;
1138             cur->locinfo->lconv->n_sign_posn = loc->locinfo->lconv->n_sign_posn;
1139
1140             if(category != MSVCRT_LC_ALL)
1141                 break;
1142         case MSVCRT_LC_NUMERIC:
1143             cur->locinfo->lc_handle[MSVCRT_LC_NUMERIC] =
1144                 loc->locinfo->lc_handle[MSVCRT_LC_NUMERIC];
1145             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale,
1146                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale);
1147             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_NUMERIC].refcount,
1148                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].refcount);
1149
1150             swap_pointers((void**)&cur->locinfo->lconv->decimal_point,
1151                     (void**)&loc->locinfo->lconv->decimal_point);
1152             swap_pointers((void**)&cur->locinfo->lconv->thousands_sep,
1153                     (void**)&loc->locinfo->lconv->thousands_sep);
1154             swap_pointers((void**)&cur->locinfo->lconv->grouping,
1155                     (void**)&loc->locinfo->lconv->grouping);
1156
1157             if(category != MSVCRT_LC_ALL)
1158                 break;
1159         case MSVCRT_LC_TIME:
1160             cur->locinfo->lc_handle[MSVCRT_LC_TIME] =
1161                 loc->locinfo->lc_handle[MSVCRT_LC_TIME];
1162             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_TIME].locale,
1163                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_TIME].locale);
1164             swap_pointers((void**)&cur->locinfo->lc_category[MSVCRT_LC_TIME].refcount,
1165                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_TIME].refcount);
1166
1167             if(category != MSVCRT_LC_ALL)
1168                 break;
1169     }
1170
1171     if(!cur)
1172         MSVCRT_locale = cur = loc;
1173     else
1174         _free_locale(loc);
1175
1176     UNLOCK_LOCALE;
1177
1178     if(cur == MSVCRT_locale) {
1179         MSVCRT___lc_codepage = cur->locinfo->lc_codepage;
1180         MSVCRT___lc_collate_cp = cur->locinfo->lc_collate_cp;
1181         MSVCRT___mb_cur_max = cur->locinfo->mb_cur_max;
1182         MSVCRT__pctype = cur->locinfo->pctype;
1183     }
1184
1185     if(category == MSVCRT_LC_ALL)
1186         return construct_lc_all(cur);
1187
1188     return cur->locinfo->lc_category[category].locale;
1189 }