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
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt
);
41 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
42 #define MAX_LOCALE_LENGTH 256
43 _locale_t MSVCRT_locale
= NULL
;
44 unsigned short *MSVCRT__pctype
= NULL
;
45 unsigned int MSVCRT___lc_codepage
= 0;
46 int MSVCRT___lc_collate_cp
= 0;
47 LCID MSVCRT___lc_handle
[LC_MAX
- LC_MIN
+ 1] = { 0 };
48 int MSVCRT___mb_cur_max
= 1;
49 BOOL initial_locale
= TRUE
;
51 #define MSVCRT_LEADBYTE 0x8000
52 #define MSVCRT_C1_DEFINED 0x200
54 __lc_time_data cloc_time_data
=
56 {{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
57 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
58 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
59 "January", "February", "March", "April", "May", "June", "July",
60 "August", "September", "October", "November", "December",
61 "AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"}},
63 MAKELCID(LANG_ENGLISH
, SORT_DEFAULT
),
66 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
67 {{L
"Sun", L
"Mon", L
"Tue", L
"Wed", L
"Thu", L
"Fri", L
"Sat",
68 L
"Sunday", L
"Monday", L
"Tuesday", L
"Wednesday", L
"Thursday", L
"Friday", L
"Saturday",
69 L
"Jan", L
"Feb", L
"Mar", L
"Apr", L
"May", L
"Jun", L
"Jul", L
"Aug", L
"Sep", L
"Oct", L
"Nov", L
"Dec",
70 L
"January", L
"February", L
"March", L
"April", L
"May", L
"June", L
"July",
71 L
"August", L
"September", L
"October", L
"November", L
"December",
72 L
"AM", L
"PM", L
"MM/dd/yy", L
"dddd, MMMM dd, yyyy", L
"HH:mm:ss"}},
79 static const unsigned char cloc_clmap
[256] =
81 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
82 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
83 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
84 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
85 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
86 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
87 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
88 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
89 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
90 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
91 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
92 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
93 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
94 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
95 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
96 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
97 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
98 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
99 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
100 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
101 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
102 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
103 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
104 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
105 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
106 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
107 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
108 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
109 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
110 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
111 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
112 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
115 static const unsigned char cloc_cumap
[256] =
117 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
118 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
119 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
120 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
121 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
122 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
123 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
124 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
125 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
126 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
127 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
128 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
129 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
130 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
131 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
132 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
133 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
134 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
135 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
136 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
137 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
138 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
139 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
140 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
141 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
142 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
143 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
144 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
145 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
146 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
147 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
148 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
151 static char empty
[] = "";
152 static char cloc_dec_point
[] = ".";
153 #if _MSVCR_VER >= 100
154 static wchar_t emptyW
[] = L
"";
155 static wchar_t cloc_dec_pointW
[] = L
".";
157 static struct lconv cloc_lconv
=
159 cloc_dec_point
, empty
, empty
, empty
, empty
, empty
, empty
, empty
, empty
, empty
,
160 CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
,
161 #if _MSVCR_VER >= 100
162 cloc_dec_pointW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
166 /* Friendly country strings & language names abbreviations. */
167 static const char * const _country_synonyms
[] =
170 "american english", "enu",
171 "american-english", "enu",
172 "english-american", "enu",
174 "english-usa", "enu",
178 "english-aus", "ena",
180 "french-belgian", "frb",
182 "english-can", "enc",
183 "french-canadian", "frc",
185 "chinese-simplified", "chs",
186 "chinese-traditional", "cht",
187 "dutch-belgian", "nlb",
191 "french-swiss", "frs",
193 "german-swiss", "des",
194 "italian-swiss", "its",
195 "german-austrian", "dea",
197 "portuguese-brazil", "ptb",
198 "spanish-mexican", "esm",
199 "norwegian-bokmal", "nor",
200 "norwegian-nynorsk", "non",
201 "spanish-modern", "esn"
204 /* INTERNAL: Map a synonym to an ISO code */
205 static void remap_synonym(char *name
)
208 for (i
= 0; i
< ARRAY_SIZE(_country_synonyms
); i
+= 2)
210 if (!_stricmp(_country_synonyms
[i
],name
))
212 TRACE(":Mapping synonym %s to %s\n",name
,_country_synonyms
[i
+1]);
213 strcpy(name
, _country_synonyms
[i
+1]);
219 /* Note: Flags are weighted in order of matching importance */
220 #define FOUND_SNAME 0x4
221 #define FOUND_LANGUAGE 0x2
222 #define FOUND_COUNTRY 0x1
225 char search_language
[MAX_ELEM_LEN
];
226 char search_country
[MAX_ELEM_LEN
];
227 DWORD found_codepage
;
228 unsigned int match_flags
;
229 LANGID found_lang_id
;
233 #define CONTINUE_LOOKING TRUE
234 #define STOP_LOOKING FALSE
236 /* INTERNAL: Get and compare locale info with a given string */
237 static int compare_info(LCID lcid
, DWORD flags
, char* buff
, const char* cmp
, BOOL exact
)
245 GetLocaleInfoA(lcid
, flags
|LOCALE_NOUSEROVERRIDE
, buff
, MAX_ELEM_LEN
);
249 /* Partial matches are only allowed on language/country names */
252 return !_stricmp(cmp
, buff
);
254 return !_strnicmp(cmp
, buff
, len
);
258 find_best_locale_proc( WCHAR
*name
, DWORD locale_flags
, LPARAM lParam
)
260 locale_search_t
*res
= (locale_search_t
*)lParam
;
261 const LCID lcid
= LocaleNameToLCID( name
, 0 );
262 char buff
[MAX_ELEM_LEN
];
263 unsigned int flags
= 0;
265 if (lcid
== LOCALE_CUSTOM_UNSPECIFIED
) return CONTINUE_LOOKING
;
267 #if _MSVCR_VER >= 110
268 if (res
->allow_sname
&& compare_info(lcid
,LOCALE_SNAME
,buff
,res
->search_language
, TRUE
))
270 TRACE(":Found locale: %s->%s\n", res
->search_language
, buff
);
271 res
->match_flags
= FOUND_SNAME
;
272 res
->found_lang_id
= LANGIDFROMLCID(lcid
);
278 if (compare_info(lcid
,LOCALE_SISO639LANGNAME
,buff
,res
->search_language
, TRUE
) ||
279 compare_info(lcid
,LOCALE_SABBREVLANGNAME
,buff
,res
->search_language
, TRUE
) ||
280 compare_info(lcid
,LOCALE_SENGLANGUAGE
,buff
,res
->search_language
, FALSE
))
282 TRACE(":Found language: %s->%s\n", res
->search_language
, buff
);
283 flags
|= FOUND_LANGUAGE
;
285 else if (res
->match_flags
& FOUND_LANGUAGE
)
287 return CONTINUE_LOOKING
;
291 if (compare_info(lcid
,LOCALE_SISO3166CTRYNAME
,buff
,res
->search_country
, TRUE
) ||
292 compare_info(lcid
,LOCALE_SABBREVCTRYNAME
,buff
,res
->search_country
, TRUE
) ||
293 compare_info(lcid
,LOCALE_SENGCOUNTRY
,buff
,res
->search_country
, FALSE
))
295 TRACE("Found country:%s->%s\n", res
->search_country
, buff
);
296 flags
|= FOUND_COUNTRY
;
298 else if (!flags
&& (res
->match_flags
& FOUND_COUNTRY
))
300 return CONTINUE_LOOKING
;
303 if (flags
> res
->match_flags
)
305 /* Found a better match than previously */
306 res
->match_flags
= flags
;
307 res
->found_lang_id
= LANGIDFROMLCID(lcid
);
309 if ((flags
& (FOUND_LANGUAGE
| FOUND_COUNTRY
)) ==
310 (FOUND_LANGUAGE
| FOUND_COUNTRY
))
312 TRACE(":found exact locale match\n");
315 return CONTINUE_LOOKING
;
318 /* Internal: Find the LCID for a locale specification */
319 LCID
locale_to_LCID(const char *locale
, unsigned short *codepage
, BOOL
*sname
)
321 thread_data_t
*data
= msvcrt_get_thread_data();
322 const char *cp
, *region
;
323 BOOL is_sname
= FALSE
;
327 if (!strcmp(locale
, data
->cached_locale
)) {
329 *codepage
= data
->cached_cp
;
331 *sname
= data
->cached_sname
;
332 return data
->cached_lcid
;
335 cp
= strchr(locale
, '.');
336 region
= strchr(locale
, '_');
338 if(!locale
[0] || (cp
== locale
&& !region
)) {
339 lcid
= GetUserDefaultLCID();
341 locale_search_t search
;
343 memset(&search
, 0, sizeof(locale_search_t
));
344 lstrcpynA(search
.search_language
, locale
, MAX_ELEM_LEN
);
346 lstrcpynA(search
.search_country
, region
+1, MAX_ELEM_LEN
);
347 if(region
-locale
< MAX_ELEM_LEN
)
348 search
.search_language
[region
-locale
] = '\0';
350 search
.search_country
[0] = '\0';
353 if(region
&& cp
-region
-1<MAX_ELEM_LEN
)
354 search
.search_country
[cp
-region
-1] = '\0';
355 if(cp
-locale
< MAX_ELEM_LEN
)
356 search
.search_language
[cp
-locale
] = '\0';
361 remap_synonym(search
.search_language
);
362 search
.allow_sname
= TRUE
;
365 EnumSystemLocalesEx( find_best_locale_proc
, 0, (LPARAM
)&search
, NULL
);
367 if (!search
.match_flags
)
370 /* If we were given something that didn't match, fail */
371 if (search
.search_language
[0] && !(search
.match_flags
& (FOUND_SNAME
| FOUND_LANGUAGE
)))
373 if (search
.search_country
[0] && !(search
.match_flags
& FOUND_COUNTRY
))
376 lcid
= MAKELCID(search
.found_lang_id
, SORT_DEFAULT
);
377 is_sname
= (search
.match_flags
& FOUND_SNAME
) != 0;
380 /* Obtain code page */
381 if (!cp
|| !cp
[1] || !_strnicmp(cp
, ".ACP", 4)) {
382 GetLocaleInfoW(lcid
, LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
383 (WCHAR
*)&locale_cp
, sizeof(DWORD
)/sizeof(WCHAR
));
385 locale_cp
= GetACP();
386 } else if (!_strnicmp(cp
, ".OCP", 4)) {
387 GetLocaleInfoW(lcid
, LOCALE_IDEFAULTCODEPAGE
| LOCALE_RETURN_NUMBER
,
388 (WCHAR
*)&locale_cp
, sizeof(DWORD
)/sizeof(WCHAR
));
389 #if _MSVCR_VER >= 140
390 } else if (!_strnicmp(cp
, ".UTF-8", 6)
391 || !_strnicmp(cp
, ".UTF8", 5)) {
395 locale_cp
= atoi(cp
+ 1);
397 if (!IsValidCodePage(locale_cp
))
404 *codepage
= locale_cp
;
408 if (strlen(locale
) < sizeof(data
->cached_locale
)) {
409 strcpy(data
->cached_locale
, locale
);
410 data
->cached_lcid
= lcid
;
411 data
->cached_cp
= locale_cp
;
412 data
->cached_sname
= is_sname
;
418 static void copy_threadlocinfo_category(pthreadlocinfo locinfo
,
419 const threadlocinfo
*old_locinfo
, int category
)
421 locinfo
->lc_handle
[category
] = old_locinfo
->lc_handle
[category
];
422 locinfo
->lc_id
[category
] = old_locinfo
->lc_id
[category
];
423 if(!locinfo
->lc_category
[category
].locale
) {
424 locinfo
->lc_category
[category
].locale
= old_locinfo
->lc_category
[category
].locale
;
425 locinfo
->lc_category
[category
].refcount
= old_locinfo
->lc_category
[category
].refcount
;
426 InterlockedIncrement((LONG
*)locinfo
->lc_category
[category
].refcount
);
428 #if _MSVCR_VER >= 110
429 locinfo
->lc_name
[category
] = old_locinfo
->lc_name
[category
];
430 locinfo
->lc_category
[category
].wrefcount
= old_locinfo
->lc_category
[category
].wrefcount
;
431 if(locinfo
->lc_category
[category
].wrefcount
)
432 InterlockedIncrement((LONG
*)locinfo
->lc_category
[category
].wrefcount
);
436 static BOOL
init_category_name(const char *name
, int len
,
437 pthreadlocinfo locinfo
, int category
)
439 locinfo
->lc_category
[category
].locale
= malloc(len
+1);
440 locinfo
->lc_category
[category
].refcount
= malloc(sizeof(int));
441 if(!locinfo
->lc_category
[category
].locale
442 || !locinfo
->lc_category
[category
].refcount
) {
443 free(locinfo
->lc_category
[category
].locale
);
444 free(locinfo
->lc_category
[category
].refcount
);
445 locinfo
->lc_category
[category
].locale
= NULL
;
446 locinfo
->lc_category
[category
].refcount
= NULL
;
450 memcpy(locinfo
->lc_category
[category
].locale
, name
, len
);
451 locinfo
->lc_category
[category
].locale
[len
] = 0;
452 *locinfo
->lc_category
[category
].refcount
= 1;
456 #if _MSVCR_VER >= 110
457 static inline BOOL
set_lc_locale_name(pthreadlocinfo locinfo
, int cat
)
459 LCID lcid
= locinfo
->lc_handle
[cat
];
463 locinfo
->lc_category
[cat
].wrefcount
= malloc(sizeof(int));
464 if(!locinfo
->lc_category
[cat
].wrefcount
)
466 *locinfo
->lc_category
[cat
].wrefcount
= 1;
468 len
= GetLocaleInfoW(lcid
, LOCALE_SISO639LANGNAME
469 |LOCALE_NOUSEROVERRIDE
, buf
, 100);
470 if(!len
) return FALSE
;
472 if(LocaleNameToLCID(buf
, 0) != lcid
)
473 len
= LCIDToLocaleName(lcid
, buf
, 100, 0);
475 if(!len
|| !(locinfo
->lc_name
[cat
] = malloc(len
*sizeof(wchar_t))))
478 memcpy(locinfo
->lc_name
[cat
], buf
, len
*sizeof(wchar_t));
482 static inline BOOL
set_lc_locale_name(pthreadlocinfo locinfo
, int cat
)
488 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
489 static BOOL
update_threadlocinfo_category(LCID lcid
, unsigned short cp
,
490 pthreadlocinfo locinfo
, int category
)
494 if(GetLocaleInfoA(lcid
, LOCALE_ILANGUAGE
|LOCALE_NOUSEROVERRIDE
, buf
, 256)) {
497 locinfo
->lc_id
[category
].wLanguage
= 0;
499 locinfo
->lc_id
[category
].wLanguage
*= 16;
502 locinfo
->lc_id
[category
].wLanguage
+= *p
-'0';
504 locinfo
->lc_id
[category
].wLanguage
+= *p
-'a'+10;
509 locinfo
->lc_id
[category
].wCountry
=
510 locinfo
->lc_id
[category
].wLanguage
;
513 locinfo
->lc_id
[category
].wCodePage
= cp
;
515 locinfo
->lc_handle
[category
] = lcid
;
517 set_lc_locale_name(locinfo
, category
);
519 if(!locinfo
->lc_category
[category
].locale
) {
522 if (lcid
== MAKELANGID( LANG_NORWEGIAN
, SUBLANG_NORWEGIAN_NYNORSK
))
524 /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */
525 strcpy( buf
, "Norwegian-Nynorsk" );
526 len
= strlen( buf
) + 1;
528 else len
+= GetLocaleInfoA(lcid
, LOCALE_SENGLANGUAGE
|LOCALE_NOUSEROVERRIDE
, buf
, 256);
530 len
+= GetLocaleInfoA(lcid
, LOCALE_SENGCOUNTRY
531 |LOCALE_NOUSEROVERRIDE
, &buf
[len
], 256-len
);
533 sprintf(buf
+len
, "%d", cp
);
534 len
+= strlen(buf
+len
);
536 return init_category_name(buf
, len
, locinfo
, category
);
542 /*********************************************************************
543 * _lock_locales (UCRTBASE.@)
545 void CDECL
_lock_locales(void)
547 _lock(_SETLOCALE_LOCK
);
550 /*********************************************************************
551 * _unlock_locales (UCRTBASE.@)
553 void CDECL
_unlock_locales(void)
555 _unlock(_SETLOCALE_LOCK
);
558 static void grab_locinfo(pthreadlocinfo locinfo
)
562 InterlockedIncrement((LONG
*)&locinfo
->refcount
);
563 for(i
=LC_MIN
+1; i
<=LC_MAX
; i
++)
565 InterlockedIncrement((LONG
*)locinfo
->lc_category
[i
].refcount
);
566 if(locinfo
->lc_category
[i
].wrefcount
)
567 InterlockedIncrement((LONG
*)locinfo
->lc_category
[i
].wrefcount
);
569 if(locinfo
->lconv_intl_refcount
)
570 InterlockedIncrement((LONG
*)locinfo
->lconv_intl_refcount
);
571 if(locinfo
->lconv_num_refcount
)
572 InterlockedIncrement((LONG
*)locinfo
->lconv_num_refcount
);
573 if(locinfo
->lconv_mon_refcount
)
574 InterlockedIncrement((LONG
*)locinfo
->lconv_mon_refcount
);
575 if(locinfo
->ctype1_refcount
)
576 InterlockedIncrement((LONG
*)locinfo
->ctype1_refcount
);
577 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
580 static void update_thread_locale(thread_data_t
*data
)
582 if((data
->locale_flags
& LOCALE_FREE
) && ((data
->locale_flags
& LOCALE_THREAD
) ||
583 (data
->locinfo
== MSVCRT_locale
->locinfo
&& data
->mbcinfo
== MSVCRT_locale
->mbcinfo
)))
586 if(data
->locale_flags
& LOCALE_FREE
)
588 free_locinfo(data
->locinfo
);
589 free_mbcinfo(data
->mbcinfo
);
593 data
->locinfo
= MSVCRT_locale
->locinfo
;
594 grab_locinfo(data
->locinfo
);
598 data
->mbcinfo
= MSVCRT_locale
->mbcinfo
;
599 InterlockedIncrement(&data
->mbcinfo
->refcount
);
600 _unlock(_MB_CP_LOCK
);
602 data
->locale_flags
|= LOCALE_FREE
;
605 /* INTERNAL: returns threadlocinfo struct */
606 pthreadlocinfo CDECL
get_locinfo(void) {
607 thread_data_t
*data
= msvcrt_get_thread_data();
608 update_thread_locale(data
);
609 return data
->locinfo
;
612 /* INTERNAL: returns pthreadmbcinfo struct */
613 pthreadmbcinfo CDECL
get_mbcinfo(void) {
614 thread_data_t
*data
= msvcrt_get_thread_data();
615 update_thread_locale(data
);
616 return data
->mbcinfo
;
619 /* INTERNAL: constructs string returned by setlocale */
620 static inline char* construct_lc_all(pthreadlocinfo locinfo
) {
621 static char current_lc_all
[MAX_LOCALE_LENGTH
];
625 for(i
=LC_MIN
+1; i
<LC_MAX
; i
++) {
626 if(strcmp(locinfo
->lc_category
[i
].locale
,
627 locinfo
->lc_category
[i
+1].locale
))
632 return locinfo
->lc_category
[LC_COLLATE
].locale
;
634 sprintf(current_lc_all
,
635 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
636 locinfo
->lc_category
[LC_COLLATE
].locale
,
637 locinfo
->lc_category
[LC_CTYPE
].locale
,
638 locinfo
->lc_category
[LC_MONETARY
].locale
,
639 locinfo
->lc_category
[LC_NUMERIC
].locale
,
640 locinfo
->lc_category
[LC_TIME
].locale
);
642 return current_lc_all
;
646 /*********************************************************************
647 * _Getdays (MSVCRT.@)
649 char* CDECL
_Getdays(void)
651 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
652 int i
, len
, size
= 0;
658 size
+= strlen(cur
->str
.names
.short_wday
[i
]) + 1;
659 size
+= strlen(cur
->str
.names
.wday
[i
]) + 1;
661 out
= malloc(size
+1);
668 len
= strlen(cur
->str
.names
.short_wday
[i
]);
669 memcpy(&out
[size
], cur
->str
.names
.short_wday
[i
], len
);
673 len
= strlen(cur
->str
.names
.wday
[i
]);
674 memcpy(&out
[size
], cur
->str
.names
.wday
[i
], len
);
682 #if _MSVCR_VER >= 110
683 /*********************************************************************
684 * _W_Getdays (MSVCR110.@)
686 wchar_t* CDECL
_W_Getdays(void)
688 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
690 int i
, len
, size
= 0;
695 size
+= wcslen(cur
->wstr
.names
.short_wday
[i
]) + 1;
696 size
+= wcslen(cur
->wstr
.names
.wday
[i
]) + 1;
698 out
= malloc((size
+1)*sizeof(*out
));
705 len
= wcslen(cur
->wstr
.names
.short_wday
[i
]);
706 memcpy(&out
[size
], cur
->wstr
.names
.short_wday
[i
], len
*sizeof(*out
));
710 len
= wcslen(cur
->wstr
.names
.wday
[i
]);
711 memcpy(&out
[size
], cur
->wstr
.names
.wday
[i
], len
*sizeof(*out
));
720 /*********************************************************************
721 * _Getmonths (MSVCRT.@)
723 char* CDECL
_Getmonths(void)
725 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
726 int i
, len
, size
= 0;
731 for(i
=0; i
<12; i
++) {
732 size
+= strlen(cur
->str
.names
.short_mon
[i
]) + 1;
733 size
+= strlen(cur
->str
.names
.mon
[i
]) + 1;
735 out
= malloc(size
+1);
740 for(i
=0; i
<12; i
++) {
742 len
= strlen(cur
->str
.names
.short_mon
[i
]);
743 memcpy(&out
[size
], cur
->str
.names
.short_mon
[i
], len
);
747 len
= strlen(cur
->str
.names
.mon
[i
]);
748 memcpy(&out
[size
], cur
->str
.names
.mon
[i
], len
);
756 #if _MSVCR_VER >= 110
757 /*********************************************************************
758 * _W_Getmonths (MSVCR110.@)
760 wchar_t* CDECL
_W_Getmonths(void)
762 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
764 int i
, len
, size
= 0;
768 for(i
=0; i
<12; i
++) {
769 size
+= wcslen(cur
->wstr
.names
.short_mon
[i
]) + 1;
770 size
+= wcslen(cur
->wstr
.names
.mon
[i
]) + 1;
772 out
= malloc((size
+1)*sizeof(*out
));
777 for(i
=0; i
<12; i
++) {
779 len
= wcslen(cur
->wstr
.names
.short_mon
[i
]);
780 memcpy(&out
[size
], cur
->wstr
.names
.short_mon
[i
], len
*sizeof(*out
));
784 len
= wcslen(cur
->wstr
.names
.mon
[i
]);
785 memcpy(&out
[size
], cur
->wstr
.names
.mon
[i
], len
*sizeof(*out
));
794 /*********************************************************************
795 * _Gettnames (MSVCRT.@)
797 void* CDECL
_Gettnames(void)
799 __lc_time_data
*ret
, *cur
= get_locinfo()->lc_time_curr
;
800 unsigned int i
, len
, size
= sizeof(__lc_time_data
);
804 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++)
805 size
+= strlen(cur
->str
.str
[i
])+1;
806 #if _MSVCR_VER >= 110
807 for(i
=0; i
<ARRAY_SIZE(cur
->wstr
.wstr
); i
++)
808 size
+= (wcslen(cur
->wstr
.wstr
[i
]) + 1) * sizeof(wchar_t);
814 memcpy(ret
, cur
, sizeof(*ret
));
817 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++) {
818 len
= strlen(cur
->str
.str
[i
])+1;
819 memcpy(&ret
->data
[size
], cur
->str
.str
[i
], len
);
820 ret
->str
.str
[i
] = &ret
->data
[size
];
823 #if _MSVCR_VER >= 110
824 for(i
=0; i
<ARRAY_SIZE(cur
->wstr
.wstr
); i
++) {
825 len
= (wcslen(cur
->wstr
.wstr
[i
]) + 1) * sizeof(wchar_t);
826 memcpy(&ret
->data
[size
], cur
->wstr
.wstr
[i
], len
);
827 ret
->wstr
.wstr
[i
] = (wchar_t*)&ret
->data
[size
];
835 #if _MSVCR_VER >= 110
836 /*********************************************************************
837 * _W_Gettnames (MSVCR110.@)
839 void* CDECL
_W_Gettnames(void)
845 /*********************************************************************
846 * __crtLCMapStringA (MSVCRT.@)
848 int CDECL
__crtLCMapStringA(
849 LCID lcid
, DWORD mapflags
, const char* src
, int srclen
, char* dst
,
850 int dstlen
, unsigned int codepage
, int xflag
852 WCHAR buf_in
[32], *in
= buf_in
;
853 WCHAR buf_out
[32], *out
= buf_out
;
854 int in_len
, out_len
, r
;
856 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
857 lcid
, mapflags
, src
, srclen
, dst
, dstlen
, codepage
, xflag
);
859 in_len
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, NULL
, 0);
860 if (!in_len
) return 0;
861 if (in_len
> ARRAY_SIZE(buf_in
))
863 in
= malloc(in_len
* sizeof(WCHAR
));
867 r
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, in
, in_len
);
870 if (mapflags
& LCMAP_SORTKEY
)
872 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, (WCHAR
*)dst
, dstlen
);
876 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, NULL
, 0);
879 if (r
> ARRAY_SIZE(buf_out
))
881 out
= malloc(r
* sizeof(WCHAR
));
889 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, out
, out_len
);
892 r
= WideCharToMultiByte(codepage
, 0, out
, out_len
, dst
, dstlen
, NULL
, NULL
);
895 if (in
!= buf_in
) free(in
);
896 if (out
!= buf_out
) free(out
);
900 /*********************************************************************
901 * __crtLCMapStringW (MSVCRT.@)
903 int CDECL
__crtLCMapStringW(LCID lcid
, DWORD mapflags
, const wchar_t *src
,
904 int srclen
, wchar_t *dst
, int dstlen
, unsigned int codepage
, int xflag
)
906 FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
907 lcid
, mapflags
, debugstr_w(src
), srclen
, dst
, dstlen
, codepage
, xflag
);
909 return LCMapStringW(lcid
, mapflags
, src
, srclen
, dst
, dstlen
);
912 /*********************************************************************
913 * __crtCompareStringA (MSVCRT.@)
915 int CDECL
__crtCompareStringA( LCID lcid
, DWORD flags
, const char *src1
, int len1
,
916 const char *src2
, int len2
)
918 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
919 lcid
, flags
, debugstr_a(src1
), len1
, debugstr_a(src2
), len2
);
920 /* FIXME: probably not entirely right */
921 return CompareStringA( lcid
, flags
, src1
, len1
, src2
, len2
);
924 /*********************************************************************
925 * __crtCompareStringW (MSVCRT.@)
927 int CDECL
__crtCompareStringW( LCID lcid
, DWORD flags
, const wchar_t *src1
, int len1
,
928 const wchar_t *src2
, int len2
)
930 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
931 lcid
, flags
, debugstr_w(src1
), len1
, debugstr_w(src2
), len2
);
932 /* FIXME: probably not entirely right */
933 return CompareStringW( lcid
, flags
, src1
, len1
, src2
, len2
);
936 /*********************************************************************
937 * __crtGetLocaleInfoW (MSVCRT.@)
939 int CDECL
__crtGetLocaleInfoW( LCID lcid
, LCTYPE type
, wchar_t *buffer
, int len
)
941 FIXME("(lcid %lx, type %lx, %p(%d), partial stub\n", lcid
, type
, buffer
, len
);
942 /* FIXME: probably not entirely right */
943 return GetLocaleInfoW( lcid
, type
, buffer
, len
);
946 #if _MSVCR_VER >= 110
947 /*********************************************************************
948 * __crtGetLocaleInfoEx (MSVC110.@)
950 int CDECL
__crtGetLocaleInfoEx( const WCHAR
*locale
, LCTYPE type
, wchar_t *buffer
, int len
)
952 TRACE("(%s, %lx, %p, %d)\n", debugstr_w(locale
), type
, buffer
, len
);
953 return GetLocaleInfoEx(locale
, type
, buffer
, len
);
957 /*********************************************************************
958 * __crtGetStringTypeW(MSVCRT.@)
960 * This function was accepting different number of arguments in older
961 * versions of msvcrt.
963 BOOL CDECL
__crtGetStringTypeW(DWORD unk
, DWORD type
,
964 wchar_t *buffer
, int len
, WORD
*out
)
966 FIXME("(unk %lx, type %lx, wstr %p(%d), %p) partial stub\n",
967 unk
, type
, buffer
, len
, out
);
969 return GetStringTypeW(type
, buffer
, len
, out
);
972 /*********************************************************************
973 * localeconv (MSVCRT.@)
975 struct lconv
* CDECL
localeconv(void)
977 return get_locinfo()->lconv
;
980 /*********************************************************************
981 * __lconv_init (MSVCRT.@)
983 int CDECL
__lconv_init(void)
985 /* this is used to make chars unsigned */
986 cloc_lconv
.int_frac_digits
= (char)UCHAR_MAX
;
987 cloc_lconv
.frac_digits
= (char)UCHAR_MAX
;
988 cloc_lconv
.p_cs_precedes
= (char)UCHAR_MAX
;
989 cloc_lconv
.p_sep_by_space
= (char)UCHAR_MAX
;
990 cloc_lconv
.n_cs_precedes
= (char)UCHAR_MAX
;
991 cloc_lconv
.n_sep_by_space
= (char)UCHAR_MAX
;
992 cloc_lconv
.p_sign_posn
= (char)UCHAR_MAX
;
993 cloc_lconv
.n_sign_posn
= (char)UCHAR_MAX
;
997 /*********************************************************************
998 * ___lc_handle_func (MSVCRT.@)
1000 LCID
* CDECL
___lc_handle_func(void)
1002 return (LCID
*)get_locinfo()->lc_handle
;
1005 #if _MSVCR_VER >= 110
1006 /*********************************************************************
1007 * ___lc_locale_name_func (MSVCR110.@)
1009 wchar_t** CDECL
___lc_locale_name_func(void)
1011 return get_locinfo()->lc_name
;
1015 /*********************************************************************
1016 * ___lc_codepage_func (MSVCRT.@)
1018 unsigned int CDECL
___lc_codepage_func(void)
1020 return get_locinfo()->lc_codepage
;
1023 /*********************************************************************
1024 * ___lc_collate_cp_func (MSVCRT.@)
1026 int CDECL
___lc_collate_cp_func(void)
1028 return get_locinfo()->lc_collate_cp
;
1031 /* INTERNAL: frees pthreadlocinfo struct */
1032 void free_locinfo(pthreadlocinfo locinfo
)
1039 for(i
=LC_MIN
+1; i
<=LC_MAX
; i
++) {
1040 if(!locinfo
->lc_category
[i
].refcount
1041 || !InterlockedDecrement((LONG
*)locinfo
->lc_category
[i
].refcount
)) {
1042 free(locinfo
->lc_category
[i
].locale
);
1043 free(locinfo
->lc_category
[i
].refcount
);
1045 if(!locinfo
->lc_category
[i
].wrefcount
1046 || !InterlockedDecrement((LONG
*)locinfo
->lc_category
[i
].wrefcount
)) {
1047 #if _MSVCR_VER >= 110
1048 free(locinfo
->lc_name
[i
]);
1050 free(locinfo
->lc_category
[i
].wrefcount
);
1054 if(locinfo
->lconv_num_refcount
1055 && !InterlockedDecrement((LONG
*)locinfo
->lconv_num_refcount
)) {
1056 free(locinfo
->lconv
->decimal_point
);
1057 free(locinfo
->lconv
->thousands_sep
);
1058 free(locinfo
->lconv
->grouping
);
1059 #if _MSVCR_VER >= 100
1060 free(locinfo
->lconv
->_W_decimal_point
);
1061 free(locinfo
->lconv
->_W_thousands_sep
);
1063 free(locinfo
->lconv_num_refcount
);
1065 if(locinfo
->lconv_mon_refcount
1066 && !InterlockedDecrement((LONG
*)locinfo
->lconv_mon_refcount
)) {
1067 free(locinfo
->lconv
->int_curr_symbol
);
1068 free(locinfo
->lconv
->currency_symbol
);
1069 free(locinfo
->lconv
->mon_decimal_point
);
1070 free(locinfo
->lconv
->mon_thousands_sep
);
1071 free(locinfo
->lconv
->mon_grouping
);
1072 free(locinfo
->lconv
->positive_sign
);
1073 free(locinfo
->lconv
->negative_sign
);
1074 #if _MSVCR_VER >= 100
1075 free(locinfo
->lconv
->_W_int_curr_symbol
);
1076 free(locinfo
->lconv
->_W_currency_symbol
);
1077 free(locinfo
->lconv
->_W_mon_decimal_point
);
1078 free(locinfo
->lconv
->_W_mon_thousands_sep
);
1079 free(locinfo
->lconv
->_W_positive_sign
);
1080 free(locinfo
->lconv
->_W_negative_sign
);
1082 free(locinfo
->lconv_mon_refcount
);
1084 if(locinfo
->lconv_intl_refcount
1085 && !InterlockedDecrement((LONG
*)locinfo
->lconv_intl_refcount
)) {
1086 free(locinfo
->lconv_intl_refcount
);
1087 free(locinfo
->lconv
);
1090 if(locinfo
->ctype1_refcount
1091 && !InterlockedDecrement((LONG
*)locinfo
->ctype1_refcount
)) {
1092 free(locinfo
->ctype1_refcount
);
1093 free(locinfo
->ctype1
);
1094 free((void*)locinfo
->pclmap
);
1095 free((void*)locinfo
->pcumap
);
1098 if(locinfo
->lc_time_curr
&& !InterlockedDecrement(&locinfo
->lc_time_curr
->refcount
)
1099 && locinfo
->lc_time_curr
!= &cloc_time_data
)
1100 free(locinfo
->lc_time_curr
);
1102 if(InterlockedDecrement((LONG
*)&locinfo
->refcount
))
1108 /* INTERNAL: frees pthreadmbcinfo struct */
1109 void free_mbcinfo(pthreadmbcinfo mbcinfo
)
1114 if(InterlockedDecrement(&mbcinfo
->refcount
))
1120 _locale_t CDECL
get_current_locale_noalloc(_locale_t locale
)
1122 thread_data_t
*data
= msvcrt_get_thread_data();
1124 update_thread_locale(data
);
1125 locale
->locinfo
= data
->locinfo
;
1126 locale
->mbcinfo
= data
->mbcinfo
;
1128 grab_locinfo(locale
->locinfo
);
1129 InterlockedIncrement(&locale
->mbcinfo
->refcount
);
1133 void CDECL
free_locale_noalloc(_locale_t locale
)
1135 free_locinfo(locale
->locinfo
);
1136 free_mbcinfo(locale
->mbcinfo
);
1139 /*********************************************************************
1140 * _get_current_locale (MSVCRT.@)
1142 _locale_t CDECL
_get_current_locale(void)
1144 _locale_t loc
= malloc(sizeof(_locale_tstruct
));
1148 return get_current_locale_noalloc(loc
);
1151 /*********************************************************************
1152 * _free_locale (MSVCRT.@)
1154 void CDECL
_free_locale(_locale_t locale
)
1159 free_locale_noalloc(locale
);
1163 static inline BOOL
category_needs_update(int cat
,
1164 const threadlocinfo
*locinfo
, LCID lcid
, unsigned short cp
)
1166 if(!locinfo
) return TRUE
;
1167 return lcid
!=locinfo
->lc_handle
[cat
] || cp
!=locinfo
->lc_id
[cat
].wCodePage
;
1170 static __lc_time_data
* create_time_data(LCID lcid
)
1172 static const DWORD time_data
[] = {
1173 LOCALE_SABBREVDAYNAME7
, LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
,
1174 LOCALE_SABBREVDAYNAME3
, LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
,
1175 LOCALE_SABBREVDAYNAME6
,
1176 LOCALE_SDAYNAME7
, LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
1177 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
,
1178 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
1179 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
1180 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
1181 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
1182 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
, LOCALE_SMONTHNAME4
,
1183 LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
, LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
,
1184 LOCALE_SMONTHNAME9
, LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
1185 LOCALE_S1159
, LOCALE_S2359
,
1186 LOCALE_SSHORTDATE
, LOCALE_SLONGDATE
,
1190 __lc_time_data
*cur
;
1193 size
= sizeof(__lc_time_data
);
1194 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1195 ret
= GetLocaleInfoA(lcid
, time_data
[i
], NULL
, 0);
1200 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1201 ret
= GetLocaleInfoW(lcid
, time_data
[i
], NULL
, 0);
1204 size
+= ret
*sizeof(wchar_t);
1207 #if _MSVCR_VER >= 110
1208 size
+= LCIDToLocaleName(lcid
, NULL
, 0, 0)*sizeof(wchar_t);
1216 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1217 cur
->str
.str
[i
] = &cur
->data
[ret
];
1218 ret
+= GetLocaleInfoA(lcid
, time_data
[i
], &cur
->data
[ret
], size
-ret
);
1220 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1221 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1222 cur
->wstr
.wstr
[i
] = (wchar_t*)&cur
->data
[ret
];
1223 ret
+= GetLocaleInfoW(lcid
, time_data
[i
],
1224 (wchar_t*)&cur
->data
[ret
], size
-ret
)*sizeof(wchar_t);
1227 #if _MSVCR_VER >= 110
1228 cur
->locname
= (wchar_t*)&cur
->data
[ret
];
1229 LCIDToLocaleName(lcid
, (wchar_t*)&cur
->data
[ret
], (size
-ret
)/sizeof(wchar_t), 0);
1239 static pthreadlocinfo
create_locinfo(int category
,
1240 const char *locale
, const threadlocinfo
*old_locinfo
)
1242 static const char collate
[] = "COLLATE=";
1243 static const char ctype
[] = "CTYPE=";
1244 static const char monetary
[] = "MONETARY=";
1245 static const char numeric
[] = "NUMERIC=";
1246 static const char time
[] = "TIME=";
1248 pthreadlocinfo locinfo
;
1249 LCID lcid
[6] = { 0 };
1250 unsigned short cp
[6] = { 0 };
1251 const char *locale_name
[6] = { 0 };
1252 int val
, locale_len
[6] = { 0 };
1255 #if _MSVCR_VER >= 100
1260 TRACE("(%d %s)\n", category
, locale
);
1262 if(category
<LC_MIN
|| category
>LC_MAX
|| !locale
)
1265 if(locale
[0]=='C' && !locale
[1]) {
1268 } else if (locale
[0] == 'L' && locale
[1] == 'C' && locale
[2] == '_') {
1272 locale
+= 3; /* LC_ */
1273 if(!memcmp(locale
, collate
, sizeof(collate
)-1)) {
1275 locale
+= sizeof(collate
)-1;
1276 } else if(!memcmp(locale
, ctype
, sizeof(ctype
)-1)) {
1278 locale
+= sizeof(ctype
)-1;
1279 } else if(!memcmp(locale
, monetary
, sizeof(monetary
)-1)) {
1281 locale
+= sizeof(monetary
)-1;
1282 } else if(!memcmp(locale
, numeric
, sizeof(numeric
)-1)) {
1284 locale
+= sizeof(numeric
)-1;
1285 } else if(!memcmp(locale
, time
, sizeof(time
)-1)) {
1287 locale
+= sizeof(time
)-1;
1291 p
= strchr(locale
, ';');
1292 if(locale
[0]=='C' && (locale
[1]==';' || locale
[1]=='\0')) {
1296 memcpy(buf
, locale
, p
-locale
);
1297 buf
[p
-locale
] = '\0';
1298 lcid
[i
] = locale_to_LCID(buf
, &cp
[i
], &sname
);
1300 locale_name
[i
] = locale
;
1301 locale_len
[i
] = p
-locale
;
1304 lcid
[i
] = locale_to_LCID(locale
, &cp
[i
], &sname
);
1306 locale_name
[i
] = locale
;
1307 locale_len
[i
] = strlen(locale
);
1314 if(!p
|| *(p
+1)!='L' || *(p
+2)!='C' || *(p
+3)!='_')
1320 lcid
[0] = locale_to_LCID(locale
, &cp
[0], &sname
);
1324 locale_name
[0] = locale
;
1325 locale_len
[0] = strlen(locale
);
1328 for(i
=1; i
<6; i
++) {
1331 locale_name
[i
] = locale_name
[0];
1332 locale_len
[i
] = locale_len
[0];
1336 for(i
=1; i
<6; i
++) {
1337 #if _MSVCR_VER < 140
1338 if(i
==LC_CTYPE
&& cp
[i
]==CP_UTF8
) {
1339 locale_name
[i
] = NULL
;
1341 lcid
[i
] = old_locinfo
? old_locinfo
->lc_handle
[i
] : 0;
1342 cp
[i
] = old_locinfo
? old_locinfo
->lc_id
[i
].wCodePage
: 0;
1345 if(category
!=LC_ALL
&& category
!=i
) {
1347 lcid
[i
] = old_locinfo
->lc_handle
[i
];
1348 cp
[i
] = old_locinfo
->lc_id
[i
].wCodePage
;
1356 locinfo
= malloc(sizeof(threadlocinfo
));
1360 memset(locinfo
, 0, sizeof(threadlocinfo
));
1361 locinfo
->refcount
= 1;
1363 if(locale_name
[LC_COLLATE
] &&
1364 !init_category_name(locale_name
[LC_COLLATE
],
1365 locale_len
[LC_COLLATE
], locinfo
, LC_COLLATE
)) {
1366 free_locinfo(locinfo
);
1370 if(!category_needs_update(LC_COLLATE
, old_locinfo
,
1371 lcid
[LC_COLLATE
], cp
[LC_COLLATE
])) {
1372 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_COLLATE
);
1373 locinfo
->lc_collate_cp
= old_locinfo
->lc_collate_cp
;
1374 } else if(lcid
[LC_COLLATE
]) {
1375 if(!update_threadlocinfo_category(lcid
[LC_COLLATE
],
1376 cp
[LC_COLLATE
], locinfo
, LC_COLLATE
)) {
1377 free_locinfo(locinfo
);
1381 locinfo
->lc_collate_cp
= locinfo
->lc_id
[LC_COLLATE
].wCodePage
;
1383 if(!init_category_name("C", 1, locinfo
, LC_COLLATE
)) {
1384 free_locinfo(locinfo
);
1389 if(locale_name
[LC_CTYPE
] &&
1390 !init_category_name(locale_name
[LC_CTYPE
],
1391 locale_len
[LC_CTYPE
], locinfo
, LC_CTYPE
)) {
1392 free_locinfo(locinfo
);
1396 if(!category_needs_update(LC_CTYPE
, old_locinfo
,
1397 lcid
[LC_CTYPE
], cp
[LC_CTYPE
])) {
1398 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_CTYPE
);
1399 locinfo
->lc_codepage
= old_locinfo
->lc_codepage
;
1400 locinfo
->lc_clike
= old_locinfo
->lc_clike
;
1401 locinfo
->mb_cur_max
= old_locinfo
->mb_cur_max
;
1402 locinfo
->ctype1
= old_locinfo
->ctype1
;
1403 locinfo
->ctype1_refcount
= old_locinfo
->ctype1_refcount
;
1404 locinfo
->pctype
= old_locinfo
->pctype
;
1405 locinfo
->pclmap
= old_locinfo
->pclmap
;
1406 locinfo
->pcumap
= old_locinfo
->pcumap
;
1407 if(locinfo
->ctype1_refcount
)
1408 InterlockedIncrement((LONG
*)locinfo
->ctype1_refcount
);
1409 } else if(lcid
[LC_CTYPE
]) {
1413 if(!update_threadlocinfo_category(lcid
[LC_CTYPE
],
1414 cp
[LC_CTYPE
], locinfo
, LC_CTYPE
)) {
1415 free_locinfo(locinfo
);
1419 locinfo
->lc_codepage
= locinfo
->lc_id
[LC_CTYPE
].wCodePage
;
1420 locinfo
->lc_clike
= 1;
1421 if(!GetCPInfo(locinfo
->lc_codepage
, &cp_info
)) {
1422 free_locinfo(locinfo
);
1425 locinfo
->mb_cur_max
= cp_info
.MaxCharSize
;
1427 locinfo
->ctype1_refcount
= malloc(sizeof(int));
1428 if(!locinfo
->ctype1_refcount
) {
1429 free_locinfo(locinfo
);
1432 *locinfo
->ctype1_refcount
= 1;
1434 locinfo
->ctype1
= malloc(sizeof(short[257]));
1435 locinfo
->pclmap
= malloc(sizeof(char[256]));
1436 locinfo
->pcumap
= malloc(sizeof(char[256]));
1437 if(!locinfo
->ctype1
|| !locinfo
->pclmap
|| !locinfo
->pcumap
) {
1438 free_locinfo(locinfo
);
1442 locinfo
->ctype1
[0] = 0;
1443 locinfo
->pctype
= locinfo
->ctype1
+1;
1445 buf
[1] = buf
[2] = '\0';
1446 for(i
=1; i
<257; i
++) {
1449 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1450 locinfo
->ctype1
[i
] = 0;
1452 GetStringTypeA(lcid
[LC_CTYPE
], CT_CTYPE1
, buf
,
1453 1, locinfo
->ctype1
+i
);
1456 for(i
=0; cp_info
.LeadByte
[i
+1]!=0; i
+=2)
1457 for(j
=cp_info
.LeadByte
[i
]; j
<=cp_info
.LeadByte
[i
+1]; j
++)
1458 locinfo
->ctype1
[j
+1] |= _LEADBYTE
;
1460 for(i
=0; i
<256; i
++) {
1461 if(locinfo
->pctype
[i
] & _LEADBYTE
)
1467 LCMapStringA(lcid
[LC_CTYPE
], LCMAP_LOWERCASE
, buf
, 256,
1468 (char*)locinfo
->pclmap
, 256);
1469 LCMapStringA(lcid
[LC_CTYPE
], LCMAP_UPPERCASE
, buf
, 256,
1470 (char*)locinfo
->pcumap
, 256);
1472 locinfo
->lc_clike
= 1;
1473 locinfo
->mb_cur_max
= 1;
1474 locinfo
->pctype
= MSVCRT__ctype
+1;
1475 locinfo
->pclmap
= cloc_clmap
;
1476 locinfo
->pcumap
= cloc_cumap
;
1477 if(!init_category_name("C", 1, locinfo
, LC_CTYPE
)) {
1478 free_locinfo(locinfo
);
1483 if(!category_needs_update(LC_MONETARY
, old_locinfo
,
1484 lcid
[LC_MONETARY
], cp
[LC_MONETARY
]) &&
1485 !category_needs_update(LC_NUMERIC
, old_locinfo
,
1486 lcid
[LC_NUMERIC
], cp
[LC_NUMERIC
])) {
1487 locinfo
->lconv
= old_locinfo
->lconv
;
1488 locinfo
->lconv_intl_refcount
= old_locinfo
->lconv_intl_refcount
;
1489 if(locinfo
->lconv_intl_refcount
)
1490 InterlockedIncrement((LONG
*)locinfo
->lconv_intl_refcount
);
1491 } else if(lcid
[LC_MONETARY
] || lcid
[LC_NUMERIC
]) {
1492 locinfo
->lconv
= malloc(sizeof(struct lconv
));
1493 locinfo
->lconv_intl_refcount
= malloc(sizeof(int));
1494 if(!locinfo
->lconv
|| !locinfo
->lconv_intl_refcount
) {
1495 free(locinfo
->lconv
);
1496 free(locinfo
->lconv_intl_refcount
);
1497 locinfo
->lconv
= NULL
;
1498 locinfo
->lconv_intl_refcount
= NULL
;
1499 free_locinfo(locinfo
);
1502 memset(locinfo
->lconv
, 0, sizeof(struct lconv
));
1503 *locinfo
->lconv_intl_refcount
= 1;
1505 locinfo
->lconv
= &cloc_lconv
;
1508 if(locale_name
[LC_MONETARY
] &&
1509 !init_category_name(locale_name
[LC_MONETARY
],
1510 locale_len
[LC_MONETARY
], locinfo
, LC_MONETARY
)) {
1511 free_locinfo(locinfo
);
1515 if(!category_needs_update(LC_MONETARY
, old_locinfo
,
1516 lcid
[LC_MONETARY
], cp
[LC_MONETARY
])) {
1517 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_MONETARY
);
1518 locinfo
->lconv_mon_refcount
= old_locinfo
->lconv_mon_refcount
;
1519 if(locinfo
->lconv_mon_refcount
)
1520 InterlockedIncrement((LONG
*)locinfo
->lconv_mon_refcount
);
1521 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1522 locinfo
->lconv
->int_curr_symbol
= old_locinfo
->lconv
->int_curr_symbol
;
1523 locinfo
->lconv
->currency_symbol
= old_locinfo
->lconv
->currency_symbol
;
1524 locinfo
->lconv
->mon_decimal_point
= old_locinfo
->lconv
->mon_decimal_point
;
1525 locinfo
->lconv
->mon_thousands_sep
= old_locinfo
->lconv
->mon_thousands_sep
;
1526 locinfo
->lconv
->mon_grouping
= old_locinfo
->lconv
->mon_grouping
;
1527 locinfo
->lconv
->positive_sign
= old_locinfo
->lconv
->positive_sign
;
1528 locinfo
->lconv
->negative_sign
= old_locinfo
->lconv
->negative_sign
;
1529 locinfo
->lconv
->int_frac_digits
= old_locinfo
->lconv
->int_frac_digits
;
1530 locinfo
->lconv
->frac_digits
= old_locinfo
->lconv
->frac_digits
;
1531 locinfo
->lconv
->p_cs_precedes
= old_locinfo
->lconv
->p_cs_precedes
;
1532 locinfo
->lconv
->p_sep_by_space
= old_locinfo
->lconv
->p_sep_by_space
;
1533 locinfo
->lconv
->n_cs_precedes
= old_locinfo
->lconv
->n_cs_precedes
;
1534 locinfo
->lconv
->n_sep_by_space
= old_locinfo
->lconv
->n_sep_by_space
;
1535 locinfo
->lconv
->p_sign_posn
= old_locinfo
->lconv
->p_sign_posn
;
1536 locinfo
->lconv
->n_sign_posn
= old_locinfo
->lconv
->n_sign_posn
;
1537 #if _MSVCR_VER >= 100
1538 locinfo
->lconv
->_W_int_curr_symbol
= old_locinfo
->lconv
->_W_int_curr_symbol
;
1539 locinfo
->lconv
->_W_currency_symbol
= old_locinfo
->lconv
->_W_currency_symbol
;
1540 locinfo
->lconv
->_W_mon_decimal_point
= old_locinfo
->lconv
->_W_mon_decimal_point
;
1541 locinfo
->lconv
->_W_mon_thousands_sep
= old_locinfo
->lconv
->_W_mon_thousands_sep
;
1542 locinfo
->lconv
->_W_positive_sign
= old_locinfo
->lconv
->_W_positive_sign
;
1543 locinfo
->lconv
->_W_negative_sign
= old_locinfo
->lconv
->_W_negative_sign
;
1546 } else if(lcid
[LC_MONETARY
]) {
1547 if(!update_threadlocinfo_category(lcid
[LC_MONETARY
],
1548 cp
[LC_MONETARY
], locinfo
, LC_MONETARY
)) {
1549 free_locinfo(locinfo
);
1553 locinfo
->lconv_mon_refcount
= malloc(sizeof(int));
1554 if(!locinfo
->lconv_mon_refcount
) {
1555 free_locinfo(locinfo
);
1559 *locinfo
->lconv_mon_refcount
= 1;
1561 i
= GetLocaleInfoA(lcid
[LC_MONETARY
], LOCALE_SINTLSYMBOL
1562 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1563 if(i
&& (locinfo
->lconv
->int_curr_symbol
= malloc(i
)))
1564 memcpy(locinfo
->lconv
->int_curr_symbol
, buf
, i
);
1566 free_locinfo(locinfo
);
1570 i
= GetLocaleInfoA(lcid
[LC_MONETARY
], LOCALE_SCURRENCY
1571 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1572 if(i
&& (locinfo
->lconv
->currency_symbol
= malloc(i
)))
1573 memcpy(locinfo
->lconv
->currency_symbol
, buf
, i
);
1575 free_locinfo(locinfo
);
1579 i
= GetLocaleInfoA(lcid
[LC_MONETARY
], LOCALE_SMONDECIMALSEP
1580 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1581 if(i
&& (locinfo
->lconv
->mon_decimal_point
= malloc(i
)))
1582 memcpy(locinfo
->lconv
->mon_decimal_point
, buf
, i
);
1584 free_locinfo(locinfo
);
1588 i
= GetLocaleInfoA(lcid
[LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1589 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1590 if(i
&& (locinfo
->lconv
->mon_thousands_sep
= malloc(i
)))
1591 memcpy(locinfo
->lconv
->mon_thousands_sep
, buf
, i
);
1593 free_locinfo(locinfo
);
1597 i
= GetLocaleInfoA(lcid
[LC_MONETARY
], LOCALE_SMONGROUPING
1598 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1600 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1601 if(i
&& (locinfo
->lconv
->mon_grouping
= malloc(i
))) {
1602 for(i
=0; buf
[i
+1]==';'; i
+=2)
1603 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1604 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1606 locinfo
->lconv
->mon_grouping
[i
/2+1] = 127;
1608 free_locinfo(locinfo
);
1612 i
= GetLocaleInfoA(lcid
[LC_MONETARY
], LOCALE_SPOSITIVESIGN
1613 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1614 if(i
&& (locinfo
->lconv
->positive_sign
= malloc(i
)))
1615 memcpy(locinfo
->lconv
->positive_sign
, buf
, i
);
1617 free_locinfo(locinfo
);
1621 i
= GetLocaleInfoA(lcid
[LC_MONETARY
], LOCALE_SNEGATIVESIGN
1622 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1623 if(i
&& (locinfo
->lconv
->negative_sign
= malloc(i
)))
1624 memcpy(locinfo
->lconv
->negative_sign
, buf
, i
);
1626 free_locinfo(locinfo
);
1630 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_IINTLCURRDIGITS
1631 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1632 locinfo
->lconv
->int_frac_digits
= val
;
1634 free_locinfo(locinfo
);
1638 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_ICURRDIGITS
1639 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1640 locinfo
->lconv
->frac_digits
= val
;
1642 free_locinfo(locinfo
);
1646 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_IPOSSYMPRECEDES
1647 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1648 locinfo
->lconv
->p_cs_precedes
= val
;
1650 free_locinfo(locinfo
);
1654 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_IPOSSEPBYSPACE
1655 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1656 locinfo
->lconv
->p_sep_by_space
= val
;
1658 free_locinfo(locinfo
);
1662 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_INEGSYMPRECEDES
1663 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1664 locinfo
->lconv
->n_cs_precedes
= val
;
1666 free_locinfo(locinfo
);
1670 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_INEGSEPBYSPACE
1671 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1672 locinfo
->lconv
->n_sep_by_space
= val
;
1674 free_locinfo(locinfo
);
1678 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_IPOSSIGNPOSN
1679 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1680 locinfo
->lconv
->p_sign_posn
= val
;
1682 free_locinfo(locinfo
);
1686 if(GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_INEGSIGNPOSN
1687 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1688 locinfo
->lconv
->n_sign_posn
= val
;
1690 free_locinfo(locinfo
);
1694 #if _MSVCR_VER >= 100
1695 i
= GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_SINTLSYMBOL
1696 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1697 if(i
&& (locinfo
->lconv
->_W_int_curr_symbol
= malloc(i
* sizeof(wchar_t))))
1698 memcpy(locinfo
->lconv
->_W_int_curr_symbol
, wbuf
, i
* sizeof(wchar_t));
1700 free_locinfo(locinfo
);
1704 i
= GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_SCURRENCY
1705 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1706 if(i
&& (locinfo
->lconv
->_W_currency_symbol
= malloc(i
* sizeof(wchar_t))))
1707 memcpy(locinfo
->lconv
->_W_currency_symbol
, wbuf
, i
* sizeof(wchar_t));
1709 free_locinfo(locinfo
);
1713 i
= GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_SMONDECIMALSEP
1714 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1715 if(i
&& (locinfo
->lconv
->_W_mon_decimal_point
= malloc(i
* sizeof(wchar_t))))
1716 memcpy(locinfo
->lconv
->_W_mon_decimal_point
, wbuf
, i
* sizeof(wchar_t));
1718 free_locinfo(locinfo
);
1722 i
= GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1723 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1724 if(i
&& (locinfo
->lconv
->_W_mon_thousands_sep
= malloc(i
* sizeof(wchar_t))))
1725 memcpy(locinfo
->lconv
->_W_mon_thousands_sep
, wbuf
, i
* sizeof(wchar_t));
1727 free_locinfo(locinfo
);
1731 i
= GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_SPOSITIVESIGN
1732 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1733 if(i
&& (locinfo
->lconv
->_W_positive_sign
= malloc(i
* sizeof(wchar_t))))
1734 memcpy(locinfo
->lconv
->_W_positive_sign
, wbuf
, i
* sizeof(wchar_t));
1736 free_locinfo(locinfo
);
1740 i
= GetLocaleInfoW(lcid
[LC_MONETARY
], LOCALE_SNEGATIVESIGN
1741 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1742 if(i
&& (locinfo
->lconv
->_W_negative_sign
= malloc(i
* sizeof(wchar_t))))
1743 memcpy(locinfo
->lconv
->_W_negative_sign
, wbuf
, i
* sizeof(wchar_t));
1745 free_locinfo(locinfo
);
1750 if (locinfo
->lconv
!= &cloc_lconv
) {
1751 locinfo
->lconv
->int_curr_symbol
= cloc_lconv
.int_curr_symbol
;
1752 locinfo
->lconv
->currency_symbol
= cloc_lconv
.currency_symbol
;
1753 locinfo
->lconv
->mon_decimal_point
= cloc_lconv
.mon_decimal_point
;
1754 locinfo
->lconv
->mon_thousands_sep
= cloc_lconv
.mon_thousands_sep
;
1755 locinfo
->lconv
->mon_grouping
= cloc_lconv
.mon_grouping
;
1756 locinfo
->lconv
->positive_sign
= cloc_lconv
.positive_sign
;
1757 locinfo
->lconv
->negative_sign
= cloc_lconv
.negative_sign
;
1758 locinfo
->lconv
->int_frac_digits
= cloc_lconv
.int_frac_digits
;
1759 locinfo
->lconv
->frac_digits
= cloc_lconv
.frac_digits
;
1760 locinfo
->lconv
->p_cs_precedes
= cloc_lconv
.p_cs_precedes
;
1761 locinfo
->lconv
->p_sep_by_space
= cloc_lconv
.p_sep_by_space
;
1762 locinfo
->lconv
->n_cs_precedes
= cloc_lconv
.n_cs_precedes
;
1763 locinfo
->lconv
->n_sep_by_space
= cloc_lconv
.n_sep_by_space
;
1764 locinfo
->lconv
->p_sign_posn
= cloc_lconv
.p_sign_posn
;
1765 locinfo
->lconv
->n_sign_posn
= cloc_lconv
.n_sign_posn
;
1767 #if _MSVCR_VER >= 100
1768 locinfo
->lconv
->_W_int_curr_symbol
= cloc_lconv
._W_int_curr_symbol
;
1769 locinfo
->lconv
->_W_currency_symbol
= cloc_lconv
._W_currency_symbol
;
1770 locinfo
->lconv
->_W_mon_decimal_point
= cloc_lconv
._W_mon_decimal_point
;
1771 locinfo
->lconv
->_W_mon_thousands_sep
= cloc_lconv
._W_mon_thousands_sep
;
1772 locinfo
->lconv
->_W_positive_sign
= cloc_lconv
._W_positive_sign
;
1773 locinfo
->lconv
->_W_negative_sign
= cloc_lconv
._W_negative_sign
;
1777 if(!init_category_name("C", 1, locinfo
, LC_MONETARY
)) {
1778 free_locinfo(locinfo
);
1783 if(locale_name
[LC_NUMERIC
] &&
1784 !init_category_name(locale_name
[LC_NUMERIC
],
1785 locale_len
[LC_NUMERIC
], locinfo
, LC_NUMERIC
)) {
1786 free_locinfo(locinfo
);
1790 if(!category_needs_update(LC_NUMERIC
, old_locinfo
,
1791 lcid
[LC_NUMERIC
], cp
[LC_NUMERIC
])) {
1792 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_NUMERIC
);
1793 locinfo
->lconv_num_refcount
= old_locinfo
->lconv_num_refcount
;
1794 if(locinfo
->lconv_num_refcount
)
1795 InterlockedIncrement((LONG
*)locinfo
->lconv_num_refcount
);
1796 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1797 locinfo
->lconv
->decimal_point
= old_locinfo
->lconv
->decimal_point
;
1798 locinfo
->lconv
->thousands_sep
= old_locinfo
->lconv
->thousands_sep
;
1799 locinfo
->lconv
->grouping
= old_locinfo
->lconv
->grouping
;
1800 #if _MSVCR_VER >= 100
1801 locinfo
->lconv
->_W_decimal_point
= old_locinfo
->lconv
->_W_decimal_point
;
1802 locinfo
->lconv
->_W_thousands_sep
= old_locinfo
->lconv
->_W_thousands_sep
;
1805 } else if(lcid
[LC_NUMERIC
]) {
1806 if(!update_threadlocinfo_category(lcid
[LC_NUMERIC
],
1807 cp
[LC_NUMERIC
], locinfo
, LC_NUMERIC
)) {
1808 free_locinfo(locinfo
);
1812 locinfo
->lconv_num_refcount
= malloc(sizeof(int));
1813 if(!locinfo
->lconv_num_refcount
) {
1814 free_locinfo(locinfo
);
1818 *locinfo
->lconv_num_refcount
= 1;
1820 i
= GetLocaleInfoA(lcid
[LC_NUMERIC
], LOCALE_SDECIMAL
1821 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1822 if(i
&& (locinfo
->lconv
->decimal_point
= malloc(i
)))
1823 memcpy(locinfo
->lconv
->decimal_point
, buf
, i
);
1825 free_locinfo(locinfo
);
1829 i
= GetLocaleInfoA(lcid
[LC_NUMERIC
], LOCALE_STHOUSAND
1830 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1831 if(i
&& (locinfo
->lconv
->thousands_sep
= malloc(i
)))
1832 memcpy(locinfo
->lconv
->thousands_sep
, buf
, i
);
1834 free_locinfo(locinfo
);
1838 i
= GetLocaleInfoA(lcid
[LC_NUMERIC
], LOCALE_SGROUPING
1839 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1841 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1842 if(i
&& (locinfo
->lconv
->grouping
= malloc(i
))) {
1843 for(i
=0; buf
[i
+1]==';'; i
+=2)
1844 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1845 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1847 locinfo
->lconv
->grouping
[i
/2+1] = 127;
1849 free_locinfo(locinfo
);
1853 #if _MSVCR_VER >= 100
1854 i
= GetLocaleInfoW(lcid
[LC_NUMERIC
], LOCALE_SDECIMAL
1855 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1856 if(i
&& (locinfo
->lconv
->_W_decimal_point
= malloc(i
* sizeof(wchar_t))))
1857 memcpy(locinfo
->lconv
->_W_decimal_point
, wbuf
, i
* sizeof(wchar_t));
1859 free_locinfo(locinfo
);
1863 i
= GetLocaleInfoW(lcid
[LC_NUMERIC
], LOCALE_STHOUSAND
1864 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1865 if(i
&& (locinfo
->lconv
->_W_thousands_sep
= malloc(i
* sizeof(wchar_t))))
1866 memcpy(locinfo
->lconv
->_W_thousands_sep
, wbuf
, i
* sizeof(wchar_t));
1868 free_locinfo(locinfo
);
1873 if (locinfo
->lconv
!= &cloc_lconv
) {
1874 locinfo
->lconv
->decimal_point
= cloc_lconv
.decimal_point
;
1875 locinfo
->lconv
->thousands_sep
= cloc_lconv
.thousands_sep
;
1876 locinfo
->lconv
->grouping
= cloc_lconv
.grouping
;
1878 #if _MSVCR_VER >= 100
1879 locinfo
->lconv
->_W_decimal_point
= cloc_lconv
._W_decimal_point
;
1880 locinfo
->lconv
->_W_thousands_sep
= cloc_lconv
._W_thousands_sep
;
1884 if (!init_category_name("C", 1, locinfo
, LC_NUMERIC
)) {
1885 free_locinfo(locinfo
);
1890 if(locale_name
[LC_TIME
] &&
1891 !init_category_name(locale_name
[LC_TIME
],
1892 locale_len
[LC_TIME
], locinfo
, LC_TIME
)) {
1893 free_locinfo(locinfo
);
1897 if(!category_needs_update(LC_TIME
, old_locinfo
,
1898 lcid
[LC_TIME
], cp
[LC_TIME
])) {
1899 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_TIME
);
1900 locinfo
->lc_time_curr
= old_locinfo
->lc_time_curr
;
1901 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
1902 } else if(lcid
[LC_TIME
]) {
1903 if(!update_threadlocinfo_category(lcid
[LC_TIME
],
1904 cp
[LC_TIME
], locinfo
, LC_TIME
)) {
1905 free_locinfo(locinfo
);
1909 locinfo
->lc_time_curr
= create_time_data(lcid
[LC_TIME
]);
1910 if(!locinfo
->lc_time_curr
) {
1911 free_locinfo(locinfo
);
1915 if(!init_category_name("C", 1, locinfo
, LC_TIME
)) {
1916 free_locinfo(locinfo
);
1919 locinfo
->lc_time_curr
= &cloc_time_data
;
1920 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
1926 /*********************************************************************
1927 * _create_locale (MSVCRT.@)
1929 _locale_t CDECL
_create_locale(int category
, const char *locale
)
1933 loc
= malloc(sizeof(_locale_tstruct
));
1937 loc
->locinfo
= create_locinfo(category
, locale
, NULL
);
1943 loc
->mbcinfo
= create_mbcinfo(loc
->locinfo
->lc_id
[LC_CTYPE
].wCodePage
,
1944 loc
->locinfo
->lc_handle
[LC_CTYPE
], NULL
);
1946 free_locinfo(loc
->locinfo
);
1953 #if _MSVCR_VER >= 110
1954 /*********************************************************************
1955 * _wcreate_locale (MSVCR110.@)
1957 _locale_t CDECL
_wcreate_locale(int category
, const wchar_t *locale
)
1963 if(category
<LC_MIN
|| category
>LC_MAX
|| !locale
)
1966 len
= wcstombs(NULL
, locale
, 0);
1969 if(!(str
= malloc(++len
)))
1971 wcstombs(str
, locale
, len
);
1973 loc
= _create_locale(category
, str
);
1980 /*********************************************************************
1981 * setlocale (MSVCRT.@)
1983 char* CDECL
setlocale(int category
, const char* locale
)
1985 thread_data_t
*data
= msvcrt_get_thread_data();
1986 pthreadlocinfo locinfo
= get_locinfo(), newlocinfo
;
1988 if(category
<LC_MIN
|| category
>LC_MAX
)
1992 if(category
== LC_ALL
)
1993 return construct_lc_all(locinfo
);
1995 return locinfo
->lc_category
[category
].locale
;
1998 newlocinfo
= create_locinfo(category
, locale
, locinfo
);
2000 WARN("%d %s failed\n", category
, locale
);
2004 if(locale
[0] != 'C' || locale
[1] != '\0')
2005 initial_locale
= FALSE
;
2007 if(data
->locale_flags
& LOCALE_THREAD
)
2009 if(data
->locale_flags
& LOCALE_FREE
)
2010 free_locinfo(data
->locinfo
);
2011 data
->locinfo
= newlocinfo
;
2018 free_locinfo(MSVCRT_locale
->locinfo
);
2019 MSVCRT_locale
->locinfo
= newlocinfo
;
2021 MSVCRT___lc_codepage
= newlocinfo
->lc_codepage
;
2022 MSVCRT___lc_collate_cp
= newlocinfo
->lc_collate_cp
;
2023 MSVCRT___mb_cur_max
= newlocinfo
->mb_cur_max
;
2024 MSVCRT__pctype
= newlocinfo
->pctype
;
2025 for(i
=LC_MIN
; i
<=LC_MAX
; i
++)
2026 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2028 update_thread_locale(data
);
2031 if(category
== LC_ALL
)
2032 return construct_lc_all(data
->locinfo
);
2034 return data
->locinfo
->lc_category
[category
].locale
;
2037 /*********************************************************************
2038 * _wsetlocale (MSVCRT.@)
2040 wchar_t* CDECL
_wsetlocale(int category
, const wchar_t* wlocale
)
2042 static wchar_t current_lc_all
[MAX_LOCALE_LENGTH
];
2044 char *locale
= NULL
;
2049 len
= wcstombs(NULL
, wlocale
, 0);
2053 locale
= malloc(++len
);
2057 wcstombs(locale
, wlocale
, len
);
2061 ret
= setlocale(category
, locale
);
2064 if(ret
&& mbstowcs(current_lc_all
, ret
, MAX_LOCALE_LENGTH
)==-1)
2068 return ret
? current_lc_all
: NULL
;
2071 #if _MSVCR_VER >= 80
2072 /*********************************************************************
2073 * _configthreadlocale (MSVCR80.@)
2075 int CDECL
_configthreadlocale(int type
)
2077 thread_data_t
*data
= msvcrt_get_thread_data();
2080 ret
= (data
->locale_flags
& LOCALE_THREAD
? _ENABLE_PER_THREAD_LOCALE
:
2081 _DISABLE_PER_THREAD_LOCALE
);
2083 if(type
== _ENABLE_PER_THREAD_LOCALE
)
2084 data
->locale_flags
|= LOCALE_THREAD
;
2085 else if(type
== _DISABLE_PER_THREAD_LOCALE
)
2086 data
->locale_flags
&= ~LOCALE_THREAD
;
2094 BOOL
msvcrt_init_locale(void)
2099 MSVCRT_locale
= _create_locale(0, "C");
2104 MSVCRT___lc_codepage
= MSVCRT_locale
->locinfo
->lc_codepage
;
2105 MSVCRT___lc_collate_cp
= MSVCRT_locale
->locinfo
->lc_collate_cp
;
2106 MSVCRT___mb_cur_max
= MSVCRT_locale
->locinfo
->mb_cur_max
;
2107 MSVCRT__pctype
= MSVCRT_locale
->locinfo
->pctype
;
2108 for(i
=LC_MIN
; i
<=LC_MAX
; i
++)
2109 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2110 _setmbcp(_MB_CP_ANSI
);
2114 #if _MSVCR_VER >= 120
2115 /*********************************************************************
2116 * wctrans (MSVCR120.@)
2118 wctrans_t CDECL
wctrans(const char *property
)
2120 static const char str_tolower
[] = "tolower";
2121 static const char str_toupper
[] = "toupper";
2123 if(!strcmp(property
, str_tolower
))
2125 if(!strcmp(property
, str_toupper
))