msvcrt: Added UTF8 support to read function.
[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     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 /* _get_current_locale - not exported in native msvcrt */
706 MSVCRT__locale_t CDECL MSVCRT__get_current_locale(void)
707 {
708     MSVCRT__locale_t loc = MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct));
709     if(!loc)
710         return NULL;
711
712     loc->locinfo = get_locinfo();
713     loc->mbcinfo = get_mbcinfo();
714     InterlockedIncrement(&loc->locinfo->refcount);
715     InterlockedIncrement(&loc->mbcinfo->refcount);
716     return loc;
717 }
718
719 /* _free_locale - not exported in native msvcrt */
720 void CDECL MSVCRT__free_locale(MSVCRT__locale_t locale)
721 {
722     if (!locale)
723         return;
724
725     free_locinfo(locale->locinfo);
726     free_mbcinfo(locale->mbcinfo);
727     MSVCRT_free(locale);
728 }
729
730 /* _create_locale - not exported in native msvcrt */
731 MSVCRT__locale_t CDECL MSVCRT__create_locale(int category, const char *locale)
732 {
733     static const DWORD time_data[] = {
734         LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
735         LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
736         LOCALE_SABBREVDAYNAME6,
737         LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
738         LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6,
739         LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
740         LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
741         LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
742         LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
743         LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
744         LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
745         LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
746         LOCALE_S1159, LOCALE_S2359,
747         LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
748         LOCALE_STIMEFORMAT
749     };
750     static const char collate[] = "COLLATE=";
751     static const char ctype[] = "CTYPE=";
752     static const char monetary[] = "MONETARY=";
753     static const char numeric[] = "NUMERIC=";
754     static const char time[] = "TIME=";
755     static const char cloc_short_date[] = "MM/dd/yy";
756     static const MSVCRT_wchar_t cloc_short_dateW[] = {'M','M','/','d','d','/','y','y',0};
757     static const char cloc_long_date[] = "dddd, MMMM dd, yyyy";
758     static const MSVCRT_wchar_t cloc_long_dateW[] = {'d','d','d','d',',',' ','M','M','M','M',' ','d','d',',',' ','y','y','y','y',0};
759     static const char cloc_time[] = "HH:mm:ss";
760     static const MSVCRT_wchar_t cloc_timeW[] = {'H','H',':','m','m',':','s','s',0};
761
762     MSVCRT__locale_t loc;
763     LCID lcid[6] = { 0 }, lcid_tmp;
764     unsigned short cp[6] = { 0 };
765     char buf[256];
766     int i, ret, size;
767
768     TRACE("(%d %s)\n", category, locale);
769
770     if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
771         return NULL;
772
773     if(locale[0]=='C' && !locale[1]) {
774         lcid[0] = 0;
775         cp[0] = CP_ACP;
776     } else if(!locale[0]) {
777         lcid[0] = GetSystemDefaultLCID();
778         GetLocaleInfoA(lcid[0], LOCALE_IDEFAULTANSICODEPAGE
779                 |LOCALE_NOUSEROVERRIDE, buf, sizeof(buf));
780         cp[0] = atoi(buf);
781
782         for(i=1; i<6; i++) {
783             lcid[i] = lcid[0];
784             cp[i] = cp[0];
785         }
786     } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
787         const char *p;
788
789         while(1) {
790             locale += 3; /* LC_ */
791             if(!memcmp(locale, collate, sizeof(collate)-1)) {
792                 i = MSVCRT_LC_COLLATE;
793                 locale += sizeof(collate)-1;
794             } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) {
795                 i = MSVCRT_LC_CTYPE;
796                 locale += sizeof(ctype)-1;
797             } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) {
798                 i = MSVCRT_LC_MONETARY;
799                 locale += sizeof(monetary)-1;
800             } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) {
801                 i = MSVCRT_LC_NUMERIC;
802                 locale += sizeof(numeric)-1;
803             } else if(!memcmp(locale, time, sizeof(time)-1)) {
804                 i = MSVCRT_LC_TIME;
805                 locale += sizeof(time)-1;
806             } else
807                 return NULL;
808
809             p = strchr(locale, ';');
810             if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
811                 lcid[i] = 0;
812                 cp[i] = CP_ACP;
813             } else if(p) {
814                 memcpy(buf, locale, p-locale);
815                 buf[p-locale] = '\0';
816                 lcid[i] = MSVCRT_locale_to_LCID(buf, &cp[i]);
817             } else
818                 lcid[i] = MSVCRT_locale_to_LCID(locale, &cp[i]);
819
820             if(lcid[i] == -1)
821                 return NULL;
822
823             if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
824                 break;
825
826             locale = p+1;
827         }
828     } else {
829         lcid[0] = MSVCRT_locale_to_LCID(locale, &cp[0]);
830         if(lcid[0] == -1)
831             return NULL;
832
833         for(i=1; i<6; i++) {
834             lcid[i] = lcid[0];
835             cp[i] = cp[0];
836         }
837     }
838
839     loc = MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct));
840     if(!loc)
841         return NULL;
842
843     loc->locinfo = MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo));
844     if(!loc->locinfo) {
845         MSVCRT_free(loc);
846         return NULL;
847     }
848
849     loc->mbcinfo = MSVCRT_malloc(sizeof(MSVCRT_threadmbcinfo));
850     if(!loc->mbcinfo) {
851         MSVCRT_free(loc->locinfo);
852         MSVCRT_free(loc);
853         return NULL;
854     }
855
856     memset(loc->locinfo, 0, sizeof(MSVCRT_threadlocinfo));
857     loc->locinfo->refcount = 1;
858     loc->mbcinfo->refcount = 1;
859
860     loc->locinfo->lconv = MSVCRT_malloc(sizeof(struct MSVCRT_lconv));
861     if(!loc->locinfo->lconv) {
862         MSVCRT__free_locale(loc);
863         return NULL;
864     }
865     memset(loc->locinfo->lconv, 0, sizeof(struct MSVCRT_lconv));
866
867     loc->locinfo->pclmap = MSVCRT_malloc(sizeof(char[256]));
868     loc->locinfo->pcumap = MSVCRT_malloc(sizeof(char[256]));
869     if(!loc->locinfo->pclmap || !loc->locinfo->pcumap) {
870         MSVCRT__free_locale(loc);
871         return NULL;
872     }
873
874     if(lcid[MSVCRT_LC_COLLATE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_COLLATE)) {
875         if(update_threadlocinfo_category(lcid[MSVCRT_LC_COLLATE], cp[MSVCRT_LC_COLLATE], loc, MSVCRT_LC_COLLATE)) {
876             MSVCRT__free_locale(loc);
877             return NULL;
878         }
879
880         loc->locinfo->lc_collate_cp = loc->locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage;
881     } else
882         loc->locinfo->lc_category[MSVCRT_LC_COLLATE].locale = MSVCRT__strdup("C");
883
884     if(lcid[MSVCRT_LC_CTYPE] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_CTYPE)) {
885         CPINFO cp_info;
886         int j;
887
888         if(update_threadlocinfo_category(lcid[MSVCRT_LC_CTYPE], cp[MSVCRT_LC_CTYPE], loc, MSVCRT_LC_CTYPE)) {
889             MSVCRT__free_locale(loc);
890             return NULL;
891         }
892
893         loc->locinfo->lc_codepage = loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage;
894         loc->locinfo->lc_clike = 1;
895         if(!GetCPInfo(loc->locinfo->lc_codepage, &cp_info)) {
896             MSVCRT__free_locale(loc);
897             return NULL;
898         }
899         loc->locinfo->mb_cur_max = cp_info.MaxCharSize;
900
901         loc->locinfo->ctype1_refcount = MSVCRT_malloc(sizeof(int));
902         loc->locinfo->ctype1 = MSVCRT_malloc(sizeof(short[257]));
903         if(!loc->locinfo->ctype1_refcount || !loc->locinfo->ctype1) {
904             MSVCRT__free_locale(loc);
905             return NULL;
906         }
907
908         *loc->locinfo->ctype1_refcount = 1;
909         loc->locinfo->ctype1[0] = 0;
910         loc->locinfo->pctype = loc->locinfo->ctype1+1;
911
912         buf[1] = buf[2] = '\0';
913         for(i=1; i<257; i++) {
914             buf[0] = i-1;
915
916             /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
917             loc->locinfo->ctype1[i] = 0;
918
919             GetStringTypeA(lcid[MSVCRT_LC_CTYPE], CT_CTYPE1, buf,
920                     1, loc->locinfo->ctype1+i);
921         }
922
923         for(i=0; cp_info.LeadByte[i+1]!=0; i+=2)
924             for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++)
925                 loc->locinfo->ctype1[j+1] |= MSVCRT__LEADBYTE;
926     } else {
927         loc->locinfo->lc_clike = 1;
928         loc->locinfo->mb_cur_max = 1;
929         loc->locinfo->pctype = MSVCRT__ctype+1;
930         loc->locinfo->lc_category[MSVCRT_LC_CTYPE].locale = MSVCRT__strdup("C");
931     }
932
933     for(i=0; i<256; i++) {
934         if(loc->locinfo->pctype[i] & MSVCRT__LEADBYTE)
935             buf[i] = ' ';
936         else
937             buf[i] = i;
938
939     }
940
941     if(lcid[MSVCRT_LC_CTYPE]) {
942         LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_LOWERCASE, buf, 256,
943                 (char*)loc->locinfo->pclmap, 256);
944         LCMapStringA(lcid[MSVCRT_LC_CTYPE], LCMAP_UPPERCASE, buf, 256,
945                 (char*)loc->locinfo->pcumap, 256);
946     } else {
947         for(i=0; i<256; i++) {
948             loc->locinfo->pclmap[i] = (i>='A' && i<='Z' ? i-'A'+'a' : i);
949             loc->locinfo->pcumap[i] = (i>='a' && i<='z' ? i-'a'+'A' : i);
950         }
951     }
952
953     _setmbcp_l(loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage, lcid[MSVCRT_LC_CTYPE], loc->mbcinfo);
954
955     if(lcid[MSVCRT_LC_MONETARY] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_MONETARY)) {
956         if(update_threadlocinfo_category(lcid[MSVCRT_LC_MONETARY], cp[MSVCRT_LC_MONETARY], loc, MSVCRT_LC_MONETARY)) {
957             MSVCRT__free_locale(loc);
958             return NULL;
959         }
960
961         loc->locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
962         loc->locinfo->lconv_mon_refcount = MSVCRT_malloc(sizeof(int));
963         if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_mon_refcount) {
964             MSVCRT__free_locale(loc);
965             return NULL;
966         }
967
968         *loc->locinfo->lconv_intl_refcount = 1;
969         *loc->locinfo->lconv_mon_refcount = 1;
970
971         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SINTLSYMBOL
972                 |LOCALE_NOUSEROVERRIDE, buf, 256);
973         if(i && (loc->locinfo->lconv->int_curr_symbol = MSVCRT_malloc(i)))
974             memcpy(loc->locinfo->lconv->int_curr_symbol, buf, i);
975         else {
976             MSVCRT__free_locale(loc);
977             return NULL;
978         }
979
980         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SCURRENCY
981                 |LOCALE_NOUSEROVERRIDE, buf, 256);
982         if(i && (loc->locinfo->lconv->currency_symbol = MSVCRT_malloc(i)))
983             memcpy(loc->locinfo->lconv->currency_symbol, buf, i);
984         else {
985             MSVCRT__free_locale(loc);
986             return NULL;
987         }
988
989         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONDECIMALSEP
990                 |LOCALE_NOUSEROVERRIDE, buf, 256);
991         if(i && (loc->locinfo->lconv->mon_decimal_point = MSVCRT_malloc(i)))
992             memcpy(loc->locinfo->lconv->mon_decimal_point, buf, i);
993         else {
994             MSVCRT__free_locale(loc);
995             return NULL;
996         }
997
998         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONTHOUSANDSEP
999                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1000         if(i && (loc->locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(i)))
1001             memcpy(loc->locinfo->lconv->mon_thousands_sep, buf, i);
1002         else {
1003             MSVCRT__free_locale(loc);
1004             return NULL;
1005         }
1006
1007         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONGROUPING
1008                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1009         if(i>1)
1010             i = i/2 + (buf[i-2]=='0'?0:1);
1011         if(i && (loc->locinfo->lconv->mon_grouping = MSVCRT_malloc(i))) {
1012             for(i=0; buf[i+1]==';'; i+=2)
1013                 loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1014             loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0';
1015             if(buf[i] != '0')
1016                 loc->locinfo->lconv->mon_grouping[i/2+1] = 127;
1017         } else {
1018             MSVCRT__free_locale(loc);
1019             return NULL;
1020         }
1021
1022         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SPOSITIVESIGN
1023                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1024         if(i && (loc->locinfo->lconv->positive_sign = MSVCRT_malloc(i)))
1025             memcpy(loc->locinfo->lconv->positive_sign, buf, i);
1026         else {
1027             MSVCRT__free_locale(loc);
1028             return NULL;
1029         }
1030
1031         i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SNEGATIVESIGN
1032                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1033         if(i && (loc->locinfo->lconv->negative_sign = MSVCRT_malloc(i)))
1034             memcpy(loc->locinfo->lconv->negative_sign, buf, i);
1035         else {
1036             MSVCRT__free_locale(loc);
1037             return NULL;
1038         }
1039
1040         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IINTLCURRDIGITS
1041                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1042             loc->locinfo->lconv->int_frac_digits = atoi(buf);
1043         else {
1044             MSVCRT__free_locale(loc);
1045             return NULL;
1046         }
1047
1048         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_ICURRDIGITS
1049                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1050             loc->locinfo->lconv->frac_digits = atoi(buf);
1051         else {
1052             MSVCRT__free_locale(loc);
1053             return NULL;
1054         }
1055
1056         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSYMPRECEDES
1057                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1058             loc->locinfo->lconv->p_cs_precedes = atoi(buf);
1059         else {
1060             MSVCRT__free_locale(loc);
1061             return NULL;
1062         }
1063
1064         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSEPBYSPACE
1065                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1066             loc->locinfo->lconv->p_sep_by_space = atoi(buf);
1067         else {
1068             MSVCRT__free_locale(loc);
1069             return NULL;
1070         }
1071
1072         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSYMPRECEDES
1073                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1074             loc->locinfo->lconv->n_cs_precedes = atoi(buf);
1075         else {
1076             MSVCRT__free_locale(loc);
1077             return NULL;
1078         }
1079
1080         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSEPBYSPACE
1081                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1082             loc->locinfo->lconv->n_sep_by_space = atoi(buf);
1083         else {
1084             MSVCRT__free_locale(loc);
1085             return NULL;
1086         }
1087
1088         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_IPOSSIGNPOSN
1089                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1090             loc->locinfo->lconv->p_sign_posn = atoi(buf);
1091         else {
1092             MSVCRT__free_locale(loc);
1093             return NULL;
1094         }
1095
1096         if(GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_INEGSIGNPOSN
1097                     |LOCALE_NOUSEROVERRIDE, buf, 256))
1098             loc->locinfo->lconv->n_sign_posn = atoi(buf);
1099         else {
1100             MSVCRT__free_locale(loc);
1101             return NULL;
1102         }
1103     } else {
1104         loc->locinfo->lconv->int_curr_symbol = MSVCRT_malloc(sizeof(char));
1105         loc->locinfo->lconv->currency_symbol = MSVCRT_malloc(sizeof(char));
1106         loc->locinfo->lconv->mon_decimal_point = MSVCRT_malloc(sizeof(char));
1107         loc->locinfo->lconv->mon_thousands_sep = MSVCRT_malloc(sizeof(char));
1108         loc->locinfo->lconv->mon_grouping = MSVCRT_malloc(sizeof(char));
1109         loc->locinfo->lconv->positive_sign = MSVCRT_malloc(sizeof(char));
1110         loc->locinfo->lconv->negative_sign = MSVCRT_malloc(sizeof(char));
1111
1112         if(!loc->locinfo->lconv->int_curr_symbol || !loc->locinfo->lconv->currency_symbol
1113                 || !loc->locinfo->lconv->mon_decimal_point || !loc->locinfo->lconv->mon_thousands_sep
1114                 || !loc->locinfo->lconv->mon_grouping || !loc->locinfo->lconv->positive_sign
1115                 || !loc->locinfo->lconv->negative_sign) {
1116             MSVCRT__free_locale(loc);
1117             return NULL;
1118         }
1119
1120         loc->locinfo->lconv->int_curr_symbol[0] = '\0';
1121         loc->locinfo->lconv->currency_symbol[0] = '\0';
1122         loc->locinfo->lconv->mon_decimal_point[0] = '\0';
1123         loc->locinfo->lconv->mon_thousands_sep[0] = '\0';
1124         loc->locinfo->lconv->mon_grouping[0] = '\0';
1125         loc->locinfo->lconv->positive_sign[0] = '\0';
1126         loc->locinfo->lconv->negative_sign[0] = '\0';
1127         loc->locinfo->lconv->int_frac_digits = 127;
1128         loc->locinfo->lconv->frac_digits = 127;
1129         loc->locinfo->lconv->p_cs_precedes = 127;
1130         loc->locinfo->lconv->p_sep_by_space = 127;
1131         loc->locinfo->lconv->n_cs_precedes = 127;
1132         loc->locinfo->lconv->n_sep_by_space = 127;
1133         loc->locinfo->lconv->p_sign_posn = 127;
1134         loc->locinfo->lconv->n_sign_posn = 127;
1135
1136         loc->locinfo->lc_category[MSVCRT_LC_MONETARY].locale = MSVCRT__strdup("C");
1137     }
1138
1139     if(lcid[MSVCRT_LC_NUMERIC] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_NUMERIC)) {
1140         if(update_threadlocinfo_category(lcid[MSVCRT_LC_NUMERIC], cp[MSVCRT_LC_NUMERIC], loc, MSVCRT_LC_NUMERIC)) {
1141             MSVCRT__free_locale(loc);
1142             return NULL;
1143         }
1144
1145         if(!loc->locinfo->lconv_intl_refcount)
1146             loc->locinfo->lconv_intl_refcount = MSVCRT_malloc(sizeof(int));
1147         loc->locinfo->lconv_num_refcount = MSVCRT_malloc(sizeof(int));
1148         if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_num_refcount) {
1149             MSVCRT__free_locale(loc);
1150             return NULL;
1151         }
1152
1153         *loc->locinfo->lconv_intl_refcount = 1;
1154         *loc->locinfo->lconv_num_refcount = 1;
1155
1156         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SDECIMAL
1157                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1158         if(i && (loc->locinfo->lconv->decimal_point = MSVCRT_malloc(i)))
1159             memcpy(loc->locinfo->lconv->decimal_point, buf, i);
1160         else {
1161             MSVCRT__free_locale(loc);
1162             return NULL;
1163         }
1164
1165         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_STHOUSAND
1166                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1167         if(i && (loc->locinfo->lconv->thousands_sep = MSVCRT_malloc(i)))
1168             memcpy(loc->locinfo->lconv->thousands_sep, buf, i);
1169         else {
1170             MSVCRT__free_locale(loc);
1171             return NULL;
1172         }
1173
1174         i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SGROUPING
1175                 |LOCALE_NOUSEROVERRIDE, buf, 256);
1176         if(i>1)
1177             i = i/2 + (buf[i-2]=='0'?0:1);
1178         if(i && (loc->locinfo->lconv->grouping = MSVCRT_malloc(i))) {
1179             for(i=0; buf[i+1]==';'; i+=2)
1180                 loc->locinfo->lconv->grouping[i/2] = buf[i]-'0';
1181             loc->locinfo->lconv->grouping[i/2] = buf[i]-'0';
1182             if(buf[i] != '0')
1183                 loc->locinfo->lconv->grouping[i/2+1] = 127;
1184         } else {
1185             MSVCRT__free_locale(loc);
1186             return NULL;
1187         }
1188     } else {
1189         loc->locinfo->lconv->decimal_point = MSVCRT_malloc(sizeof(char[2]));
1190         loc->locinfo->lconv->thousands_sep = MSVCRT_malloc(sizeof(char));
1191         loc->locinfo->lconv->grouping = MSVCRT_malloc(sizeof(char));
1192         if(!loc->locinfo->lconv->decimal_point || !loc->locinfo->lconv->thousands_sep
1193                 || !loc->locinfo->lconv->grouping) {
1194             MSVCRT__free_locale(loc);
1195             return NULL;
1196         }
1197
1198         loc->locinfo->lconv->decimal_point[0] = '.';
1199         loc->locinfo->lconv->decimal_point[1] = '\0';
1200         loc->locinfo->lconv->thousands_sep[0] = '\0';
1201         loc->locinfo->lconv->grouping[0] = '\0';
1202
1203         loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale = MSVCRT__strdup("C");
1204     }
1205
1206     if(lcid[MSVCRT_LC_TIME] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_TIME)) {
1207         if(update_threadlocinfo_category(lcid[MSVCRT_LC_TIME], cp[MSVCRT_LC_TIME], loc, MSVCRT_LC_TIME)) {
1208             MSVCRT__free_locale(loc);
1209             return NULL;
1210         }
1211     } else
1212         loc->locinfo->lc_category[MSVCRT_LC_TIME].locale = MSVCRT__strdup("C");
1213
1214     size = sizeof(MSVCRT___lc_time_data);
1215     lcid_tmp = lcid[MSVCRT_LC_TIME] ? lcid[MSVCRT_LC_TIME] : MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
1216     for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1217         if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1218             size += sizeof(cloc_short_date) + sizeof(cloc_short_dateW);
1219         }else if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1220             size += sizeof(cloc_long_date) + sizeof(cloc_long_dateW);
1221         }else {
1222             ret = GetLocaleInfoA(lcid_tmp, time_data[i]
1223                     |LOCALE_NOUSEROVERRIDE, NULL, 0);
1224             if(!ret) {
1225                 MSVCRT__free_locale(loc);
1226                 return NULL;
1227             }
1228             size += ret;
1229
1230             ret = GetLocaleInfoW(lcid_tmp, time_data[i]
1231                     |LOCALE_NOUSEROVERRIDE, NULL, 0);
1232             if(!ret) {
1233                 MSVCRT__free_locale(loc);
1234                 return NULL;
1235             }
1236             size += ret*sizeof(MSVCRT_wchar_t);
1237         }
1238     }
1239
1240     loc->locinfo->lc_time_curr = MSVCRT_malloc(size);
1241     if(!loc->locinfo->lc_time_curr) {
1242         MSVCRT__free_locale(loc);
1243         return NULL;
1244     }
1245
1246     ret = 0;
1247     for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1248         loc->locinfo->lc_time_curr->str.str[i] = &loc->locinfo->lc_time_curr->data[ret];
1249         if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1250             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_short_date, sizeof(cloc_short_date));
1251             ret += sizeof(cloc_short_date);
1252         }else if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1253             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_long_date, sizeof(cloc_long_date));
1254             ret += sizeof(cloc_long_date);
1255         }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
1256             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_time, sizeof(cloc_time));
1257             ret += sizeof(cloc_time);
1258         }else {
1259             ret += GetLocaleInfoA(lcid_tmp, time_data[i]|LOCALE_NOUSEROVERRIDE,
1260                     &loc->locinfo->lc_time_curr->data[ret], size-ret);
1261         }
1262     }
1263     for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
1264         loc->locinfo->lc_time_curr->wstr[i] = (MSVCRT_wchar_t*)&loc->locinfo->lc_time_curr->data[ret];
1265         if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1266             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_short_dateW, sizeof(cloc_short_dateW));
1267             ret += sizeof(cloc_short_dateW);
1268         }else if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
1269             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_long_dateW, sizeof(cloc_long_dateW));
1270             ret += sizeof(cloc_long_dateW);
1271         }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
1272             memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_timeW, sizeof(cloc_timeW));
1273             ret += sizeof(cloc_timeW);
1274         }else {
1275             ret += GetLocaleInfoW(lcid_tmp, time_data[i]|LOCALE_NOUSEROVERRIDE,
1276                     (MSVCRT_wchar_t*)&loc->locinfo->lc_time_curr->data[ret], size-ret)*sizeof(MSVCRT_wchar_t);
1277         }
1278     }
1279     loc->locinfo->lc_time_curr->lcid = lcid[MSVCRT_LC_TIME];
1280
1281     return loc;
1282 }
1283
1284 /*********************************************************************
1285  *             setlocale (MSVCRT.@)
1286  */
1287 char* CDECL MSVCRT_setlocale(int category, const char* locale)
1288 {
1289     MSVCRT__locale_t loc;
1290     MSVCRT_pthreadlocinfo locinfo = get_locinfo();
1291
1292     if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX)
1293         return NULL;
1294
1295     if(!locale) {
1296         if(category == MSVCRT_LC_ALL)
1297             return construct_lc_all(locinfo);
1298
1299         return locinfo->lc_category[category].locale;
1300     }
1301
1302     loc = MSVCRT__create_locale(category, locale);
1303     if(!loc) {
1304         WARN("%d %s failed\n", category, locale);
1305         return NULL;
1306     }
1307
1308     LOCK_LOCALE;
1309
1310     switch(category) {
1311         case MSVCRT_LC_ALL:
1312         case MSVCRT_LC_COLLATE:
1313             locinfo->lc_collate_cp = loc->locinfo->lc_collate_cp;
1314             locinfo->lc_handle[MSVCRT_LC_COLLATE] =
1315                 loc->locinfo->lc_handle[MSVCRT_LC_COLLATE];
1316             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_COLLATE].locale,
1317                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_COLLATE].locale);
1318             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_COLLATE].refcount,
1319                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_COLLATE].refcount);
1320
1321             if(category != MSVCRT_LC_ALL)
1322                 break;
1323             /* fall through */
1324         case MSVCRT_LC_CTYPE:
1325             locinfo->lc_handle[MSVCRT_LC_CTYPE] =
1326                 loc->locinfo->lc_handle[MSVCRT_LC_CTYPE];
1327             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_CTYPE].locale,
1328                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_CTYPE].locale);
1329             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_CTYPE].refcount,
1330                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_CTYPE].refcount);
1331
1332             locinfo->lc_codepage = loc->locinfo->lc_codepage;
1333             locinfo->lc_clike = loc->locinfo->lc_clike;
1334             locinfo->mb_cur_max = loc->locinfo->mb_cur_max;
1335
1336             swap_pointers((void**)&locinfo->ctype1_refcount,
1337                     (void**)&loc->locinfo->ctype1_refcount);
1338             swap_pointers((void**)&locinfo->ctype1, (void**)&loc->locinfo->ctype1);
1339             swap_pointers((void**)&locinfo->pctype, (void**)&loc->locinfo->pctype);
1340             swap_pointers((void**)&locinfo->pclmap, (void**)&loc->locinfo->pclmap);
1341             swap_pointers((void**)&locinfo->pcumap, (void**)&loc->locinfo->pcumap);
1342
1343             if(category != MSVCRT_LC_ALL)
1344                 break;
1345             /* fall through */
1346         case MSVCRT_LC_MONETARY:
1347             locinfo->lc_handle[MSVCRT_LC_MONETARY] =
1348                 loc->locinfo->lc_handle[MSVCRT_LC_MONETARY];
1349             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_MONETARY].locale,
1350                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_MONETARY].locale);
1351             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_MONETARY].refcount,
1352                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_MONETARY].refcount);
1353
1354             swap_pointers((void**)&locinfo->lconv->int_curr_symbol,
1355                     (void**)&loc->locinfo->lconv->int_curr_symbol);
1356             swap_pointers((void**)&locinfo->lconv->currency_symbol,
1357                     (void**)&loc->locinfo->lconv->currency_symbol);
1358             swap_pointers((void**)&locinfo->lconv->mon_decimal_point,
1359                     (void**)&loc->locinfo->lconv->mon_decimal_point);
1360             swap_pointers((void**)&locinfo->lconv->mon_thousands_sep,
1361                     (void**)&loc->locinfo->lconv->mon_thousands_sep);
1362             swap_pointers((void**)&locinfo->lconv->mon_grouping,
1363                     (void**)&loc->locinfo->lconv->mon_grouping);
1364             swap_pointers((void**)&locinfo->lconv->positive_sign,
1365                     (void**)&loc->locinfo->lconv->positive_sign);
1366             swap_pointers((void**)&locinfo->lconv->negative_sign,
1367                     (void**)&loc->locinfo->lconv->negative_sign);
1368             locinfo->lconv->int_frac_digits = loc->locinfo->lconv->int_frac_digits;
1369             locinfo->lconv->frac_digits = loc->locinfo->lconv->frac_digits;
1370             locinfo->lconv->p_cs_precedes = loc->locinfo->lconv->p_cs_precedes;
1371             locinfo->lconv->p_sep_by_space = loc->locinfo->lconv->p_sep_by_space;
1372             locinfo->lconv->n_cs_precedes = loc->locinfo->lconv->n_cs_precedes;
1373             locinfo->lconv->n_sep_by_space = loc->locinfo->lconv->n_sep_by_space;
1374             locinfo->lconv->p_sign_posn = loc->locinfo->lconv->p_sign_posn;
1375             locinfo->lconv->n_sign_posn = loc->locinfo->lconv->n_sign_posn;
1376
1377             if(category != MSVCRT_LC_ALL)
1378                 break;
1379             /* fall through */
1380         case MSVCRT_LC_NUMERIC:
1381             locinfo->lc_handle[MSVCRT_LC_NUMERIC] =
1382                 loc->locinfo->lc_handle[MSVCRT_LC_NUMERIC];
1383             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_NUMERIC].locale,
1384                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].locale);
1385             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_NUMERIC].refcount,
1386                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_NUMERIC].refcount);
1387
1388             swap_pointers((void**)&locinfo->lconv->decimal_point,
1389                     (void**)&loc->locinfo->lconv->decimal_point);
1390             swap_pointers((void**)&locinfo->lconv->thousands_sep,
1391                     (void**)&loc->locinfo->lconv->thousands_sep);
1392             swap_pointers((void**)&locinfo->lconv->grouping,
1393                     (void**)&loc->locinfo->lconv->grouping);
1394
1395             if(category != MSVCRT_LC_ALL)
1396                 break;
1397             /* fall through */
1398         case MSVCRT_LC_TIME:
1399             locinfo->lc_handle[MSVCRT_LC_TIME] =
1400                 loc->locinfo->lc_handle[MSVCRT_LC_TIME];
1401             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_TIME].locale,
1402                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_TIME].locale);
1403             swap_pointers((void**)&locinfo->lc_category[MSVCRT_LC_TIME].refcount,
1404                     (void**)&loc->locinfo->lc_category[MSVCRT_LC_TIME].refcount);
1405             swap_pointers((void**)&locinfo->lc_time_curr,
1406                     (void**)&loc->locinfo->lc_time_curr);
1407
1408             if(category != MSVCRT_LC_ALL)
1409                 break;
1410     }
1411
1412     MSVCRT__free_locale(loc);
1413     UNLOCK_LOCALE;
1414
1415     if(locinfo == MSVCRT_locale->locinfo) {
1416         int i;
1417
1418         MSVCRT___lc_codepage = locinfo->lc_codepage;
1419         MSVCRT___lc_collate_cp = locinfo->lc_collate_cp;
1420         MSVCRT___mb_cur_max = locinfo->mb_cur_max;
1421         MSVCRT__pctype = locinfo->pctype;
1422         for(i=MSVCRT_LC_MIN; i<=MSVCRT_LC_MAX; i++)
1423             MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
1424     }
1425
1426     if(category == MSVCRT_LC_ALL)
1427         return construct_lc_all(locinfo);
1428
1429     return locinfo->lc_category[category].locale;
1430 }
1431
1432 /* _configthreadlocale - not exported in native msvcrt */
1433 int CDECL _configthreadlocale(int type)
1434 {
1435     thread_data_t *data = msvcrt_get_thread_data();
1436     MSVCRT__locale_t locale;
1437     int ret;
1438
1439     if(!data)
1440         return -1;
1441
1442     ret = (data->have_locale ? MSVCRT__ENABLE_PER_THREAD_LOCALE : MSVCRT__DISABLE_PER_THREAD_LOCALE);
1443
1444     if(type == MSVCRT__ENABLE_PER_THREAD_LOCALE) {
1445         if(!data->have_locale) {
1446             /* Copy current global locale */
1447             locale = MSVCRT__create_locale(MSVCRT_LC_ALL, MSVCRT_setlocale(MSVCRT_LC_ALL, NULL));
1448             if(!locale)
1449                 return -1;
1450
1451             data->locinfo = locale->locinfo;
1452             data->mbcinfo = locale->mbcinfo;
1453             data->have_locale = TRUE;
1454             MSVCRT_free(locale);
1455         }
1456
1457         return ret;
1458     }
1459
1460     if(type == MSVCRT__DISABLE_PER_THREAD_LOCALE) {
1461         if(data->have_locale) {
1462             free_locinfo(data->locinfo);
1463             free_mbcinfo(data->mbcinfo);
1464             data->locinfo = MSVCRT_locale->locinfo;
1465             data->mbcinfo = MSVCRT_locale->mbcinfo;
1466             data->have_locale = FALSE;
1467         }
1468
1469         return ret;
1470     }
1471
1472     if(!type)
1473         return ret;
1474
1475     return -1;
1476 }
1477
1478 BOOL msvcrt_init_locale(void)
1479 {
1480     int i;
1481
1482     LOCK_LOCALE;
1483     MSVCRT_locale = MSVCRT__create_locale(0, "C");
1484     UNLOCK_LOCALE;
1485     if(!MSVCRT_locale)
1486         return FALSE;
1487
1488     MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage;
1489     MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp;
1490     MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max;
1491     MSVCRT__pctype = MSVCRT_locale->locinfo->pctype;
1492     for(i=MSVCRT_LC_MIN; i<=MSVCRT_LC_MAX; i++)
1493         MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i];
1494     _setmbcp(_MB_CP_ANSI);
1495     return TRUE;
1496 }