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