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