2 * msvcrt.dll locale functions
4 * Copyright 2000 Jon Griffiths
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.
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.
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
22 #include "wine/port.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt
);
41 /* FIXME: Need to hold locale for each LC_* type and aggregate
42 * string to produce lc_all.
44 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
45 #define MAX_LOCALE_LENGTH 256
46 MSVCRT__locale_t MSVCRT_locale
= NULL
;
47 int MSVCRT___lc_codepage
;
48 int MSVCRT___lc_collate_cp
;
49 HANDLE MSVCRT___lc_handle
[MSVCRT_LC_MAX
- MSVCRT_LC_MIN
+ 1] = { 0 };
50 unsigned char charmax
= CHAR_MAX
;
53 #define LOCK_LOCALE _mlock(_SETLOCALE_LOCK);
54 #define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK);
56 #define MSVCRT_LEADBYTE 0x8000
58 /* Friendly country strings & iso codes for synonym support.
59 * Based on MS documentation for setlocale().
61 static const char * const _country_synonyms
[] =
69 "United Kingdom","GB",
70 "United-Kingdom","GB",
79 /* INTERNAL: Map a synonym to an ISO code */
80 static void remap_synonym(char *name
)
83 for (i
= 0; i
< sizeof(_country_synonyms
)/sizeof(char*); i
+= 2 )
85 if (!strcasecmp(_country_synonyms
[i
],name
))
87 TRACE(":Mapping synonym %s to %s\n",name
,_country_synonyms
[i
+1]);
88 name
[0] = _country_synonyms
[i
+1][0];
89 name
[1] = _country_synonyms
[i
+1][1];
96 /* Note: Flags are weighted in order of matching importance */
97 #define FOUND_LANGUAGE 0x4
98 #define FOUND_COUNTRY 0x2
99 #define FOUND_CODEPAGE 0x1
102 char search_language
[MAX_ELEM_LEN
];
103 char search_country
[MAX_ELEM_LEN
];
104 char search_codepage
[MAX_ELEM_LEN
];
105 char found_language
[MAX_ELEM_LEN
];
106 char found_country
[MAX_ELEM_LEN
];
107 char found_codepage
[MAX_ELEM_LEN
];
108 unsigned int match_flags
;
109 LANGID found_lang_id
;
112 #define CONTINUE_LOOKING TRUE
113 #define STOP_LOOKING FALSE
115 /* INTERNAL: Get and compare locale info with a given string */
116 static int compare_info(LCID lcid
, DWORD flags
, char* buff
, const char* cmp
)
119 GetLocaleInfoA(lcid
, flags
|LOCALE_NOUSEROVERRIDE
,buff
, MAX_ELEM_LEN
);
120 if (!buff
[0] || !cmp
[0])
122 /* Partial matches are allowed, e.g. "Germ" matches "Germany" */
123 return !strncasecmp(cmp
, buff
, strlen(cmp
));
127 find_best_locale_proc(HMODULE hModule
, LPCSTR type
, LPCSTR name
, WORD LangID
, LONG_PTR lParam
)
129 locale_search_t
*res
= (locale_search_t
*)lParam
;
130 const LCID lcid
= MAKELCID(LangID
, SORT_DEFAULT
);
131 char buff
[MAX_ELEM_LEN
];
132 unsigned int flags
= 0;
134 if(PRIMARYLANGID(LangID
) == LANG_NEUTRAL
)
135 return CONTINUE_LOOKING
;
138 if (compare_info(lcid
,LOCALE_SISO639LANGNAME
,buff
,res
->search_language
) ||
139 compare_info(lcid
,LOCALE_SABBREVLANGNAME
,buff
,res
->search_language
) ||
140 compare_info(lcid
,LOCALE_SENGLANGUAGE
,buff
,res
->search_language
))
142 TRACE(":Found language: %s->%s\n", res
->search_language
, buff
);
143 flags
|= FOUND_LANGUAGE
;
144 memcpy(res
->found_language
,res
->search_language
,MAX_ELEM_LEN
);
146 else if (res
->match_flags
& FOUND_LANGUAGE
)
148 return CONTINUE_LOOKING
;
152 if (compare_info(lcid
,LOCALE_SISO3166CTRYNAME
,buff
,res
->search_country
) ||
153 compare_info(lcid
,LOCALE_SABBREVCTRYNAME
,buff
,res
->search_country
) ||
154 compare_info(lcid
,LOCALE_SENGCOUNTRY
,buff
,res
->search_country
))
156 TRACE("Found country:%s->%s\n", res
->search_country
, buff
);
157 flags
|= FOUND_COUNTRY
;
158 memcpy(res
->found_country
,res
->search_country
,MAX_ELEM_LEN
);
160 else if (res
->match_flags
& FOUND_COUNTRY
)
162 return CONTINUE_LOOKING
;
166 if (compare_info(lcid
,LOCALE_IDEFAULTCODEPAGE
,buff
,res
->search_codepage
) ||
167 (compare_info(lcid
,LOCALE_IDEFAULTANSICODEPAGE
,buff
,res
->search_codepage
)))
169 TRACE("Found codepage:%s->%s\n", res
->search_codepage
, buff
);
170 flags
|= FOUND_CODEPAGE
;
171 memcpy(res
->found_codepage
,res
->search_codepage
,MAX_ELEM_LEN
);
173 else if (res
->match_flags
& FOUND_CODEPAGE
)
175 return CONTINUE_LOOKING
;
178 if (flags
> res
->match_flags
)
180 /* Found a better match than previously */
181 res
->match_flags
= flags
;
182 res
->found_lang_id
= LangID
;
184 if ((flags
& (FOUND_LANGUAGE
| FOUND_COUNTRY
| FOUND_CODEPAGE
)) ==
185 (FOUND_LANGUAGE
| FOUND_COUNTRY
| FOUND_CODEPAGE
))
187 TRACE(":found exact locale match\n");
190 return CONTINUE_LOOKING
;
193 extern int atoi(const char *);
195 /* Internal: Find the LCID for a locale specification */
196 static LCID
MSVCRT_locale_to_LCID(locale_search_t
* locale
)
199 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR
)RT_STRING
,
200 (LPCSTR
)LOCALE_ILANGUAGE
,find_best_locale_proc
,
203 if (!locale
->match_flags
)
206 /* If we were given something that didn't match, fail */
207 if (locale
->search_country
[0] && !(locale
->match_flags
& FOUND_COUNTRY
))
210 lcid
= MAKELCID(locale
->found_lang_id
, SORT_DEFAULT
);
212 /* Populate partial locale, translating LCID to locale string elements */
213 if (!locale
->found_codepage
[0])
215 /* Even if a codepage is not enumerated for a locale
216 * it can be set if valid */
217 if (locale
->search_codepage
[0])
219 if (IsValidCodePage(atoi(locale
->search_codepage
)))
220 memcpy(locale
->found_codepage
,locale
->search_codepage
,MAX_ELEM_LEN
);
223 /* Special codepage values: OEM & ANSI */
224 if (strcasecmp(locale
->search_codepage
,"OCP"))
226 GetLocaleInfoA(lcid
, LOCALE_IDEFAULTCODEPAGE
,
227 locale
->found_codepage
, MAX_ELEM_LEN
);
229 else if (strcasecmp(locale
->search_codepage
,"ACP"))
231 GetLocaleInfoA(lcid
, LOCALE_IDEFAULTANSICODEPAGE
,
232 locale
->found_codepage
, MAX_ELEM_LEN
);
237 if (!atoi(locale
->found_codepage
))
243 /* Prefer ANSI codepages if present */
244 GetLocaleInfoA(lcid
, LOCALE_IDEFAULTANSICODEPAGE
,
245 locale
->found_codepage
, MAX_ELEM_LEN
);
246 if (!locale
->found_codepage
[0] || !atoi(locale
->found_codepage
))
247 GetLocaleInfoA(lcid
, LOCALE_IDEFAULTCODEPAGE
,
248 locale
->found_codepage
, MAX_ELEM_LEN
);
251 GetLocaleInfoA(lcid
, LOCALE_SENGLANGUAGE
|LOCALE_NOUSEROVERRIDE
,
252 locale
->found_language
, MAX_ELEM_LEN
);
253 GetLocaleInfoA(lcid
, LOCALE_SENGCOUNTRY
|LOCALE_NOUSEROVERRIDE
,
254 locale
->found_country
, MAX_ELEM_LEN
);
258 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
259 static BOOL
update_threadlocinfo_category(LCID lcid
, MSVCRT__locale_t loc
, int category
)
264 if(GetLocaleInfoA(lcid
, LOCALE_ILANGUAGE
, buf
, 256)) {
267 loc
->locinfo
->lc_id
[category
].wLanguage
= 0;
269 loc
->locinfo
->lc_id
[category
].wLanguage
*= 16;
272 loc
->locinfo
->lc_id
[category
].wLanguage
+= *p
-'0';
274 loc
->locinfo
->lc_id
[category
].wLanguage
+= *p
-'a'+10;
279 loc
->locinfo
->lc_id
[category
].wCountry
=
280 loc
->locinfo
->lc_id
[category
].wLanguage
;
283 if(GetLocaleInfoA(lcid
, LOCALE_IDEFAULTANSICODEPAGE
, buf
, 256))
284 loc
->locinfo
->lc_id
[category
].wCodePage
= atoi(buf
);
286 loc
->locinfo
->lc_handle
[category
] = lcid
;
289 len
+= GetLocaleInfoA(lcid
, LOCALE_SLANGUAGE
, buf
, 256);
291 len
+= GetLocaleInfoA(lcid
, LOCALE_SCOUNTRY
, &buf
[len
], 256-len
);
293 len
+= GetLocaleInfoA(lcid
, LOCALE_IDEFAULTANSICODEPAGE
, &buf
[len
], 256-len
);
295 loc
->locinfo
->lc_category
[category
].locale
= MSVCRT_malloc(sizeof(char[len
]));
296 loc
->locinfo
->lc_category
[category
].refcount
= MSVCRT_malloc(sizeof(int));
297 if(!loc
->locinfo
->lc_category
[category
].locale
298 || !loc
->locinfo
->lc_category
[category
].refcount
) {
299 MSVCRT_free(loc
->locinfo
->lc_category
[category
].locale
);
300 MSVCRT_free(loc
->locinfo
->lc_category
[category
].refcount
);
301 loc
->locinfo
->lc_category
[category
].locale
= NULL
;
302 loc
->locinfo
->lc_category
[category
].refcount
= NULL
;
305 memcpy(loc
->locinfo
->lc_category
[category
].locale
, buf
, sizeof(char[len
]));
306 *loc
->locinfo
->lc_category
[category
].refcount
= 1;
311 /* INTERNAL: swap pointers values */
312 static inline void swap_pointers(void **p1
, void **p2
) {
321 /*********************************************************************
322 * wsetlocale (MSVCRT.@)
324 MSVCRT_wchar_t
* CDECL
MSVCRT__wsetlocale(int category
, const MSVCRT_wchar_t
* locale
)
326 static MSVCRT_wchar_t fake
[] = {
327 'E','n','g','l','i','s','h','_','U','n','i','t','e','d',' ',
328 'S','t','a','t','e','s','.','1','2','5','2',0 };
330 FIXME("%d %s\n", category
, debugstr_w(locale
));
335 /*********************************************************************
336 * _Getdays (MSVCRT.@)
338 const char* CDECL
_Getdays(void)
340 static const char MSVCRT_days
[] = ":Sun:Sunday:Mon:Monday:Tue:Tuesday:Wed:"
341 "Wednesday:Thu:Thursday:Fri:Friday:Sat:Saturday";
342 /* FIXME: Use locale */
343 TRACE("(void) semi-stub\n");
347 /*********************************************************************
348 * _Getmonths (MSVCRT.@)
350 const char* CDECL
_Getmonths(void)
352 static const char MSVCRT_months
[] = ":Jan:January:Feb:February:Mar:March:Apr:"
353 "April:May:May:Jun:June:Jul:July:Aug:August:Sep:September:Oct:"
354 "October:Nov:November:Dec:December";
355 /* FIXME: Use locale */
356 TRACE("(void) semi-stub\n");
357 return MSVCRT_months
;
360 /*********************************************************************
361 * _Gettnames (MSVCRT.@)
363 const char* CDECL
_Gettnames(void)
366 TRACE("(void) stub\n");
370 /*********************************************************************
371 * _Strftime (MSVCRT.@)
373 const char* CDECL
_Strftime(char *out
, unsigned int len
, const char *fmt
,
374 const void *tm
, void *foo
)
377 TRACE("(%p %d %s %p %p) stub\n", out
, len
, fmt
, tm
, foo
);
381 /*********************************************************************
382 * __crtLCMapStringA (MSVCRT.@)
384 int CDECL
__crtLCMapStringA(
385 LCID lcid
, DWORD mapflags
, const char* src
, int srclen
, char* dst
,
386 int dstlen
, unsigned int codepage
, int xflag
388 FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
389 lcid
,mapflags
,src
,srclen
,dst
,dstlen
,codepage
,xflag
);
390 /* FIXME: A bit incorrect. But msvcrt itself just converts its
391 * arguments to wide strings and then calls LCMapStringW
393 return LCMapStringA(lcid
,mapflags
,src
,srclen
,dst
,dstlen
);
396 /*********************************************************************
397 * __crtCompareStringA (MSVCRT.@)
399 int CDECL
__crtCompareStringA( LCID lcid
, DWORD flags
, const char *src1
, int len1
,
400 const char *src2
, int len2
)
402 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
403 lcid
, flags
, debugstr_a(src1
), len1
, debugstr_a(src2
), len2
);
404 /* FIXME: probably not entirely right */
405 return CompareStringA( lcid
, flags
, src1
, len1
, src2
, len2
);
408 /*********************************************************************
409 * __crtCompareStringW (MSVCRT.@)
411 int CDECL
__crtCompareStringW( LCID lcid
, DWORD flags
, const MSVCRT_wchar_t
*src1
, int len1
,
412 const MSVCRT_wchar_t
*src2
, int len2
)
414 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
415 lcid
, flags
, debugstr_w(src1
), len1
, debugstr_w(src2
), len2
);
416 /* FIXME: probably not entirely right */
417 return CompareStringW( lcid
, flags
, src1
, len1
, src2
, len2
);
420 /*********************************************************************
421 * __crtGetLocaleInfoW (MSVCRT.@)
423 int CDECL
__crtGetLocaleInfoW( LCID lcid
, LCTYPE type
, MSVCRT_wchar_t
*buffer
, int len
)
425 FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid
, type
, buffer
, len
);
426 /* FIXME: probably not entirely right */
427 return GetLocaleInfoW( lcid
, type
, buffer
, len
);
430 /*********************************************************************
431 * localeconv (MSVCRT.@)
433 struct MSVCRT_lconv
* CDECL
MSVCRT_localeconv(void)
435 static struct MSVCRT_lconv xlconv
;
436 struct lconv
*ylconv
= localeconv();
438 xlconv
.decimal_point
= ylconv
->decimal_point
;
439 xlconv
.thousands_sep
= ylconv
->thousands_sep
;
440 xlconv
.grouping
= ylconv
->grouping
; /* FIXME: fixup charmax here too */
441 xlconv
.int_curr_symbol
= ylconv
->int_curr_symbol
;
442 xlconv
.currency_symbol
= ylconv
->currency_symbol
;
443 xlconv
.mon_decimal_point
= ylconv
->mon_decimal_point
;
444 xlconv
.mon_thousands_sep
= ylconv
->mon_thousands_sep
;
445 xlconv
.mon_grouping
= ylconv
->mon_grouping
;
446 xlconv
.positive_sign
= ylconv
->positive_sign
;
447 xlconv
.negative_sign
= ylconv
->negative_sign
;
448 xlconv
.int_frac_digits
= ylconv
->int_frac_digits
;
449 xlconv
.frac_digits
= ylconv
->frac_digits
;
450 xlconv
.p_cs_precedes
= ylconv
->p_cs_precedes
;
451 xlconv
.p_sep_by_space
= ylconv
->p_sep_by_space
;
452 xlconv
.n_cs_precedes
= ylconv
->n_cs_precedes
;
453 xlconv
.n_sep_by_space
= ylconv
->n_sep_by_space
;
454 xlconv
.p_sign_posn
= ylconv
->p_sign_posn
;
455 xlconv
.n_sign_posn
= ylconv
->n_sign_posn
;
457 if (ylconv
->int_frac_digits
== CHAR_MAX
) xlconv
.int_frac_digits
= charmax
;
458 if (ylconv
->frac_digits
== CHAR_MAX
) xlconv
.frac_digits
= charmax
;
459 if (ylconv
->p_cs_precedes
== CHAR_MAX
) xlconv
.p_cs_precedes
= charmax
;
460 if (ylconv
->p_sep_by_space
== CHAR_MAX
) xlconv
.p_sep_by_space
= charmax
;
461 if (ylconv
->n_cs_precedes
== CHAR_MAX
) xlconv
.n_cs_precedes
= charmax
;
462 if (ylconv
->n_sep_by_space
== CHAR_MAX
) xlconv
.n_sep_by_space
= charmax
;
463 if (ylconv
->p_sign_posn
== CHAR_MAX
) xlconv
.p_sign_posn
= charmax
;
464 if (ylconv
->n_sign_posn
== CHAR_MAX
) xlconv
.n_sign_posn
= charmax
;
469 /*********************************************************************
470 * __lconv_init (MSVCRT.@)
472 void CDECL
__lconv_init(void)
474 /* this is used to make chars unsigned */
478 /*********************************************************************
479 * ___lc_handle_func (MSVCRT.@)
481 HANDLE
* CDECL
___lc_handle_func(void)
483 return MSVCRT___lc_handle
;
486 /*********************************************************************
487 * ___lc_codepage_func (MSVCRT.@)
489 int CDECL
___lc_codepage_func(void)
491 return MSVCRT___lc_codepage
;
494 /*********************************************************************
495 * ___lc_collate_cp_func (MSVCRT.@)
497 int CDECL
___lc_collate_cp_func(void)
499 return MSVCRT___lc_collate_cp
;
502 /* _free_locale - not exported in native msvcrt */
503 void CDECL
_free_locale(MSVCRT__locale_t locale
)
507 for(i
=MSVCRT_LC_MIN
+1; i
<=MSVCRT_LC_MAX
; i
++) {
508 MSVCRT_free(locale
->locinfo
->lc_category
[i
].locale
);
509 MSVCRT_free(locale
->locinfo
->lc_category
[i
].refcount
);
512 if(locale
->locinfo
->lconv
) {
513 MSVCRT_free(locale
->locinfo
->lconv
->decimal_point
);
514 MSVCRT_free(locale
->locinfo
->lconv
->thousands_sep
);
515 MSVCRT_free(locale
->locinfo
->lconv
->grouping
);
516 MSVCRT_free(locale
->locinfo
->lconv
->int_curr_symbol
);
517 MSVCRT_free(locale
->locinfo
->lconv
->currency_symbol
);
518 MSVCRT_free(locale
->locinfo
->lconv
->mon_decimal_point
);
519 MSVCRT_free(locale
->locinfo
->lconv
->mon_thousands_sep
);
520 MSVCRT_free(locale
->locinfo
->lconv
->mon_grouping
);
521 MSVCRT_free(locale
->locinfo
->lconv
->positive_sign
);
522 MSVCRT_free(locale
->locinfo
->lconv
->negative_sign
);
524 MSVCRT_free(locale
->locinfo
->lconv_intl_refcount
);
525 MSVCRT_free(locale
->locinfo
->lconv_num_refcount
);
526 MSVCRT_free(locale
->locinfo
->lconv_mon_refcount
);
527 MSVCRT_free(locale
->locinfo
->lconv
);
529 MSVCRT_free(locale
->locinfo
->ctype1_refcount
);
530 MSVCRT_free(locale
->locinfo
->ctype1
);
532 MSVCRT_free(locale
->locinfo
->pclmap
);
533 MSVCRT_free(locale
->locinfo
->pcumap
);
535 MSVCRT_free(locale
->locinfo
);
536 MSVCRT_free(locale
->mbcinfo
);
540 /* _create_locale - not exported in native msvcrt */
541 MSVCRT__locale_t
_create_locale(int category
, const char *locale
)
543 MSVCRT__locale_t loc
;
548 TRACE("(%d %s)\n", category
, locale
);
550 if(category
<MSVCRT_LC_MIN
|| category
>MSVCRT_LC_MAX
|| !locale
)
553 if(locale
[0]=='C' && !locale
[1])
556 lcid
= GetSystemDefaultLCID();
557 else if (locale
[0] == 'L' && locale
[1] == 'C' && locale
[2] == '_') {
558 FIXME(":restore previous locale not implemented!\n");
559 /* FIXME: Easiest way to do this is parse the string and
560 * call this function recursively with its elements,
561 * Where they differ for each lc_ type.
565 locale_search_t search
;
568 memset(&search
, 0, sizeof(locale_search_t
));
570 cp
= strchr(locale
, '.');
571 region
= strchr(locale
, '_');
573 lstrcpynA(search
.search_language
, locale
, MAX_ELEM_LEN
);
575 lstrcpynA(search
.search_country
, region
+1, MAX_ELEM_LEN
);
576 if(region
-locale
< MAX_ELEM_LEN
)
577 search
.search_language
[region
-locale
] = '\0';
579 search
.search_country
[0] = '\0';
582 lstrcpynA(search
.search_codepage
, cp
+1, MAX_ELEM_LEN
);
583 if(cp
-region
< MAX_ELEM_LEN
)
584 search
.search_country
[cp
-region
] = '\0';
585 if(cp
-locale
< MAX_ELEM_LEN
)
586 search
.search_language
[cp
-locale
] = '\0';
588 search
.search_codepage
[0] = '\0';
590 /* FIXME: MSVCRT_locale_to_LCID is not finding remaped values */
591 remap_synonym(search
.search_country
);
593 lcid
= MSVCRT_locale_to_LCID(&search
);
598 loc
= MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct
));
602 loc
->locinfo
= MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo
));
608 loc
->mbcinfo
= MSVCRT_malloc(sizeof(MSVCRT_threadmbcinfo
));
610 MSVCRT_free(loc
->locinfo
);
615 memset(loc
->locinfo
, 0, sizeof(MSVCRT_threadlocinfo
));
616 memset(loc
->mbcinfo
, 0, sizeof(MSVCRT_threadmbcinfo
));
618 loc
->locinfo
->lconv
= MSVCRT_malloc(sizeof(struct MSVCRT_lconv
));
619 if(!loc
->locinfo
->lconv
) {
623 memset(loc
->locinfo
->lconv
, 0, sizeof(struct MSVCRT_lconv
));
625 loc
->locinfo
->pclmap
= MSVCRT_malloc(sizeof(char[256]));
626 loc
->locinfo
->pcumap
= MSVCRT_malloc(sizeof(char[256]));
627 if(!loc
->locinfo
->pclmap
|| !loc
->locinfo
->pcumap
) {
632 loc
->locinfo
->refcount
= 1;
634 if(lcid
&& (category
==MSVCRT_LC_ALL
|| category
==MSVCRT_LC_COLLATE
)) {
635 if(update_threadlocinfo_category(lcid
, loc
, MSVCRT_LC_COLLATE
)) {
640 loc
->locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
= strdup("C");
642 if(lcid
&& (category
==MSVCRT_LC_ALL
|| category
==MSVCRT_LC_CTYPE
)) {
645 if(update_threadlocinfo_category(lcid
, loc
, MSVCRT_LC_CTYPE
)) {
650 loc
->locinfo
->lc_codepage
= loc
->locinfo
->lc_id
[MSVCRT_LC_CTYPE
].wCodePage
;
651 loc
->locinfo
->lc_collate_cp
= loc
->locinfo
->lc_codepage
;
652 loc
->locinfo
->lc_clike
= 1;
653 if(!GetCPInfo(loc
->locinfo
->lc_codepage
, &cp
)) {
657 loc
->locinfo
->mb_cur_max
= cp
.MaxCharSize
;
659 loc
->locinfo
->ctype1_refcount
= MSVCRT_malloc(sizeof(int));
660 loc
->locinfo
->ctype1
= MSVCRT_malloc(sizeof(short[257]));
661 if(!loc
->locinfo
->ctype1_refcount
|| !loc
->locinfo
->ctype1
) {
666 *loc
->locinfo
->ctype1_refcount
= 1;
667 loc
->locinfo
->ctype1
[0] = 0;
668 loc
->locinfo
->pctype
= loc
->locinfo
->ctype1
+1;
670 buf
[1] = buf
[2] = '\0';
671 for(i
=1; i
<257; i
++) {
674 GetStringTypeA(lcid
, CT_CTYPE1
, buf
, 1, loc
->locinfo
->ctype1
+i
);
675 loc
->locinfo
->ctype1
[i
] |= 0x200;
678 loc
->locinfo
->lc_clike
= 1;
679 loc
->locinfo
->mb_cur_max
= 1;
680 loc
->locinfo
->pctype
= MSVCRT__ctype
+1;
681 loc
->locinfo
->lc_category
[MSVCRT_LC_CTYPE
].locale
= strdup("C");
687 LCMapStringA(lcid
, LCMAP_LOWERCASE
, buf
, 256, (char*)loc
->locinfo
->pclmap
, 256);
688 LCMapStringA(lcid
, LCMAP_UPPERCASE
, buf
, 256, (char*)loc
->locinfo
->pcumap
, 256);
690 loc
->mbcinfo
->refcount
= 1;
691 loc
->mbcinfo
->mbcodepage
= loc
->locinfo
->lc_id
[MSVCRT_LC_CTYPE
].wCodePage
;
693 for(i
=0; i
<256; i
++) {
694 if(loc
->locinfo
->pclmap
[i
] != i
) {
695 loc
->mbcinfo
->mbctype
[i
+1] |= 0x10;
696 loc
->mbcinfo
->mbcasemap
[i
] = loc
->locinfo
->pclmap
[i
];
697 } else if(loc
->locinfo
->pcumap
[i
] != i
) {
698 loc
->mbcinfo
->mbctype
[i
+1] |= 0x20;
699 loc
->mbcinfo
->mbcasemap
[i
] = loc
->locinfo
->pcumap
[i
];
703 if(lcid
&& (category
==MSVCRT_LC_ALL
|| category
==MSVCRT_LC_MONETARY
)) {
704 if(update_threadlocinfo_category(lcid
, loc
, MSVCRT_LC_MONETARY
)) {
709 loc
->locinfo
->lconv_intl_refcount
= MSVCRT_malloc(sizeof(int));
710 loc
->locinfo
->lconv_mon_refcount
= MSVCRT_malloc(sizeof(int));
711 if(!loc
->locinfo
->lconv_intl_refcount
|| !loc
->locinfo
->lconv_mon_refcount
) {
716 *loc
->locinfo
->lconv_intl_refcount
= 1;
717 *loc
->locinfo
->lconv_mon_refcount
= 1;
719 i
= GetLocaleInfoA(lcid
, LOCALE_SINTLSYMBOL
, buf
, 256);
720 if(i
&& (loc
->locinfo
->lconv
->int_curr_symbol
= MSVCRT_malloc(sizeof(char[i
]))))
721 memcpy(loc
->locinfo
->lconv
->int_curr_symbol
, buf
, sizeof(char[i
]));
727 i
= GetLocaleInfoA(lcid
, LOCALE_SCURRENCY
, buf
, 256);
728 if(i
&& (loc
->locinfo
->lconv
->currency_symbol
= MSVCRT_malloc(sizeof(char[i
]))))
729 memcpy(loc
->locinfo
->lconv
->currency_symbol
, buf
, sizeof(char[i
]));
735 i
= GetLocaleInfoA(lcid
, LOCALE_SMONDECIMALSEP
, buf
, 256);
736 if(i
&& (loc
->locinfo
->lconv
->mon_decimal_point
= MSVCRT_malloc(sizeof(char[i
]))))
737 memcpy(loc
->locinfo
->lconv
->mon_decimal_point
, buf
, sizeof(char[i
]));
743 i
= GetLocaleInfoA(lcid
, LOCALE_SMONTHOUSANDSEP
, buf
, 256);
744 if(i
&& (loc
->locinfo
->lconv
->mon_thousands_sep
= MSVCRT_malloc(sizeof(char[i
]))))
745 memcpy(loc
->locinfo
->lconv
->mon_thousands_sep
, buf
, sizeof(char[i
]));
751 i
= GetLocaleInfoA(lcid
, LOCALE_SMONGROUPING
, buf
, 256);
753 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
754 if(i
&& (loc
->locinfo
->lconv
->mon_grouping
= MSVCRT_malloc(sizeof(char[i
])))) {
755 for(i
=0; buf
[i
+1]==';'; i
+=2)
756 loc
->locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
757 loc
->locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
759 loc
->locinfo
->lconv
->mon_grouping
[i
/2+1] = 127;
765 i
= GetLocaleInfoA(lcid
, LOCALE_SPOSITIVESIGN
, buf
, 256);
766 if(i
&& (loc
->locinfo
->lconv
->positive_sign
= MSVCRT_malloc(sizeof(char[i
]))))
767 memcpy(loc
->locinfo
->lconv
->positive_sign
, buf
, sizeof(char[i
]));
773 i
= GetLocaleInfoA(lcid
, LOCALE_SNEGATIVESIGN
, buf
, 256);
774 if(i
&& (loc
->locinfo
->lconv
->negative_sign
= MSVCRT_malloc(sizeof(char[i
]))))
775 memcpy(loc
->locinfo
->lconv
->negative_sign
, buf
, sizeof(char[i
]));
781 if(GetLocaleInfoA(lcid
, LOCALE_IINTLCURRDIGITS
, buf
, 256))
782 loc
->locinfo
->lconv
->int_frac_digits
= atoi(buf
);
788 if(GetLocaleInfoA(lcid
, LOCALE_ICURRDIGITS
, buf
, 256))
789 loc
->locinfo
->lconv
->frac_digits
= atoi(buf
);
795 if(GetLocaleInfoA(lcid
, LOCALE_IPOSSYMPRECEDES
, buf
, 256))
796 loc
->locinfo
->lconv
->p_cs_precedes
= atoi(buf
);
802 if(GetLocaleInfoA(lcid
, LOCALE_IPOSSEPBYSPACE
, buf
, 256))
803 loc
->locinfo
->lconv
->p_sep_by_space
= atoi(buf
);
809 if(GetLocaleInfoA(lcid
, LOCALE_INEGSYMPRECEDES
, buf
, 256))
810 loc
->locinfo
->lconv
->n_cs_precedes
= atoi(buf
);
816 if(GetLocaleInfoA(lcid
, LOCALE_INEGSEPBYSPACE
, buf
, 256))
817 loc
->locinfo
->lconv
->n_sep_by_space
= atoi(buf
);
823 if(GetLocaleInfoA(lcid
, LOCALE_IPOSSIGNPOSN
, buf
, 256))
824 loc
->locinfo
->lconv
->p_sign_posn
= atoi(buf
);
830 if(GetLocaleInfoA(lcid
, LOCALE_INEGSIGNPOSN
, buf
, 256))
831 loc
->locinfo
->lconv
->n_sign_posn
= atoi(buf
);
837 loc
->locinfo
->lconv
->int_curr_symbol
= MSVCRT_malloc(sizeof(char));
838 loc
->locinfo
->lconv
->currency_symbol
= MSVCRT_malloc(sizeof(char));
839 loc
->locinfo
->lconv
->mon_decimal_point
= MSVCRT_malloc(sizeof(char));
840 loc
->locinfo
->lconv
->mon_thousands_sep
= MSVCRT_malloc(sizeof(char));
841 loc
->locinfo
->lconv
->mon_grouping
= MSVCRT_malloc(sizeof(char));
842 loc
->locinfo
->lconv
->positive_sign
= MSVCRT_malloc(sizeof(char));
843 loc
->locinfo
->lconv
->negative_sign
= MSVCRT_malloc(sizeof(char));
845 if(!loc
->locinfo
->lconv
->int_curr_symbol
|| !loc
->locinfo
->lconv
->currency_symbol
846 || !loc
->locinfo
->lconv
->mon_decimal_point
|| !loc
->locinfo
->lconv
->mon_thousands_sep
847 || !loc
->locinfo
->lconv
->mon_grouping
|| !loc
->locinfo
->lconv
->positive_sign
848 || !loc
->locinfo
->lconv
->negative_sign
) {
853 loc
->locinfo
->lconv
->int_curr_symbol
[0] = '\0';
854 loc
->locinfo
->lconv
->currency_symbol
[0] = '\0';
855 loc
->locinfo
->lconv
->mon_decimal_point
[0] = '\0';
856 loc
->locinfo
->lconv
->mon_thousands_sep
[0] = '\0';
857 loc
->locinfo
->lconv
->mon_grouping
[0] = '\0';
858 loc
->locinfo
->lconv
->positive_sign
[0] = '\0';
859 loc
->locinfo
->lconv
->negative_sign
[0] = '\0';
860 loc
->locinfo
->lconv
->int_frac_digits
= 127;
861 loc
->locinfo
->lconv
->frac_digits
= 127;
862 loc
->locinfo
->lconv
->p_cs_precedes
= 127;
863 loc
->locinfo
->lconv
->p_sep_by_space
= 127;
864 loc
->locinfo
->lconv
->n_cs_precedes
= 127;
865 loc
->locinfo
->lconv
->n_sep_by_space
= 127;
866 loc
->locinfo
->lconv
->p_sign_posn
= 127;
867 loc
->locinfo
->lconv
->n_sign_posn
= 127;
869 loc
->locinfo
->lc_category
[MSVCRT_LC_MONETARY
].locale
= strdup("C");
872 if(lcid
&& (category
==MSVCRT_LC_ALL
|| category
==MSVCRT_LC_NUMERIC
)) {
873 if(update_threadlocinfo_category(lcid
, loc
, MSVCRT_LC_NUMERIC
)) {
878 if(!loc
->locinfo
->lconv_intl_refcount
)
879 loc
->locinfo
->lconv_intl_refcount
= MSVCRT_malloc(sizeof(int));
880 loc
->locinfo
->lconv_num_refcount
= MSVCRT_malloc(sizeof(int));
881 if(!loc
->locinfo
->lconv_intl_refcount
|| !loc
->locinfo
->lconv_num_refcount
) {
886 *loc
->locinfo
->lconv_intl_refcount
= 1;
887 *loc
->locinfo
->lconv_num_refcount
= 1;
889 i
= GetLocaleInfoA(lcid
, LOCALE_SDECIMAL
, buf
, 256);
890 if(i
&& (loc
->locinfo
->lconv
->decimal_point
= MSVCRT_malloc(sizeof(char[i
]))))
891 memcpy(loc
->locinfo
->lconv
->decimal_point
, buf
, sizeof(char[i
]));
897 i
= GetLocaleInfoA(lcid
, LOCALE_STHOUSAND
, buf
, 256);
898 if(i
&& (loc
->locinfo
->lconv
->thousands_sep
= MSVCRT_malloc(sizeof(char[i
]))))
899 memcpy(loc
->locinfo
->lconv
->thousands_sep
, buf
, sizeof(char[i
]));
905 i
= GetLocaleInfoA(lcid
, LOCALE_SGROUPING
, buf
, 256);
907 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
908 if(i
&& (loc
->locinfo
->lconv
->grouping
= MSVCRT_malloc(sizeof(char[i
])))) {
909 for(i
=0; buf
[i
+1]==';'; i
+=2)
910 loc
->locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
911 loc
->locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
913 loc
->locinfo
->lconv
->grouping
[i
/2+1] = 127;
919 loc
->locinfo
->lconv
->decimal_point
= MSVCRT_malloc(sizeof(char[2]));
920 loc
->locinfo
->lconv
->thousands_sep
= MSVCRT_malloc(sizeof(char));
921 loc
->locinfo
->lconv
->grouping
= MSVCRT_malloc(sizeof(char));
922 if(!loc
->locinfo
->lconv
->decimal_point
|| !loc
->locinfo
->lconv
->thousands_sep
923 || !loc
->locinfo
->lconv
->grouping
) {
928 loc
->locinfo
->lconv
->decimal_point
[0] = '.';
929 loc
->locinfo
->lconv
->decimal_point
[1] = '\0';
930 loc
->locinfo
->lconv
->thousands_sep
[0] = '\0';
931 loc
->locinfo
->lconv
->grouping
[0] = '\0';
933 loc
->locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].locale
= strdup("C");
936 if(lcid
&& (category
==MSVCRT_LC_ALL
|| category
==MSVCRT_LC_TIME
)) {
937 if(update_threadlocinfo_category(lcid
, loc
, MSVCRT_LC_TIME
)) {
942 loc
->locinfo
->lc_category
[MSVCRT_LC_TIME
].locale
= strdup("C");
947 /*********************************************************************
948 * setlocale (MSVCRT.@)
950 char* CDECL
MSVCRT_setlocale(int category
, const char* locale
)
952 static char current_lc_all
[MAX_LOCALE_LENGTH
];
954 MSVCRT__locale_t loc
;
957 if(category
== MSVCRT_LC_ALL
) {
958 sprintf(current_lc_all
,
959 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
960 MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
,
961 MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_CTYPE
].locale
,
962 MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_MONETARY
].locale
,
963 MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].locale
,
964 MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_TIME
].locale
);
966 return current_lc_all
;
969 return MSVCRT_locale
->locinfo
->lc_category
[category
].locale
;
972 loc
= _create_locale(category
, locale
);
982 case MSVCRT_LC_COLLATE
:
983 MSVCRT_locale
->locinfo
->lc_handle
[MSVCRT_LC_COLLATE
] =
984 loc
->locinfo
->lc_handle
[MSVCRT_LC_COLLATE
];
985 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
,
986 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
);
987 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_COLLATE
].refcount
,
988 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_COLLATE
].refcount
);
990 if(category
!= MSVCRT_LC_ALL
)
992 case MSVCRT_LC_CTYPE
:
993 MSVCRT_locale
->locinfo
->lc_handle
[MSVCRT_LC_CTYPE
] =
994 loc
->locinfo
->lc_handle
[MSVCRT_LC_CTYPE
];
995 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_CTYPE
].locale
,
996 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_CTYPE
].locale
);
997 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_CTYPE
].refcount
,
998 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_CTYPE
].refcount
);
1000 MSVCRT_locale
->locinfo
->lc_codepage
= loc
->locinfo
->lc_codepage
;
1001 MSVCRT_locale
->locinfo
->lc_collate_cp
= loc
->locinfo
->lc_collate_cp
;
1002 MSVCRT_locale
->locinfo
->lc_clike
= loc
->locinfo
->lc_clike
;
1003 MSVCRT_locale
->locinfo
->mb_cur_max
= loc
->locinfo
->mb_cur_max
;
1005 swap_pointers((void**)&MSVCRT_locale
->locinfo
->ctype1_refcount
,
1006 (void**)&loc
->locinfo
->ctype1_refcount
);
1007 swap_pointers((void**)&MSVCRT_locale
->locinfo
->ctype1
, (void**)&loc
->locinfo
->ctype1
);
1008 swap_pointers((void**)&MSVCRT_locale
->locinfo
->pctype
, (void**)&loc
->locinfo
->pctype
);
1009 swap_pointers((void**)&MSVCRT_locale
->locinfo
->pclmap
, (void**)&loc
->locinfo
->pclmap
);
1010 swap_pointers((void**)&MSVCRT_locale
->locinfo
->pcumap
, (void**)&loc
->locinfo
->pcumap
);
1012 memcpy(MSVCRT_locale
->mbcinfo
, loc
->mbcinfo
, sizeof(MSVCRT_threadmbcinfo
));
1014 if(category
!= MSVCRT_LC_ALL
)
1016 case MSVCRT_LC_MONETARY
:
1017 MSVCRT_locale
->locinfo
->lc_handle
[MSVCRT_LC_MONETARY
] =
1018 loc
->locinfo
->lc_handle
[MSVCRT_LC_MONETARY
];
1019 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_MONETARY
].locale
,
1020 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_MONETARY
].locale
);
1021 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_MONETARY
].refcount
,
1022 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_MONETARY
].refcount
);
1024 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->int_curr_symbol
,
1025 (void**)&loc
->locinfo
->lconv
->int_curr_symbol
);
1026 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->currency_symbol
,
1027 (void**)&loc
->locinfo
->lconv
->currency_symbol
);
1028 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->mon_decimal_point
,
1029 (void**)&loc
->locinfo
->lconv
->mon_decimal_point
);
1030 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->mon_thousands_sep
,
1031 (void**)&loc
->locinfo
->lconv
->mon_thousands_sep
);
1032 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->mon_grouping
,
1033 (void**)&loc
->locinfo
->lconv
->mon_grouping
);
1034 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->positive_sign
,
1035 (void**)&loc
->locinfo
->lconv
->positive_sign
);
1036 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->negative_sign
,
1037 (void**)&loc
->locinfo
->lconv
->negative_sign
);
1038 MSVCRT_locale
->locinfo
->lconv
->int_frac_digits
= loc
->locinfo
->lconv
->int_frac_digits
;
1039 MSVCRT_locale
->locinfo
->lconv
->frac_digits
= loc
->locinfo
->lconv
->frac_digits
;
1040 MSVCRT_locale
->locinfo
->lconv
->p_cs_precedes
= loc
->locinfo
->lconv
->p_cs_precedes
;
1041 MSVCRT_locale
->locinfo
->lconv
->p_sep_by_space
= loc
->locinfo
->lconv
->p_sep_by_space
;
1042 MSVCRT_locale
->locinfo
->lconv
->n_cs_precedes
= loc
->locinfo
->lconv
->n_cs_precedes
;
1043 MSVCRT_locale
->locinfo
->lconv
->n_sep_by_space
= loc
->locinfo
->lconv
->n_sep_by_space
;
1044 MSVCRT_locale
->locinfo
->lconv
->p_sign_posn
= loc
->locinfo
->lconv
->p_sign_posn
;
1045 MSVCRT_locale
->locinfo
->lconv
->n_sign_posn
= loc
->locinfo
->lconv
->n_sign_posn
;
1047 if(category
!= MSVCRT_LC_ALL
)
1049 case MSVCRT_LC_NUMERIC
:
1050 MSVCRT_locale
->locinfo
->lc_handle
[MSVCRT_LC_NUMERIC
] =
1051 loc
->locinfo
->lc_handle
[MSVCRT_LC_NUMERIC
];
1052 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].locale
,
1053 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].locale
);
1054 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].refcount
,
1055 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].refcount
);
1057 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->decimal_point
,
1058 (void**)&loc
->locinfo
->lconv
->decimal_point
);
1059 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->thousands_sep
,
1060 (void**)&loc
->locinfo
->lconv
->thousands_sep
);
1061 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lconv
->grouping
,
1062 (void**)&loc
->locinfo
->lconv
->grouping
);
1064 if(category
!= MSVCRT_LC_ALL
)
1066 case MSVCRT_LC_TIME
:
1067 MSVCRT_locale
->locinfo
->lc_handle
[MSVCRT_LC_TIME
] =
1068 loc
->locinfo
->lc_handle
[MSVCRT_LC_TIME
];
1069 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_TIME
].locale
,
1070 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_TIME
].locale
);
1071 swap_pointers((void**)&MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_TIME
].refcount
,
1072 (void**)&loc
->locinfo
->lc_category
[MSVCRT_LC_TIME
].refcount
);
1074 if(category
!= MSVCRT_LC_ALL
)
1079 MSVCRT_locale
= loc
;
1085 MSVCRT___lc_codepage
= MSVCRT_locale
->locinfo
->lc_codepage
;
1086 MSVCRT___lc_collate_cp
= MSVCRT_locale
->locinfo
->lc_collate_cp
;
1087 MSVCRT___mb_cur_max
= MSVCRT_locale
->locinfo
->mb_cur_max
;
1088 MSVCRT__pctype
= MSVCRT_locale
->locinfo
->pctype
;
1090 if(category
== MSVCRT_LC_ALL
)
1091 return MSVCRT_locale
->locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
;
1093 return MSVCRT_locale
->locinfo
->lc_category
[category
].locale
;