Release 1.5.29.
[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 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
42 #define MAX_LOCALE_LENGTH 256
43 MSVCRT__locale_t MSVCRT_locale = NULL;
44 unsigned short *MSVCRT__pctype = NULL;
45 unsigned int MSVCRT___lc_codepage = 0;
46 int MSVCRT___lc_collate_cp = 0;
47 LCID MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1] = { 0 };
48 int MSVCRT___mb_cur_max = 1;
49 static unsigned char charmax = CHAR_MAX;
50
51 /* MT */
52 #define LOCK_LOCALE   _mlock(_SETLOCALE_LOCK);
53 #define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK);
54
55 #define MSVCRT_LEADBYTE  0x8000
56 #define MSVCRT_C1_DEFINED 0x200
57
58 /* Friendly country strings & language names abbreviations. */
59 static const char * const _country_synonyms[] =
60 {
61     "american", "enu",
62     "american english", "enu",
63     "american-english", "enu",
64     "english-american", "enu",
65     "english-us", "enu",
66     "english-usa", "enu",
67     "us", "enu",
68     "usa", "enu",
69     "australian", "ena",
70     "english-aus", "ena",
71     "belgian", "nlb",
72     "french-belgian", "frb",
73     "canadian", "enc",
74     "english-can", "enc",
75     "french-canadian", "frc",
76     "chinese", "chs",
77     "chinese-simplified", "chs",
78     "chinese-traditional", "cht",
79     "dutch-belgian", "nlb",
80     "english-nz", "enz",
81     "uk", "eng",
82     "english-uk", "eng",
83     "french-swiss", "frs",
84     "swiss", "des",
85     "german-swiss", "des",
86     "italian-swiss", "its",
87     "german-austrian", "dea",
88     "portuguese", "ptb",
89     "portuguese-brazil", "ptb",
90     "spanish-mexican", "esm",
91     "norwegian-bokmal", "nor",
92     "norwegian-nynorsk", "non",
93     "spanish-modern", "esn"
94 };
95
96 /* INTERNAL: Map a synonym to an ISO code */
97 static void remap_synonym(char *name)
98 {
99   unsigned int i;
100   for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
101   {
102     if (!strcasecmp(_country_synonyms[i],name))
103     {
104       TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
105       strcpy(name, _country_synonyms[i+1]);
106       return;
107     }
108   }
109 }
110
111 /* Note: Flags are weighted in order of matching importance */
112 #define FOUND_LANGUAGE         0x4
113 #define FOUND_COUNTRY          0x2
114 #define FOUND_CODEPAGE         0x1
115
116 typedef struct {
117   char search_language[MAX_ELEM_LEN];
118   char search_country[MAX_ELEM_LEN];
119   char search_codepage[MAX_ELEM_LEN];
120   char found_codepage[MAX_ELEM_LEN];
121   unsigned int match_flags;
122   LANGID found_lang_id;
123 } locale_search_t;
124
125 #define CONTINUE_LOOKING TRUE
126 #define STOP_LOOKING     FALSE
127
128 /* INTERNAL: Get and compare locale info with a given string */
129 static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp, BOOL exact)
130 {
131   int len;
132
133   if(!cmp[0])
134       return 0;
135
136   buff[0] = 0;
137   GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN);
138   if (!buff[0])
139     return 0;
140
141   /* Partial matches are only allowed on language/country names */
142   len = strlen(cmp);
143   if(exact || len<=3)
144     return !strcasecmp(cmp, buff);
145   else
146     return !strncasecmp(cmp, buff, len);
147 }
148
149 static BOOL CALLBACK
150 find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam)
151 {
152   locale_search_t *res = (locale_search_t *)lParam;
153   const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
154   char buff[MAX_ELEM_LEN];
155   unsigned int flags = 0;
156
157   if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
158     return CONTINUE_LOOKING;
159
160   /* Check Language */
161   if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) ||
162       compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) ||
163       compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE))
164   {
165     TRACE(":Found language: %s->%s\n", res->search_language, buff);
166     flags |= FOUND_LANGUAGE;
167   }
168   else if (res->match_flags & FOUND_LANGUAGE)
169   {
170     return CONTINUE_LOOKING;
171   }
172
173   /* Check Country */
174   if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) ||
175       compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) ||
176       compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE))
177   {
178     TRACE("Found country:%s->%s\n", res->search_country, buff);
179     flags |= FOUND_COUNTRY;
180   }
181   else if (!flags && (res->match_flags & FOUND_COUNTRY))
182   {
183     return CONTINUE_LOOKING;
184   }
185
186   /* Check codepage */
187   if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage, TRUE) ||
188       (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage, TRUE)))
189   {
190     TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
191     flags |= FOUND_CODEPAGE;
192     memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
193   }
194   else if (!flags && (res->match_flags & FOUND_CODEPAGE))
195   {
196     return CONTINUE_LOOKING;
197   }
198
199   if (flags > res->match_flags)
200   {
201     /* Found a better match than previously */
202     res->match_flags = flags;
203     res->found_lang_id = LangID;
204   }
205   if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE)) ==
206         (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE))
207   {
208     TRACE(":found exact locale match\n");
209     return STOP_LOOKING;
210   }
211   return CONTINUE_LOOKING;
212 }
213
214 extern int atoi(const char *);
215
216 /* Internal: Find the LCID for a locale specification */
217 LCID MSVCRT_locale_to_LCID(const char *locale, unsigned short *codepage)
218 {
219     LCID lcid;
220     locale_search_t search;
221     const char *cp, *region;
222
223     memset(&search, 0, sizeof(locale_search_t));
224
225     cp = strchr(locale, '.');
226     region = strchr(locale, '_');
227
228     lstrcpynA(search.search_language, locale, MAX_ELEM_LEN);
229     if(region) {
230         lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN);
231         if(region-locale < MAX_ELEM_LEN)
232             search.search_language[region-locale] = '\0';
233     } else
234         search.search_country[0] = '\0';
235
236     if(cp) {
237         lstrcpynA(search.search_codepage, cp+1, MAX_ELEM_LEN);
238         if(region && cp-region-1<MAX_ELEM_LEN)
239           search.search_country[cp-region-1] = '\0';
240         if(cp-locale < MAX_ELEM_LEN)
241             search.search_language[cp-locale] = '\0';
242     } else
243         search.search_codepage[0] = '\0';
244
245     if(!search.search_country[0] && !search.search_codepage[0])
246         remap_synonym(search.search_language);
247
248     EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
249             (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
250             (LONG_PTR)&search);
251
252     if (!search.match_flags)
253         return -1;
254
255     /* If we were given something that didn't match, fail */
256     if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
257         return -1;
258
259     lcid =  MAKELCID(search.found_lang_id, SORT_DEFAULT);
260
261     /* Populate partial locale, translating LCID to locale string elements */
262     if (!(search.match_flags & FOUND_CODEPAGE)) {
263         /* Even if a codepage is not enumerated for a locale
264          * it can be set if valid */
265         if (search.search_codepage[0]) {
266             if (IsValidCodePage(atoi(search.search_codepage)))
267                 memcpy(search.found_codepage,search.search_codepage,MAX_ELEM_LEN);
268             else {
269                 /* Special codepage values: OEM & ANSI */
270                 if (!strcasecmp(search.search_codepage,"OCP")) {
271                     GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
272                             search.found_codepage, MAX_ELEM_LEN);
273                 } else if (!strcasecmp(search.search_codepage,"ACP")) {
274                     GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
275                             search.found_codepage, MAX_ELEM_LEN);
276                 } else
277                     return -1;
278
279                 if (!atoi(search.found_codepage))
280                     return -1;
281             }
282         } else {
283             /* Prefer ANSI codepages if present */
284             GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
285                     search.found_codepage, MAX_ELEM_LEN);
286             if (!search.found_codepage[0] || !atoi(search.found_codepage))
287                 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
288                         search.found_codepage, MAX_ELEM_LEN);
289         }
290     }
291     if (codepage)
292         *codepage = atoi(search.found_codepage);
293
294     return lcid;
295 }
296
297 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
298 static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp,
299         MSVCRT__locale_t loc, int category)
300 {
301     char buf[256], *p;
302     int len;
303
304     if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) {
305         p = buf;
306
307         loc->locinfo->lc_id[category].wLanguage = 0;
308         while(*p) {
309             loc->locinfo->lc_id[category].wLanguage *= 16;
310
311             if(*p <= '9')
312                 loc->locinfo->lc_id[category].wLanguage += *p-'0';
313             else
314                 loc->locinfo->lc_id[category].wLanguage += *p-'a'+10;
315
316             p++;
317         }
318
319         loc->locinfo->lc_id[category].wCountry =
320             loc->locinfo->lc_id[category].wLanguage;
321     }
322
323     loc->locinfo->lc_id[category].wCodePage = cp;
324
325     loc->locinfo->lc_handle[category] = lcid;
326
327     len = 0;
328     len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE
329             |LOCALE_NOUSEROVERRIDE, buf, 256);
330     buf[len-1] = '_';
331     len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY
332             |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len);
333     buf[len-1] = '.';
334     sprintf(buf+len, "%d", cp);
335     len += strlen(buf+len)+1;
336
337     loc->locinfo->lc_category[category].locale = MSVCRT_malloc(len);
338     loc->locinfo->lc_category[category].refcount = MSVCRT_malloc(sizeof(int));
339     if(!loc->locinfo->lc_category[category].locale
340             || !loc->locinfo->lc_category[category].refcount) {
341         MSVCRT_free(loc->locinfo->lc_category[category].locale);
342         MSVCRT_free(loc->locinfo->lc_category[category].refcount);
343         loc->locinfo->lc_category[category].locale = NULL;
344         loc->locinfo->lc_category[category].refcount = NULL;
345         return TRUE;
346     }
347     memcpy(loc->locinfo->lc_category[category].locale, buf, len);
348     *loc->locinfo->lc_category[category].refcount = 1;
349
350     return FALSE;
351 }
352
353 /* INTERNAL: swap pointers values */
354 static inline void swap_pointers(void **p1, void **p2) {
355     void *hlp;
356
357     hlp = *p1;
358     *p1 = *p2;
359     *p2 = hlp;
360 }
361
362 /* INTERNAL: returns pthreadlocinfo struct */
363 MSVCRT_pthreadlocinfo get_locinfo(void) {
364     thread_data_t *data = msvcrt_get_thread_data();
365
366     if(!data || !data->have_locale)
367         return MSVCRT_locale->locinfo;
368
369     return data->locinfo;
370 }
371
372 /* INTERNAL: returns pthreadlocinfo struct */
373 MSVCRT_pthreadmbcinfo get_mbcinfo(void) {
374     thread_data_t *data = msvcrt_get_thread_data();
375
376     if(!data || !data->have_locale)
377         return MSVCRT_locale->mbcinfo;
378
379     return data->mbcinfo;
380 }
381
382 /* INTERNAL: constructs string returned by setlocale */
383 static inline char* construct_lc_all(MSVCRT_pthreadlocinfo locinfo) {
384     static char current_lc_all[MAX_LOCALE_LENGTH];
385
386     int i;
387
388     for(i=MSVCRT_LC_MIN+1; i<MSVCRT_LC_MAX; i++) {
389         if(strcmp(locinfo->lc_category[i].locale,
390                     locinfo->lc_category[i+1].locale))
391             break;
392     }
393
394     if(i==MSVCRT_LC_MAX)
395         return locinfo->lc_category[MSVCRT_LC_COLLATE].locale;
396
397     sprintf(current_lc_all,
398             "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
399             locinfo->lc_category[MSVCRT_LC_COLLATE].locale,
400             locinfo->lc_category[MSVCRT_LC_CTYPE].locale,
401             locinfo->lc_category[MSVCRT_LC_MONETARY].locale,
402             locinfo->lc_category[MSVCRT_LC_NUMERIC].locale,
403             locinfo->lc_category[MSVCRT_LC_TIME].locale);
404
405     return current_lc_all;
406 }
407
408
409 /*********************************************************************
410  *              wsetlocale (MSVCRT.@)
411  */
412 MSVCRT_wchar_t* CDECL MSVCRT__wsetlocale(int category, const MSVCRT_wchar_t* locale)
413 {
414   static MSVCRT_wchar_t fake[] = {
415     'E','n','g','l','i','s','h','_','U','n','i','t','e','d',' ',
416     'S','t','a','t','e','s','.','1','2','5','2',0 };
417
418   FIXME("%d %s\n", category, debugstr_w(locale));
419
420   return fake;
421 }
422
423 /*********************************************************************
424  *              _Getdays (MSVCRT.@)
425  */
426 char* CDECL _Getdays(void)
427 {
428     MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr;
429     int i, len, size;
430     char *out;
431
432     TRACE("\n");
433
434     size = cur->str.names.short_mon[0]-cur->str.names.short_wday[0];
435     out = MSVCRT_malloc(size+1);
436     if(!out)
437         return NULL;
438
439     size = 0;
440     for(i=0; i<7; i++) {
441         out[size++] = ':';
442         len = strlen(cur->str.names.short_wday[i]);
443         memcpy(&out[size], cur->str.names.short_wday[i], len);
444         size += len;
445
446         out[size++] = ':';
447         len = strlen(cur->str.names.wday[i]);
448         memcpy(&out[size], cur->str.names.wday[i], len);
449         size += len;
450     }
451     out[size] = '\0';
452
453     return out;
454 }
455
456 /*********************************************************************
457  *              _Getmonths (MSVCRT.@)
458  */
459 char* CDECL _Getmonths(void)
460 {
461     MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr;
462     int i, len, size;
463     char *out;
464
465     TRACE("\n");
466
467     size = cur->str.names.am-cur->str.names.short_mon[0];
468     out = MSVCRT_malloc(size+1);
469     if(!out)
470         return NULL;
471
472     size = 0;
473     for(i=0; i<12; i++) {
474         out[size++] = ':';
475         len = strlen(cur->str.names.short_mon[i]);
476         memcpy(&out[size], cur->str.names.short_mon[i], len);
477         size += len;
478
479         out[size++] = ':';
480         len = strlen(cur->str.names.mon[i]);
481         memcpy(&out[size], cur->str.names.mon[i], len);
482         size += len;
483     }
484     out[size] = '\0';
485
486     return out;
487 }
488
489 /*********************************************************************
490  *              _Gettnames (MSVCRT.@)
491  */
492 void* CDECL _Gettnames(void)
493 {
494     MSVCRT___lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
495     unsigned int i, size = sizeof(MSVCRT___lc_time_data);
496
497     TRACE("\n");
498
499     for(i=0; i<sizeof(cur->str.str)/sizeof(cur->str.str[0]); i++)
500         size += strlen(cur->str.str[i])+1;
501
502     ret = MSVCRT_malloc(size);
503     if(!ret)
504         return NULL;
505     memcpy(ret, cur, size);
506
507     size = 0;
508     for(i=0; i<sizeof(cur->str.str)/sizeof(cur->str.str[0]); i++) {
509         ret->str.str[i] = &ret->data[size];
510         size += strlen(&ret->data[size])+1;
511     }
512
513     return ret;
514 }
515
516 /*********************************************************************
517  *              __crtLCMapStringA (MSVCRT.@)
518  */
519 int CDECL __crtLCMapStringA(
520   LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
521   int dstlen, unsigned int codepage, int xflag
522 ) {
523   FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
524         lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
525   /* FIXME: A bit incorrect. But msvcrt itself just converts its
526    * arguments to wide strings and then calls LCMapStringW
527    */
528   return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
529 }
530
531 /*********************************************************************
532  *              __crtLCMapStringW (MSVCRT.@)
533  */
534 int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const MSVCRT_wchar_t *src,
535         int srclen, MSVCRT_wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
536 {
537     FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
538             lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
539
540     return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
541 }
542
543 /*********************************************************************
544  *              __crtCompareStringA (MSVCRT.@)
545  */
546 int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
547                                const char *src2, int len2 )
548 {
549     FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
550           lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 );
551     /* FIXME: probably not entirely right */
552     return CompareStringA( lcid, flags, src1, len1, src2, len2 );
553 }
554
555 /*********************************************************************
556  *              __crtCompareStringW (MSVCRT.@)
557  */
558 int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const MSVCRT_wchar_t *src1, int len1,
559                                const MSVCRT_wchar_t *src2, int len2 )
560 {
561     FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
562           lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 );
563     /* FIXME: probably not entirely right */
564     return CompareStringW( lcid, flags, src1, len1, src2, len2 );
565 }
566
567 /*********************************************************************
568  *              __crtGetLocaleInfoW (MSVCRT.@)
569  */
570 int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, MSVCRT_wchar_t *buffer, int len )
571 {
572     FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid, type, buffer, len );
573     /* FIXME: probably not entirely right */
574     return GetLocaleInfoW( lcid, type, buffer, len );
575 }
576
577 /*********************************************************************
578  *              btowc(MSVCRT.@)
579  */
580 MSVCRT_wint_t CDECL MSVCRT_btowc(int c)
581 {
582     unsigned char letter = c;
583     MSVCRT_wchar_t ret;
584
585     if(!MultiByteToWideChar(get_locinfo()->lc_handle[MSVCRT_LC_CTYPE],
586                 0, (LPCSTR)&letter, 1, &ret, 1))
587         return 0;
588
589     return ret;
590 }
591
592 /*********************************************************************
593  *              __crtGetStringTypeW(MSVCRT.@)
594  *
595  * This function was accepting different number of arguments in older
596  * versions of msvcrt.
597  */
598 BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
599         MSVCRT_wchar_t *buffer, int len, WORD *out)
600 {
601     FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
602             unk, type, buffer, len, out);
603
604     return GetStringTypeW(type, buffer, len, out);
605 }
606
607 /*********************************************************************
608  *              localeconv (MSVCRT.@)
609  */
610 struct MSVCRT_lconv * CDECL MSVCRT_localeconv(void)
611 {
612     return get_locinfo()->lconv;
613 }
614
615 /*********************************************************************
616  *              __lconv_init (MSVCRT.@)
617  */
618 int CDECL __lconv_init(void)
619 {
620     /* this is used to make chars unsigned */
621     charmax = 255;
622     return 0;
623 }
624
625 /*********************************************************************
626  *      ___lc_handle_func (MSVCRT.@)
627  */
628 LCID* CDECL ___lc_handle_func(void)
629 {
630     return MSVCRT___lc_handle;
631 }
632
633 /*********************************************************************
634  *      ___lc_codepage_func (MSVCRT.@)
635  */
636 unsigned int CDECL ___lc_codepage_func(void)
637 {
638     return MSVCRT___lc_codepage;
639 }
640
641 /*********************************************************************
642  *      ___lc_collate_cp_func (MSVCRT.@)
643  */
644 int CDECL ___lc_collate_cp_func(void)
645 {
646     return get_locinfo()->lc_collate_cp;
647 }
648
649 /* INTERNAL: frees MSVCRT_pthreadlocinfo struct */
650 void free_locinfo(MSVCRT_pthreadlocinfo locinfo)
651 {
652     int i;
653
654     if(!locinfo)
655         return;
656
657     if(InterlockedDecrement(&locinfo->refcount))
658         return;
659
660     for(i=MSVCRT_LC_MIN+1; i<=MSVCRT_LC_MAX; i++) {
661         MSVCRT_free(locinfo->lc_category[i].locale);
662         MSVCRT_free(locinfo->lc_category[i].refcount);
663     }
664
665     if(locinfo->lconv) {
666         MSVCRT_free(locinfo->lconv->decimal_point);
667         MSVCRT_free(locinfo->lconv->thousands_sep);
668         MSVCRT_free(locinfo->lconv->grouping);
669         MSVCRT_free(locinfo->lconv->int_curr_symbol);
670         MSVCRT_free(locinfo->lconv->currency_symbol);
671         MSVCRT_free(locinfo->lconv->mon_decimal_point);
672         MSVCRT_free(locinfo->lconv->mon_thousands_sep);
673         MSVCRT_free(locinfo->lconv->mon_grouping);
674         MSVCRT_free(locinfo->lconv->positive_sign);
675         MSVCRT_free(locinfo->lconv->negative_sign);
676     }
677     MSVCRT_free(locinfo->lconv_intl_refcount);
678     MSVCRT_free(locinfo->lconv_num_refcount);
679     MSVCRT_free(locinfo->lconv_mon_refcount);
680     MSVCRT_free(locinfo->lconv);
681
682     MSVCRT_free(locinfo->ctype1_refcount);
683     MSVCRT_free(locinfo->ctype1);
684
685     MSVCRT_free(locinfo->pclmap);
686     MSVCRT_free(locinfo->pcumap);
687
688     MSVCRT_free(locinfo->lc_time_curr);
689
690     MSVCRT_free(locinfo);
691 }
692
693 /* INTERNAL: frees MSVCRT_pthreadmbcinfo struct */
694 void free_mbcinfo(MSVCRT_pthreadmbcinfo mbcinfo)
695 {
696     if(!mbcinfo)
697         return;
698
699     if(InterlockedDecrement(&mbcinfo->refcount))
700         return;
701
702     MSVCRT_free(mbcinfo);
703 }
704
705 /*********************************************************************
706  *      _get_current_locale (MSVCRT.@)
707  */
708 MSVCRT__locale_t CDECL MSVCRT__get_current_locale(void)
709 {
710     MSVCRT__locale_t loc = MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct));
711     if(!loc)
712         return NULL;
713
714     loc->locinfo = get_locinfo();
715     loc->mbcinfo = get_mbcinfo();
716     InterlockedIncrement(&loc->locinfo->refcount);
717     InterlockedIncrement(&loc->mbcinfo->refcount);
718     return loc;
719 }
720
721 /*********************************************************************
722  *      _free_locale (MSVCRT.@)
723  */
724 void CDECL MSVCRT__free_locale(MSVCRT__locale_t locale)
725 {
726     if (!locale)
727         return;
728
729     free_locinfo(locale->locinfo);
730     free_mbcinfo(locale->mbcinfo);
731     MSVCRT_free(locale);
732 }
733
734 /*********************************************************************
735  *      _create_locale (MSVCRT.@)
736  */
737 MSVCRT__locale_t CDECL MSVCRT__create_locale(int category, const char *locale)
738 {
739     static const DWORD time_data[] = {
740         LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
741         LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
742         LOCALE_SABBREVDAYNAME6,
743         LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
744         LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
745         LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
746         LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
747         LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
748         LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
749         LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
750         LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
751         LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
752         LOCALE_S1159, LOCALE_S2359,
753         LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
754         LOCALE_STIMEFORMAT
755     };
756     static const char collate[] = "COLLATE=";
757     static const char ctype[] = "CTYPE=";
758     static const char monetary[] = "MONETARY=";
759     static const char numeric[] = "NUMERIC=";
760     static const char time[] = "TIME=";
761     static const char cloc_short_date[] = "MM/dd/yy";
762     static const MSVCRT_wchar_t cloc_short_dateW[] = {'M','M','/','d','d','/','y','y',0};
763     static const char cloc_long_date[] = "dddd, MMMM dd, yyyy";
764     static const MSVCRT_wchar_t cloc_long_dateW[] = {'d','d','d','d',',',' ','M','M','M','M',' ','d','d',',',' ','y','y','y','y',0};
765     static const char cloc_time[] = "HH:mm:ss";
766     static const MSVCRT_wchar_t cloc_timeW[] = {'H','H',':','m','m',':','s','s',0};
767
768     MSVCRT__locale_t loc;
769     LCID lcid[6] = { 0 }, lcid_tmp;
770     unsigned short cp[6] = { 0 };
771     char buf[256];
772     int i, ret, size;
773
774     TRACE("(%d %s)\n", category, locale);
775
776     if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
777         return NULL;
778
779     if(locale[0]=='C' && !locale[1]) {
780         lcid[0] = 0;
781         cp[0] = CP_ACP;
782     } else if(!locale[0]) {
783         lcid[0] = GetSystemDefaultLCID();
784         GetLocaleInfoA(lcid[0], LOCALE_IDEFAULTANSICODEPAGE
785                 |LOCALE_NOUSEROVERRIDE, buf, sizeof(buf));
786         cp[0] = atoi(buf);
787
788         for(i=1; i<6; i++) {
789             lcid[i] = lcid[0];
790             cp[i] = cp[0];
791         }
792     } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
793         const char *p;
794
795         while(1) {
796             locale += 3; /* LC_ */
797             if(!memcmp(locale, collate, sizeof(collate)-1)) {
798                 i = MSVCRT_LC_COLLATE;
799                 locale += sizeof(collate)-1;
800             } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
801                 i = MSVCRT_LC_CTYPE;
802                 locale += sizeof(ctype)-1;
803             } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
804                 i = MSVCRT_LC_MONETARY;
805                 locale += sizeof(monetary)-1;
806             } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
807                 i = MSVCRT_LC_NUMERIC;
808                 locale += sizeof(numeric)-1;
809             } else if(!memcmp(locale, time, sizeof(time)-1)) {
810                 i = MSVCRT_LC_TIME;
811                 locale += sizeof(time)-1;
812             } else
813                 return NULL;
814
815             p = strchr(locale, ';');
816             if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
817                 lcid[i] = 0;
818                 cp[i] = CP_ACP;
819             } else if(p) {
820                 memcpy(buf, locale, p-locale);
821                 buf[p-locale] = '\0';
822                 lcid[i] = MSVCRT_locale_to_LCID(buf, &cp[i]);
823             } else
824                 lcid[i] = MSVCRT_locale_to_LCID(locale, &cp[i]);
825
826             if(lcid[i] == -1)
827                 return NULL;
828
829             if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
830                 break;
831
832             locale = p+1;
833         }
834     } else {
835         lcid[0] = MSVCRT_locale_to_LCID(locale, &cp[0]);
836         if(lcid[0] == -1)
837             return NULL;
838
839         for(i=1; i<6; i++) {
840             lcid[i] = lcid[0];
841             cp[i] = cp[0];
842         }
843     }
844
845     loc = MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct));
846     if(!loc)
847         return NULL;
848
849     loc->locinfo = MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo));
850     if(!loc->locinfo) {
851         MSVCRT_free(loc);
852         return NULL;
853     }
854
855     loc->mbcinfo = MSVCRT_malloc(sizeof(MSVCRT_threadmbcinfo));
856     if(!loc->mbcinfo) {
857         MSVCRT_free(loc->locinfo);
858         MSVCRT_free(loc);
859         return NULL;
860     }
861
862     memset(loc->locinfo, 0, sizeof(MSVCRT_threadlocinfo));
863     loc->locinfo->refcount = 1;
864     loc->mbcinfo->refcount = 1;
865
866     loc->locinfo->lconv = MSVCRT_malloc(sizeof(struct MSVCRT_lconv));
867     if(!loc->locinfo->lconv) {
868         MSVCRT__free_locale(loc);
869         return NULL;
870     }
871     memset(loc->locinfo->lconv, 0, sizeof(struct MSVCRT_lconv));
872
873     loc->locinfo->pclmap = MSVCRT_malloc(sizeof(char[256]));
874     loc->locinfo->pcumap = MSVCRT_malloc(sizeof(char[256]));
875     if(!loc->locinfo->pclmap || !loc->locinfo->pcumap) {
876         MSVCRT__free_locale(loc);
877         return NULL;
878     }
879
880     if(lcid[MSVCRT_LC_COLLATE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_COLLATE)) {
881         if(update_threadlocinfo_category(lcid[MSVCRT_LC_COLLATE], cp[MSVCRT_LC_COLLATE], loc, MSVCRT_LC_COLLATE)) {
882             MSVCRT__free_locale(loc);
883             return NULL;
884         }
885
886         loc->locinfo->lc_collate_cp = loc->locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage;
887     } else
888         loc->locinfo->lc_category[MSVCRT_LC_COLLATE].locale = MSVCRT__strdup("C");
889
890     if(lcid[MSVCRT_LC_CTYPE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_CTYPE)) {
891         CPINFO cp_info;
892         int j;
893
894         if(update_threadlocinfo_category(lcid[MSVCRT_LC_CTYPE], cp[MSVCRT_LC_CTYPE], loc, MSVCRT_LC_CTYPE)) {
895             MSVCRT__free_locale(loc);
896             return NULL;
897         }
898
899         loc->locinfo->lc_codepage = loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage;
900         loc->locinfo->lc_clike = 1;
901         if(!GetCPInfo(loc->locinfo->lc_codepage, &cp_info)) {
902             MSVCRT__free_locale(loc);
903             return NULL;
904         }
905         loc->locinfo->mb_cur_max = cp_info.MaxCharSize;
906
907         loc->locinfo->ctype1_refcount = MSVCRT_malloc(sizeof(int));
908         loc->locinfo->ctype1 = MSVCRT_malloc(sizeof(short[257]));
909         if(!loc->locinfo->ctype1_refcount || !loc->locinfo->ctype1) {
910             MSVCRT__free_locale(loc);
911             return NULL;
912         }
913
914         *loc->locinfo->ctype1_refcount = 1;
915         loc->locinfo->ctype1[0] = 0;
916         loc->locinfo->pctype = loc->locinfo->ctype1+1;
917
918         buf[1] = buf[2] = '\0';
919         for(i=1; i<257; i++) {
920             buf[0] = i-1;
921
922             /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
923             loc->locinfo->ctype1[i] = 0;
924
925             GetStringTypeA(lcid[MSVCRT_LC_CTYPE], CT_CTYPE1, buf,
926                     1, loc->locinfo->ctype1+i);
927         }
928
929         for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
930             for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
931                 loc->locinfo->ctype1[j+1] |= MSVCRT__LEADBYTE;
932     } else {
933         loc->locinfo->lc_clike = 1;
934         loc->locinfo->mb_cur_max = 1;
935         loc->locinfo->pctype = MSVCRT__ctype+1;
936         loc->locinfo->lc_category[MSVCRT_LC_CTYPE].locale = MSVCRT__strdup("C");
937     }
938
939     for(i=0; i<256; i++) {
940         if(loc->locinfo->pctype[i] & MSVCRT__LEADBYTE)
941             buf[i] = ' ';
942         else
943             buf[i] = i;
944
945     }
946
947     if(lcid[MSVCRT_LC_CTYPE]) {
948         LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
949                 (char*)loc->locinfo->pclmap, 256);
950         LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
951                 (char*)loc->locinfo->pcumap, 256);
952     } else {
953         for(i=0; i<256; i++) {
954             loc->locinfo->pclmap[i] = (i>='A' && i<='Z' ? i-'A'+'a' : i);
955             loc->locinfo->pcumap[i] = (i>='a' && i<='z' ? i-'a'+'A' : i);
956         }
957     }
958
959     _setmbcp_l(loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage, lcid[MSVCRT_LC_CTYPE], loc->mbcinfo);
960
961     if(lcid[MSVCRT_LC_MONETARY] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_MONETARY)) {
962         if(update_threadlocinfo_category(lcid[MSVCRT_LC_MONETARY], cp[MSVCRT_LC_MONETARY], loc, MSVCRT_LC_MONETARY)) {
963             MSVCRT__free_locale(loc);
964             return NULL;
965         }
966
967         loc->locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
968         loc->locinfo->lconv_mon_refcount = MSVCRT_malloc(sizeof(int));
969         if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_mon_refcount) {
970             MSVCRT__free_locale(loc);
971             return NULL;
972         }
973
974         *loc->locinfo->lconv_intl_refcount = 1;
975         *loc->locinfo->lconv_mon_refcount = 1;
976
977         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SINTLSYMBOL
978                 |LOCALE_NOUSEROVERRIDE, buf, 256);
979         if(i && (loc->locinfo->lconv->int_curr_symbol = MSVCRT_malloc(i)))
980             memcpy(loc->locinfo->lconv->int_curr_symbol, buf, i);
981         else {
982             MSVCRT__free_locale(loc);
983             return NULL;
984         }
985
986         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SCURRENCY
987                 |LOCALE_NOUSEROVERRIDE, buf, 256);
988         if(i && (loc->locinfo->lconv->currency_symbol = MSVCRT_malloc(i)))
989             memcpy(loc->locinfo->lconv->currency_symbol, buf, i);
990         else {
991             MSVCRT__free_locale(loc);
992             return NULL;
993         }
994
995         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONDECIMALSEP
996                 |LOCALE_NOUSEROVERRIDE, buf, 256);
997         if(i && (loc->locinfo->lconv->mon_decimal_point = MSVCRT_malloc(i)))
998             memcpy(loc->locinfo->lconv->mon_decimal_point, buf, i);
999         else {
1000             MSVCRT__free_locale(loc);
1001             return NULL;
1002         }
1003
1004         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONTHOUSANDSEP
1005                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1006         if(i && (loc->locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(i)))
1007             memcpy(loc->locinfo->lconv->mon_thousands_sep, buf, i);
1008         else {
1009             MSVCRT__free_locale(loc);
1010             return NULL;
1011         }
1012
1013         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONGROUPING
1014                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1015         if(i>1)
1016             i = i/2 + (buf[i-2]=='0'?0:1);
1017         if(i && (loc->locinfo->lconv->mon_grouping = MSVCRT_malloc(i))) {
1018             for(i=0; buf[i+1]==';'; i+=2)
1019                 loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1020             loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1021             if(buf[i] != '0')
1022                 loc->locinfo->lconv->mon_grouping[i/2+1] = 127;
1023         } else {
1024             MSVCRT__free_locale(loc);
1025             return NULL;
1026         }
1027
1028         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SPOSITIVESIGN
1029                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1030         if(i && (loc->locinfo->lconv->positive_sign = MSVCRT_malloc(i)))
1031             memcpy(loc->locinfo->lconv->positive_sign, buf, i);
1032         else {
1033             MSVCRT__free_locale(loc);
1034             return NULL;
1035         }
1036
1037         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SNEGATIVESIGN
1038                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1039         if(i && (loc->locinfo->lconv->negative_sign = MSVCRT_malloc(i)))
1040             memcpy(loc->locinfo->lconv->negative_sign, buf, i);
1041         else {
1042             MSVCRT__free_locale(loc);
1043             return NULL;
1044         }
1045
1046         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IINTLCURRDIGITS
1047                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1048             loc->locinfo->lconv->int_frac_digits = atoi(buf);
1049         else {
1050             MSVCRT__free_locale(loc);
1051             return NULL;
1052         }
1053
1054         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_ICURRDIGITS
1055                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1056             loc->locinfo->lconv->frac_digits = atoi(buf);
1057         else {
1058             MSVCRT__free_locale(loc);
1059             return NULL;
1060         }
1061
1062         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSYMPRECEDES
1063                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1064             loc->locinfo->lconv->p_cs_precedes = atoi(buf);
1065         else {
1066             MSVCRT__free_locale(loc);
1067             return NULL;
1068         }
1069
1070         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSEPBYSPACE
1071                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1072             loc->locinfo->lconv->p_sep_by_space = atoi(buf);
1073         else {
1074             MSVCRT__free_locale(loc);
1075             return NULL;
1076         }
1077
1078         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSYMPRECEDES
1079                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1080             loc->locinfo->lconv->n_cs_precedes = atoi(buf);
1081         else {
1082             MSVCRT__free_locale(loc);
1083             return NULL;
1084         }
1085
1086         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSEPBYSPACE
1087                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1088             loc->locinfo->lconv->n_sep_by_space = atoi(buf);
1089         else {
1090             MSVCRT__free_locale(loc);
1091             return NULL;
1092         }
1093
1094         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSIGNPOSN
1095                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1096             loc->locinfo->lconv->p_sign_posn = atoi(buf);
1097         else {
1098             MSVCRT__free_locale(loc);
1099             return NULL;
1100         }
1101
1102         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSIGNPOSN
1103                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1104             loc->locinfo->lconv->n_sign_posn = atoi(buf);
1105         else {
1106             MSVCRT__free_locale(loc);
1107             return NULL;
1108         }
1109     } else {
1110         loc->locinfo->lconv->int_curr_symbol = MSVCRT_malloc(sizeof(char));
1111         loc->locinfo->lconv->currency_symbol = MSVCRT_malloc(sizeof(char));
1112         loc->locinfo->lconv->mon_decimal_point = MSVCRT_malloc(sizeof(char));
1113         loc->locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(sizeof(char));
1114         loc->locinfo->lconv->mon_grouping = MSVCRT_malloc(sizeof(char));
1115         loc->locinfo->lconv->positive_sign = MSVCRT_malloc(sizeof(char));
1116         loc->locinfo->lconv->negative_sign = MSVCRT_malloc(sizeof(char));
1117
1118         if(!loc->locinfo->lconv->int_curr_symbol || !loc->locinfo->lconv->currency_symbol
1119                 || !loc->locinfo->lconv->mon_decimal_point || !loc->locinfo->lconv->mon_thousands_sep
1120                 || !loc->locinfo->lconv->mon_grouping || !loc->locinfo->lconv->positive_sign
1121                 || !loc->locinfo->lconv->negative_sign) {
1122             MSVCRT__free_locale(loc);
1123             return NULL;
1124         }
1125
1126         loc->locinfo->lconv->int_curr_symbol[0] = '\0';
1127         loc->locinfo->lconv->currency_symbol[0] = '\0';
1128         loc->locinfo->lconv->mon_decimal_point[0] = '\0';
1129         loc->locinfo->lconv->mon_thousands_sep[0] = '\0';
1130         loc->locinfo->lconv->mon_grouping[0] = '\0';
1131         loc->locinfo->lconv->positive_sign[0] = '\0';
1132         loc->locinfo->lconv->negative_sign[0] = '\0';
1133         loc->locinfo->lconv->int_frac_digits = 127;
1134         loc->locinfo->lconv->frac_digits = 127;
1135         loc->locinfo->lconv->p_cs_precedes = 127;
1136         loc->locinfo->lconv->p_sep_by_space = 127;
1137         loc->locinfo->lconv->n_cs_precedes = 127;
1138         loc->locinfo->lconv->n_sep_by_space = 127;
1139         loc->locinfo->lconv->p_sign_posn = 127;
1140         loc->locinfo->lconv->n_sign_posn = 127;
1141
1142         loc->locinfo->lc_category[MSVCRT_LC_MONETARY].locale = MSVCRT__strdup("C");
1143     }
1144
1145     if(lcid[MSVCRT_LC_NUMERIC] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_NUMERIC)) {
1146         if(update_threadlocinfo_category(lcid[MSVCRT_LC_NUMERIC], cp[MSVCRT_LC_NUMERIC], loc, MSVCRT_LC_NUMERIC)) {
1147             MSVCRT__free_locale(loc);
1148             return NULL;
1149         }
1150
1151         if(!loc->locinfo->lconv_intl_refcount)
1152             loc->locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
1153         loc->locinfo->lconv_num_refcount = MSVCRT_malloc(sizeof(int));
1154         if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_num_refcount) {
1155             MSVCRT__free_locale(loc);
1156             return NULL;
1157         }
1158
1159         *loc->locinfo->lconv_intl_refcount = 1;
1160         *loc->locinfo->lconv_num_refcount = 1;
1161
1162         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SDECIMAL
1163                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1164         if(i && (loc->locinfo->lconv->decimal_point = MSVCRT_malloc(i)))
1165             memcpy(loc->locinfo->lconv->decimal_point, buf, i);
1166         else {
1167             MSVCRT__free_locale(loc);
1168             return NULL;
1169         }
1170
1171         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_STHOUSAND
1172                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1173         if(i && (loc->locinfo->lconv->thousands_sep = MSVCRT_malloc(i)))
1174             memcpy(loc->locinfo->lconv->thousands_sep, buf, i);
1175         else {
1176             MSVCRT__free_locale(loc);
1177             return NULL;
1178         }
1179
1180         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SGROUPING
1181                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1182         if(i>1)
1183             i = i/2 + (buf[i-2]=='0'?0:1);
1184         if(i && (loc->locinfo->lconv->grouping = MSVCRT_malloc(i))) {
1185             for(i=0; buf[i+1]==';'; i+=2)
1186                 loc->locinfo->lconv->grouping[i/2] = buf[i]-'0';
1187             loc->locinfo->lconv->grouping[i/2] = buf[i]-'0';
1188             if(buf[i] != '0')
1189                 loc->locinfo->lconv->grouping[i/2+1] = 127;
1190         } else {
1191             MSVCRT__free_locale(loc);
1192             return NULL;
1193         }
1194     } else {
1195         loc->locinfo->lconv->decimal_point = MSVCRT_malloc(sizeof(char[2]));
1196         loc->locinfo->lconv->thousands_sep = MSVCRT_malloc(sizeof(char));
1197         loc->locinfo->lconv->grouping = MSVCRT_malloc(sizeof(char));
1198         if(!loc->locinfo->lconv->decimal_point || !loc->locinfo->lconv->thousands_sep
1199                 || !loc->locinfo->lconv->grouping) {
1200             MSVCRT__free_locale(loc);
1201             return NULL;
1202         }
1203
1204         loc->locinfo->lconv->decimal_point[0] = '.';
1205         loc->locinfo->lconv->decimal_point[1] = '\0';
1206         loc->locinfo->lconv->thousands_sep[0] = '\0';
1207         loc->locinfo->lconv->grouping[0] = '\0';
1208
1209         loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale = MSVCRT__strdup("C");
1210     }
1211
1212     if(lcid[MSVCRT_LC_TIME] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_TIME)) {
1213         if(update_threadlocinfo_category(lcid[MSVCRT_LC_TIME], cp[MSVCRT_LC_TIME], loc, MSVCRT_LC_TIME)) {
1214             MSVCRT__free_locale(loc);
1215             return NULL;
1216         }
1217     } else
1218         loc->locinfo->lc_category[MSVCRT_LC_TIME].locale = MSVCRT__strdup("C");
1219
1220     size = sizeof(MSVCRT___lc_time_data);
1221     lcid_tmp = lcid[MSVCRT_LC_TIME] ? lcid[MSVCRT_LC_TIME] : MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
1222     for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1223         if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1224             size += sizeof(cloc_short_date) + sizeof(cloc_short_dateW);
1225         }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
1226             size += sizeof(cloc_long_date) + sizeof(cloc_long_dateW);
1227         }else {
1228             ret = GetLocaleInfoA(lcid_tmp, time_data[i]
1229                     |LOCALE_NOUSEROVERRIDE, NULL, 0);
1230             if(!ret) {
1231                 MSVCRT__free_locale(loc);
1232                 return NULL;
1233             }
1234             size += ret;
1235
1236             ret = GetLocaleInfoW(lcid_tmp, time_data[i]
1237                     |LOCALE_NOUSEROVERRIDE, NULL, 0);
1238             if(!ret) {
1239                 MSVCRT__free_locale(loc);
1240                 return NULL;
1241             }
1242             size += ret*sizeof(MSVCRT_wchar_t);
1243         }
1244     }
1245
1246     loc->locinfo->lc_time_curr = MSVCRT_malloc(size);
1247     if(!loc->locinfo->lc_time_curr) {
1248         MSVCRT__free_locale(loc);
1249         return NULL;
1250     }
1251
1252     ret = 0;
1253     for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1254         loc->locinfo->lc_time_curr->str.str[i] = &loc->locinfo->lc_time_curr->data[ret];
1255         if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1256             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_short_date, sizeof(cloc_short_date));
1257             ret += sizeof(cloc_short_date);
1258         }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
1259             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_long_date, sizeof(cloc_long_date));
1260             ret += sizeof(cloc_long_date);
1261         }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
1262             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_time, sizeof(cloc_time));
1263             ret += sizeof(cloc_time);
1264         }else {
1265             ret += GetLocaleInfoA(lcid_tmp, time_data[i]|LOCALE_NOUSEROVERRIDE,
1266                     &loc->locinfo->lc_time_curr->data[ret], size-ret);
1267         }
1268     }
1269     for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1270         loc->locinfo->lc_time_curr->wstr[i] = (MSVCRT_wchar_t*)&loc->locinfo->lc_time_curr->data[ret];
1271         if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1272             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_short_dateW, sizeof(cloc_short_dateW));
1273             ret += sizeof(cloc_short_dateW);
1274         }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
1275             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_long_dateW, sizeof(cloc_long_dateW));
1276             ret += sizeof(cloc_long_dateW);
1277         }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
1278             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_timeW, sizeof(cloc_timeW));
1279             ret += sizeof(cloc_timeW);
1280         }else {
1281             ret += GetLocaleInfoW(lcid_tmp, time_data[i]|LOCALE_NOUSEROVERRIDE,
1282                     (MSVCRT_wchar_t*)&loc->locinfo->lc_time_curr->data[ret], size-ret)*sizeof(MSVCRT_wchar_t);
1283         }
1284     }
1285     loc->locinfo->lc_time_curr->lcid = lcid[MSVCRT_LC_TIME];
1286
1287     return loc;
1288 }
1289
1290 /*********************************************************************
1291  *             setlocale (MSVCRT.@)
1292  */
1293 char* CDECL MSVCRT_setlocale(int category, const char* locale)
1294 {
1295     MSVCRT__locale_t loc;
1296     MSVCRT_pthreadlocinfo locinfo = get_locinfo();
1297
1298     if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX)
1299         return NULL;
1300
1301     if(!locale) {
1302         if(category == MSVCRT_LC_ALL)
1303             return construct_lc_all(locinfo);
1304
1305         return locinfo->lc_category[category].locale;
1306     }
1307
1308     loc = MSVCRT__create_locale(category, locale);
1309     if(!loc) {
1310         WARN("%d %s failed\n", category, locale);
1311         return NULL;
1312     }
1313
1314     LOCK_LOCALE;
1315
1316     switch(category) {
1317         case MSVCRT_LC_ALL:
1318         case MSVCRT_LC_COLLATE:
1319             locinfo->lc_collate_cp = loc->locinfo->lc_collate_cp;
1320             locinfo->lc_handle[MSVCRT_LC_COLLATE] =
1321                 loc->locinfo->lc_handle[MSVCRT_LC_COLLATE];
1322             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_COLLATE].locale,
1323                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_COLLATE].locale);
1324             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_COLLATE].refcount,
1325                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_COLLATE].refcount);
1326
1327             if(category != MSVCRT_LC_ALL)
1328                 break;
1329             /* fall through */
1330         case MSVCRT_LC_CTYPE:
1331             locinfo->lc_handle[MSVCRT_LC_CTYPE] =
1332                 loc->locinfo->lc_handle[MSVCRT_LC_CTYPE];
1333             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_CTYPE].locale,
1334                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_CTYPE].locale);
1335             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_CTYPE].refcount,
1336                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_CTYPE].refcount);
1337
1338             locinfo->lc_codepage = loc->locinfo->lc_codepage;
1339             locinfo->lc_clike = loc->locinfo->lc_clike;
1340             locinfo->mb_cur_max = loc->locinfo->mb_cur_max;
1341
1342             swap_pointers((void**)&locinfo->ctype1_refcount,
1343                     (void**)&loc->locinfo->ctype1_refcount);
1344             swap_pointers((void**)&locinfo->ctype1, (void**)&loc->locinfo->ctype1);
1345             swap_pointers((void**)&locinfo->pctype, (void**)&loc->locinfo->pctype);
1346             swap_pointers((void**)&locinfo->pclmap, (void**)&loc->locinfo->pclmap);
1347             swap_pointers((void**)&locinfo->pcumap, (void**)&loc->locinfo->pcumap);
1348
1349             if(category != MSVCRT_LC_ALL)
1350                 break;
1351             /* fall through */
1352         case MSVCRT_LC_MONETARY:
1353             locinfo->lc_handle[MSVCRT_LC_MONETARY] =
1354                 loc->locinfo->lc_handle[MSVCRT_LC_MONETARY];
1355             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_MONETARY].locale,
1356                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_MONETARY].locale);
1357             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_MONETARY].refcount,
1358                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_MONETARY].refcount);
1359
1360             swap_pointers((void**)&locinfo->lconv->int_curr_symbol,
1361                     (void**)&loc->locinfo->lconv->int_curr_symbol);
1362             swap_pointers((void**)&locinfo->lconv->currency_symbol,
1363                     (void**)&loc->locinfo->lconv->currency_symbol);
1364             swap_pointers((void**)&locinfo->lconv->mon_decimal_point,
1365                     (void**)&loc->locinfo->lconv->mon_decimal_point);
1366             swap_pointers((void**)&locinfo->lconv->mon_thousands_sep,
1367                     (void**)&loc->locinfo->lconv->mon_thousands_sep);
1368             swap_pointers((void**)&locinfo->lconv->mon_grouping,
1369                     (void**)&loc->locinfo->lconv->mon_grouping);
1370             swap_pointers((void**)&locinfo->lconv->positive_sign,
1371                     (void**)&loc->locinfo->lconv->positive_sign);
1372             swap_pointers((void**)&locinfo->lconv->negative_sign,
1373                     (void**)&loc->locinfo->lconv->negative_sign);
1374             locinfo->lconv->int_frac_digits = loc->locinfo->lconv->int_frac_digits;
1375             locinfo->lconv->frac_digits = loc->locinfo->lconv->frac_digits;
1376             locinfo->lconv->p_cs_precedes = loc->locinfo->lconv->p_cs_precedes;
1377             locinfo->lconv->p_sep_by_space = loc->locinfo->lconv->p_sep_by_space;
1378             locinfo->lconv->n_cs_precedes = loc->locinfo->lconv->n_cs_precedes;
1379             locinfo->lconv->n_sep_by_space = loc->locinfo->lconv->n_sep_by_space;
1380             locinfo->lconv->p_sign_posn = loc->locinfo->lconv->p_sign_posn;
1381             locinfo->lconv->n_sign_posn = loc->locinfo->lconv->n_sign_posn;
1382
1383             if(category != MSVCRT_LC_ALL)
1384                 break;
1385             /* fall through */
1386         case MSVCRT_LC_NUMERIC:
1387             locinfo->lc_handle[MSVCRT_LC_NUMERIC] =
1388                 loc->locinfo->lc_handle[MSVCRT_LC_NUMERIC];
1389             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_NUMERIC].locale,
1390                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale);
1391             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_NUMERIC].refcount,
1392                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].refcount);
1393
1394             swap_pointers((void**)&locinfo->lconv->decimal_point,
1395                     (void**)&loc->locinfo->lconv->decimal_point);
1396             swap_pointers((void**)&locinfo->lconv->thousands_sep,
1397                     (void**)&loc->locinfo->lconv->thousands_sep);
1398             swap_pointers((void**)&locinfo->lconv->grouping,
1399                     (void**)&loc->locinfo->lconv->grouping);
1400
1401             if(category != MSVCRT_LC_ALL)
1402                 break;
1403             /* fall through */
1404         case MSVCRT_LC_TIME:
1405             locinfo->lc_handle[MSVCRT_LC_TIME] =
1406                 loc->locinfo->lc_handle[MSVCRT_LC_TIME];
1407             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_TIME].locale,
1408                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_TIME].locale);
1409             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_TIME].refcount,
1410                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_TIME].refcount);
1411             swap_pointers((void**)&locinfo->lc_time_curr,
1412                     (void**)&loc->locinfo->lc_time_curr);
1413
1414             if(category != MSVCRT_LC_ALL)
1415                 break;
1416     }
1417
1418     MSVCRT__free_locale(loc);
1419     UNLOCK_LOCALE;
1420
1421     if(locinfo == MSVCRT_locale->locinfo) {
1422         int i;
1423
1424         MSVCRT___lc_codepage = locinfo->lc_codepage;
1425         MSVCRT___lc_collate_cp = locinfo->lc_collate_cp;
1426         MSVCRT___mb_cur_max = locinfo->mb_cur_max;
1427         MSVCRT__pctype = locinfo->pctype;
1428         for(i=MSVCRT_LC_MIN; i<=MSVCRT_LC_MAX; i++)
1429             MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
1430     }
1431
1432     if(category == MSVCRT_LC_ALL)
1433         return construct_lc_all(locinfo);
1434
1435     return locinfo->lc_category[category].locale;
1436 }
1437
1438 /* _configthreadlocale - not exported in native msvcrt */
1439 int CDECL _configthreadlocale(int type)
1440 {
1441     thread_data_t *data = msvcrt_get_thread_data();
1442     MSVCRT__locale_t locale;
1443     int ret;
1444
1445     if(!data)
1446         return -1;
1447
1448     ret = (data->have_locale ? MSVCRT__ENABLE_PER_THREAD_LOCALE : MSVCRT__DISABLE_PER_THREAD_LOCALE);
1449
1450     if(type == MSVCRT__ENABLE_PER_THREAD_LOCALE) {
1451         if(!data->have_locale) {
1452             /* Copy current global locale */
1453             locale = MSVCRT__create_locale(MSVCRT_LC_ALL, MSVCRT_setlocale(MSVCRT_LC_ALL, NULL));
1454             if(!locale)
1455                 return -1;
1456
1457             data->locinfo = locale->locinfo;
1458             data->mbcinfo = locale->mbcinfo;
1459             data->have_locale = TRUE;
1460             MSVCRT_free(locale);
1461         }
1462
1463         return ret;
1464     }
1465
1466     if(type == MSVCRT__DISABLE_PER_THREAD_LOCALE) {
1467         if(data->have_locale) {
1468             free_locinfo(data->locinfo);
1469             free_mbcinfo(data->mbcinfo);
1470             data->locinfo = MSVCRT_locale->locinfo;
1471             data->mbcinfo = MSVCRT_locale->mbcinfo;
1472             data->have_locale = FALSE;
1473         }
1474
1475         return ret;
1476     }
1477
1478     if(!type)
1479         return ret;
1480
1481     return -1;
1482 }
1483
1484 BOOL msvcrt_init_locale(void)
1485 {
1486     int i;
1487
1488     LOCK_LOCALE;
1489     MSVCRT_locale = MSVCRT__create_locale(0, "C");
1490     UNLOCK_LOCALE;
1491     if(!MSVCRT_locale)
1492         return FALSE;
1493
1494     MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
1495     MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
1496     MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
1497     MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
1498     for(i=MSVCRT_LC_MIN; i<=MSVCRT_LC_MAX; i++)
1499         MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
1500     _setmbcp(_MB_CP_ANSI);
1501     return TRUE;
1502 }