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
55 #define LCID_CONVERSION_FLAGS LOCALE_ALLOW_NEUTRAL_NAMES
57 #define LCID_CONVERSION_FLAGS 0
60 __lc_time_data cloc_time_data
=
62 {{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
63 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
64 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
65 "January", "February", "March", "April", "May", "June", "July",
66 "August", "September", "October", "November", "December",
67 "AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"}},
69 MAKELCID(LANG_ENGLISH
, SORT_DEFAULT
),
72 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
73 {{L
"Sun", L
"Mon", L
"Tue", L
"Wed", L
"Thu", L
"Fri", L
"Sat",
74 L
"Sunday", L
"Monday", L
"Tuesday", L
"Wednesday", L
"Thursday", L
"Friday", L
"Saturday",
75 L
"Jan", L
"Feb", L
"Mar", L
"Apr", L
"May", L
"Jun", L
"Jul", L
"Aug", L
"Sep", L
"Oct", L
"Nov", L
"Dec",
76 L
"January", L
"February", L
"March", L
"April", L
"May", L
"June", L
"July",
77 L
"August", L
"September", L
"October", L
"November", L
"December",
78 L
"AM", L
"PM", L
"MM/dd/yy", L
"dddd, MMMM dd, yyyy", L
"HH:mm:ss"}},
85 static const unsigned char cloc_clmap
[256] =
87 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
88 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
89 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
90 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
91 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
92 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
93 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
94 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
95 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
96 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
97 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
98 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
99 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
100 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
101 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
102 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
103 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
104 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
105 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
106 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
107 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
108 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
109 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
110 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
111 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
112 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
113 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
114 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
115 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
116 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
117 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
118 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
121 static const unsigned char cloc_cumap
[256] =
123 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
124 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
125 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
126 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
127 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
128 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
129 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
130 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
131 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
132 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
133 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
134 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
135 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
136 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
137 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
138 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
139 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
140 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
141 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
142 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
143 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
144 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
145 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
146 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
147 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
148 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
149 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
150 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
151 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
152 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
153 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
154 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
157 static char empty
[] = "";
158 static char cloc_dec_point
[] = ".";
159 #if _MSVCR_VER >= 100
160 static wchar_t emptyW
[] = L
"";
161 static wchar_t cloc_dec_pointW
[] = L
".";
163 static struct lconv cloc_lconv
=
165 cloc_dec_point
, empty
, empty
, empty
, empty
, empty
, empty
, empty
, empty
, empty
,
166 CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
,
167 #if _MSVCR_VER >= 100
168 cloc_dec_pointW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
172 /* Friendly country strings & language names abbreviations. */
173 static const char * const _country_synonyms
[] =
176 "american english", "en-US",
177 "american-english", "en-US",
178 "english-american", "en-US",
179 "english-us", "en-US",
180 "english-usa", "en-US",
183 "australian", "en-AU",
184 "english-aus", "en-AU",
186 "french-belgian", "fr-BE",
188 "english-can", "en-CA",
189 "french-canadian", "fr-CA",
190 #if _MSVCR_VER >= 110
192 "chinese-simplified", "zh",
193 "chinese-traditional", "zh-HK",
198 "chinese-simplified", "zh-CN",
199 "chinese-traditional", "zh-TW",
203 "dutch-belgian", "nl-BE",
204 "english-nz", "en-NZ",
206 "english-uk", "en-GB",
207 "french-swiss", "fr-CH",
209 "german-swiss", "de-CH",
210 "italian-swiss", "it-CH",
211 "german-austrian", "de-AT",
212 "portuguese", "pt-BR",
213 "portuguese-brazil", "pt-BR",
214 "spanish-mexican", "es-MX",
215 "norwegian-bokmal", "nb",
216 "norwegian-nynorsk", "nn-NO",
217 "spanish-modern", "es-ES"
221 /* INTERNAL: Map a synonym to an ISO code */
222 static BOOL
remap_synonym(char *name
)
225 for (i
= 0; i
< ARRAY_SIZE(_country_synonyms
); i
+= 2)
227 if (!_stricmp(_country_synonyms
[i
],name
))
229 TRACE(":Mapping synonym %s to %s\n",name
,_country_synonyms
[i
+1]);
230 strcpy(name
, _country_synonyms
[i
+1]);
238 /* Note: Flags are weighted in order of matching importance */
239 #define FOUND_SNAME 0x4
240 #define FOUND_LANGUAGE 0x2
241 #define FOUND_COUNTRY 0x1
244 WCHAR search_language
[MAX_ELEM_LEN
];
245 WCHAR search_country
[MAX_ELEM_LEN
];
246 WCHAR found_lang_sname
[LOCALE_NAME_MAX_LENGTH
];
247 unsigned int match_flags
;
251 #define CONTINUE_LOOKING TRUE
252 #define STOP_LOOKING FALSE
254 /* INTERNAL: Get and compare locale info with a given string */
255 static int compare_info(WCHAR
*name
, DWORD flags
, WCHAR
*buff
, const WCHAR
*cmp
, BOOL exact
)
263 GetLocaleInfoEx(name
, flags
|LOCALE_NOUSEROVERRIDE
, buff
, MAX_ELEM_LEN
);
267 /* Partial matches are only allowed on language/country names */
271 return !_wcsicmp(cmp
, buff
);
273 return !_wcsnicmp(cmp
, buff
, len
);
277 find_best_locale_proc( WCHAR
*name
, DWORD locale_flags
, LPARAM lParam
)
279 locale_search_t
*res
= (locale_search_t
*)lParam
;
280 WCHAR buff
[MAX_ELEM_LEN
];
281 unsigned int flags
= 0;
283 if (res
->allow_sname
&& compare_info(name
,LOCALE_SNAME
,buff
,res
->search_language
, TRUE
))
285 TRACE(":Found locale: %s->%s\n", wine_dbgstr_w(res
->search_language
), wine_dbgstr_w(buff
));
286 res
->match_flags
= FOUND_SNAME
;
287 wcscpy(res
->found_lang_sname
, name
);
292 if (compare_info(name
,LOCALE_SISO639LANGNAME
,buff
,res
->search_language
, TRUE
) ||
293 compare_info(name
,LOCALE_SABBREVLANGNAME
,buff
,res
->search_language
, TRUE
) ||
294 compare_info(name
,LOCALE_SENGLANGUAGE
,buff
,res
->search_language
, FALSE
))
296 TRACE(":Found language: %s->%s\n", wine_dbgstr_w(res
->search_language
), wine_dbgstr_w(buff
));
297 flags
|= FOUND_LANGUAGE
;
299 else if (res
->match_flags
& FOUND_LANGUAGE
)
301 return CONTINUE_LOOKING
;
305 if (compare_info(name
,LOCALE_SISO3166CTRYNAME
,buff
,res
->search_country
, TRUE
) ||
306 compare_info(name
,LOCALE_SABBREVCTRYNAME
,buff
,res
->search_country
, TRUE
) ||
307 compare_info(name
,LOCALE_SENGCOUNTRY
,buff
,res
->search_country
, FALSE
))
309 TRACE("Found country:%s->%s\n", wine_dbgstr_w(res
->search_country
), wine_dbgstr_w(buff
));
310 flags
|= FOUND_COUNTRY
;
312 else if (!flags
&& (res
->match_flags
& FOUND_COUNTRY
))
314 return CONTINUE_LOOKING
;
317 if (flags
> res
->match_flags
)
319 /* Found a better match than previously */
320 res
->match_flags
= flags
;
321 wcscpy(res
->found_lang_sname
, name
);
323 if ((flags
& (FOUND_LANGUAGE
| FOUND_COUNTRY
)) ==
324 (FOUND_LANGUAGE
| FOUND_COUNTRY
))
326 TRACE(":found exact locale match\n");
329 return CONTINUE_LOOKING
;
332 /* Internal: Find the sname for a locale specification.
333 * sname must be at least LOCALE_NAME_MAX_LENGTH characters long
335 BOOL
locale_to_sname(const char *locale
, unsigned short *codepage
, BOOL
*sname_match
, WCHAR
*sname
)
337 thread_data_t
*data
= msvcrt_get_thread_data();
338 const char *cp
, *region
;
339 BOOL is_sname
= FALSE
;
342 if (!strcmp(locale
, data
->cached_locale
)) {
344 *codepage
= data
->cached_cp
;
346 *sname_match
= data
->cached_sname_match
;
347 wcscpy(sname
, data
->cached_sname
);
351 cp
= strchr(locale
, '.');
352 region
= strchr(locale
, '_');
354 if(!locale
[0] || (cp
== locale
&& !region
)) {
355 GetUserDefaultLocaleName(sname
, LOCALE_NAME_MAX_LENGTH
);
357 char search_language_buf
[MAX_ELEM_LEN
] = { 0 }, search_country_buf
[MAX_ELEM_LEN
] = { 0 };
358 locale_search_t search
;
359 BOOL remapped
= FALSE
;
361 memset(&search
, 0, sizeof(locale_search_t
));
362 lstrcpynA(search_language_buf
, locale
, MAX_ELEM_LEN
);
364 lstrcpynA(search_country_buf
, region
+1, MAX_ELEM_LEN
);
365 if(region
-locale
< MAX_ELEM_LEN
)
366 search_language_buf
[region
-locale
] = '\0';
368 search_country_buf
[0] = '\0';
371 if(region
&& cp
-region
-1<MAX_ELEM_LEN
)
372 search_country_buf
[cp
-region
-1] = '\0';
373 if(cp
-locale
< MAX_ELEM_LEN
)
374 search_language_buf
[cp
-locale
] = '\0';
377 if ((remapped
= remap_synonym(search_language_buf
)))
379 search
.allow_sname
= TRUE
;
382 #if _MSVCR_VER >= 110
385 search
.allow_sname
= TRUE
;
389 MultiByteToWideChar(CP_ACP
, 0, search_language_buf
, -1, search
.search_language
, MAX_ELEM_LEN
);
390 if (search
.allow_sname
&& IsValidLocaleName(search
.search_language
))
392 search
.match_flags
= FOUND_SNAME
;
393 wcscpy(sname
, search
.search_language
);
397 MultiByteToWideChar(CP_ACP
, 0, search_country_buf
, -1, search
.search_country
, MAX_ELEM_LEN
);
398 EnumSystemLocalesEx( find_best_locale_proc
, 0, (LPARAM
)&search
, NULL
);
400 if (!search
.match_flags
)
403 /* If we were given something that didn't match, fail */
404 if (search
.search_language
[0] && !(search
.match_flags
& (FOUND_SNAME
| FOUND_LANGUAGE
)))
406 if (search
.search_country
[0] && !(search
.match_flags
& FOUND_COUNTRY
))
409 wcscpy(sname
, search
.found_lang_sname
);
412 is_sname
= !remapped
&& (search
.match_flags
& FOUND_SNAME
) != 0;
415 /* Obtain code page */
416 if (!cp
|| !cp
[1] || !_strnicmp(cp
, ".ACP", 4)) {
417 GetLocaleInfoEx(sname
, LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
418 (WCHAR
*)&locale_cp
, sizeof(DWORD
)/sizeof(WCHAR
));
420 locale_cp
= GetACP();
421 } else if (!_strnicmp(cp
, ".OCP", 4)) {
422 GetLocaleInfoEx(sname
, LOCALE_IDEFAULTCODEPAGE
| LOCALE_RETURN_NUMBER
,
423 (WCHAR
*)&locale_cp
, sizeof(DWORD
)/sizeof(WCHAR
));
424 #if _MSVCR_VER >= 140
425 } else if (!_strnicmp(cp
, ".UTF-8", 6)
426 || !_strnicmp(cp
, ".UTF8", 5)) {
430 locale_cp
= atoi(cp
+ 1);
432 if (!IsValidCodePage(locale_cp
))
439 *codepage
= locale_cp
;
441 *sname_match
= is_sname
;
443 if (strlen(locale
) < sizeof(data
->cached_locale
)) {
444 strcpy(data
->cached_locale
, locale
);
445 data
->cached_cp
= locale_cp
;
446 data
->cached_sname_match
= is_sname
;
447 wcscpy(data
->cached_sname
, sname
);
453 static void copy_threadlocinfo_category(pthreadlocinfo locinfo
,
454 const threadlocinfo
*old_locinfo
, int category
)
456 locinfo
->lc_handle
[category
] = old_locinfo
->lc_handle
[category
];
457 locinfo
->lc_id
[category
] = old_locinfo
->lc_id
[category
];
458 if(!locinfo
->lc_category
[category
].locale
) {
459 locinfo
->lc_category
[category
].locale
= old_locinfo
->lc_category
[category
].locale
;
460 locinfo
->lc_category
[category
].refcount
= old_locinfo
->lc_category
[category
].refcount
;
461 InterlockedIncrement((LONG
*)locinfo
->lc_category
[category
].refcount
);
463 #if _MSVCR_VER >= 110
464 locinfo
->lc_name
[category
] = old_locinfo
->lc_name
[category
];
465 locinfo
->lc_category
[category
].wrefcount
= old_locinfo
->lc_category
[category
].wrefcount
;
466 if(locinfo
->lc_category
[category
].wrefcount
)
467 InterlockedIncrement((LONG
*)locinfo
->lc_category
[category
].wrefcount
);
471 static BOOL
init_category_name(const char *name
, int len
,
472 pthreadlocinfo locinfo
, int category
)
474 locinfo
->lc_category
[category
].locale
= malloc(len
+1);
475 locinfo
->lc_category
[category
].refcount
= malloc(sizeof(int));
476 if(!locinfo
->lc_category
[category
].locale
477 || !locinfo
->lc_category
[category
].refcount
) {
478 free(locinfo
->lc_category
[category
].locale
);
479 free(locinfo
->lc_category
[category
].refcount
);
480 locinfo
->lc_category
[category
].locale
= NULL
;
481 locinfo
->lc_category
[category
].refcount
= NULL
;
485 memcpy(locinfo
->lc_category
[category
].locale
, name
, len
);
486 locinfo
->lc_category
[category
].locale
[len
] = 0;
487 *locinfo
->lc_category
[category
].refcount
= 1;
491 #if _MSVCR_VER >= 110
492 static inline BOOL
set_lc_locale_name(pthreadlocinfo locinfo
, int cat
, WCHAR
*sname
)
494 locinfo
->lc_category
[cat
].wrefcount
= malloc(sizeof(int));
495 if(!locinfo
->lc_category
[cat
].wrefcount
)
497 *locinfo
->lc_category
[cat
].wrefcount
= 1;
499 if(!(locinfo
->lc_name
[cat
] = wcsdup(sname
)))
505 static inline BOOL
set_lc_locale_name(pthreadlocinfo locinfo
, int cat
, WCHAR
*sname
)
511 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
512 static BOOL
update_threadlocinfo_category(WCHAR
*sname
, unsigned short cp
,
513 pthreadlocinfo locinfo
, int category
)
517 if(GetLocaleInfoEx(sname
, LOCALE_ILANGUAGE
|LOCALE_NOUSEROVERRIDE
, wbuf
, ARRAY_SIZE(wbuf
))) {
520 locinfo
->lc_id
[category
].wLanguage
= 0;
522 locinfo
->lc_id
[category
].wLanguage
*= 16;
525 locinfo
->lc_id
[category
].wLanguage
+= *p
-'0';
527 locinfo
->lc_id
[category
].wLanguage
+= *p
-'a'+10;
532 locinfo
->lc_id
[category
].wCountry
=
533 locinfo
->lc_id
[category
].wLanguage
;
536 locinfo
->lc_id
[category
].wCodePage
= cp
;
538 locinfo
->lc_handle
[category
] = LocaleNameToLCID(sname
, LCID_CONVERSION_FLAGS
);
540 set_lc_locale_name(locinfo
, category
, sname
);
542 if(!locinfo
->lc_category
[category
].locale
) {
547 if (LANGIDFROMLCID(locinfo
->lc_handle
[category
]) == MAKELANGID(LANG_NORWEGIAN
, SUBLANG_NORWEGIAN_NYNORSK
))
549 /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */
550 wcscpy( wbuf
, L
"Norwegian-Nynorsk" );
551 len
= wcslen( wbuf
) + 1;
555 len
+= GetLocaleInfoEx(sname
, LOCALE_SENGLANGUAGE
|LOCALE_NOUSEROVERRIDE
, wbuf
, ARRAY_SIZE(wbuf
));
557 len
+= GetLocaleInfoEx(sname
, LOCALE_SENGCOUNTRY
558 |LOCALE_NOUSEROVERRIDE
, &wbuf
[len
], ARRAY_SIZE(wbuf
) - len
);
560 swprintf(wbuf
+len
, ARRAY_SIZE(wbuf
) - len
,L
"%d", cp
);
561 len
+= wcslen(wbuf
+len
);
563 WideCharToMultiByte(cp
, 0, wbuf
, -1, buf
, ARRAY_SIZE(buf
), NULL
, NULL
);
565 return init_category_name(buf
, len
, locinfo
, category
);
571 /*********************************************************************
572 * _lock_locales (UCRTBASE.@)
574 void CDECL
_lock_locales(void)
576 _lock(_SETLOCALE_LOCK
);
579 /*********************************************************************
580 * _unlock_locales (UCRTBASE.@)
582 void CDECL
_unlock_locales(void)
584 _unlock(_SETLOCALE_LOCK
);
587 static void grab_locinfo(pthreadlocinfo locinfo
)
591 InterlockedIncrement((LONG
*)&locinfo
->refcount
);
592 for(i
=LC_MIN
+1; i
<=LC_MAX
; i
++)
594 InterlockedIncrement((LONG
*)locinfo
->lc_category
[i
].refcount
);
595 if(locinfo
->lc_category
[i
].wrefcount
)
596 InterlockedIncrement((LONG
*)locinfo
->lc_category
[i
].wrefcount
);
598 if(locinfo
->lconv_intl_refcount
)
599 InterlockedIncrement((LONG
*)locinfo
->lconv_intl_refcount
);
600 if(locinfo
->lconv_num_refcount
)
601 InterlockedIncrement((LONG
*)locinfo
->lconv_num_refcount
);
602 if(locinfo
->lconv_mon_refcount
)
603 InterlockedIncrement((LONG
*)locinfo
->lconv_mon_refcount
);
604 if(locinfo
->ctype1_refcount
)
605 InterlockedIncrement((LONG
*)locinfo
->ctype1_refcount
);
606 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
609 static void update_thread_locale(thread_data_t
*data
)
611 if((data
->locale_flags
& LOCALE_FREE
) && ((data
->locale_flags
& LOCALE_THREAD
) ||
612 (data
->locinfo
== MSVCRT_locale
->locinfo
&& data
->mbcinfo
== MSVCRT_locale
->mbcinfo
)))
615 if(data
->locale_flags
& LOCALE_FREE
)
617 free_locinfo(data
->locinfo
);
618 free_mbcinfo(data
->mbcinfo
);
622 data
->locinfo
= MSVCRT_locale
->locinfo
;
623 grab_locinfo(data
->locinfo
);
627 data
->mbcinfo
= MSVCRT_locale
->mbcinfo
;
628 InterlockedIncrement(&data
->mbcinfo
->refcount
);
629 _unlock(_MB_CP_LOCK
);
631 data
->locale_flags
|= LOCALE_FREE
;
634 /* INTERNAL: returns threadlocinfo struct */
635 pthreadlocinfo CDECL
get_locinfo(void) {
636 thread_data_t
*data
= msvcrt_get_thread_data();
637 update_thread_locale(data
);
638 return data
->locinfo
;
641 /* INTERNAL: returns pthreadmbcinfo struct */
642 pthreadmbcinfo CDECL
get_mbcinfo(void) {
643 thread_data_t
*data
= msvcrt_get_thread_data();
644 update_thread_locale(data
);
645 return data
->mbcinfo
;
648 /* INTERNAL: constructs string returned by setlocale */
649 static inline char* construct_lc_all(pthreadlocinfo locinfo
) {
650 static char current_lc_all
[MAX_LOCALE_LENGTH
];
654 for(i
=LC_MIN
+1; i
<LC_MAX
; i
++) {
655 if(strcmp(locinfo
->lc_category
[i
].locale
,
656 locinfo
->lc_category
[i
+1].locale
))
661 return locinfo
->lc_category
[LC_COLLATE
].locale
;
663 sprintf(current_lc_all
,
664 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
665 locinfo
->lc_category
[LC_COLLATE
].locale
,
666 locinfo
->lc_category
[LC_CTYPE
].locale
,
667 locinfo
->lc_category
[LC_MONETARY
].locale
,
668 locinfo
->lc_category
[LC_NUMERIC
].locale
,
669 locinfo
->lc_category
[LC_TIME
].locale
);
671 return current_lc_all
;
675 /*********************************************************************
676 * _Getdays (MSVCRT.@)
678 char* CDECL
_Getdays(void)
680 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
681 int i
, len
, size
= 0;
687 size
+= strlen(cur
->str
.names
.short_wday
[i
]) + 1;
688 size
+= strlen(cur
->str
.names
.wday
[i
]) + 1;
690 out
= malloc(size
+1);
697 len
= strlen(cur
->str
.names
.short_wday
[i
]);
698 memcpy(&out
[size
], cur
->str
.names
.short_wday
[i
], len
);
702 len
= strlen(cur
->str
.names
.wday
[i
]);
703 memcpy(&out
[size
], cur
->str
.names
.wday
[i
], len
);
711 #if _MSVCR_VER >= 110
712 /*********************************************************************
713 * _W_Getdays (MSVCR110.@)
715 wchar_t* CDECL
_W_Getdays(void)
717 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
719 int i
, len
, size
= 0;
724 size
+= wcslen(cur
->wstr
.names
.short_wday
[i
]) + 1;
725 size
+= wcslen(cur
->wstr
.names
.wday
[i
]) + 1;
727 out
= malloc((size
+1)*sizeof(*out
));
734 len
= wcslen(cur
->wstr
.names
.short_wday
[i
]);
735 memcpy(&out
[size
], cur
->wstr
.names
.short_wday
[i
], len
*sizeof(*out
));
739 len
= wcslen(cur
->wstr
.names
.wday
[i
]);
740 memcpy(&out
[size
], cur
->wstr
.names
.wday
[i
], len
*sizeof(*out
));
749 /*********************************************************************
750 * _Getmonths (MSVCRT.@)
752 char* CDECL
_Getmonths(void)
754 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
755 int i
, len
, size
= 0;
760 for(i
=0; i
<12; i
++) {
761 size
+= strlen(cur
->str
.names
.short_mon
[i
]) + 1;
762 size
+= strlen(cur
->str
.names
.mon
[i
]) + 1;
764 out
= malloc(size
+1);
769 for(i
=0; i
<12; i
++) {
771 len
= strlen(cur
->str
.names
.short_mon
[i
]);
772 memcpy(&out
[size
], cur
->str
.names
.short_mon
[i
], len
);
776 len
= strlen(cur
->str
.names
.mon
[i
]);
777 memcpy(&out
[size
], cur
->str
.names
.mon
[i
], len
);
785 #if _MSVCR_VER >= 110
786 /*********************************************************************
787 * _W_Getmonths (MSVCR110.@)
789 wchar_t* CDECL
_W_Getmonths(void)
791 __lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
793 int i
, len
, size
= 0;
797 for(i
=0; i
<12; i
++) {
798 size
+= wcslen(cur
->wstr
.names
.short_mon
[i
]) + 1;
799 size
+= wcslen(cur
->wstr
.names
.mon
[i
]) + 1;
801 out
= malloc((size
+1)*sizeof(*out
));
806 for(i
=0; i
<12; i
++) {
808 len
= wcslen(cur
->wstr
.names
.short_mon
[i
]);
809 memcpy(&out
[size
], cur
->wstr
.names
.short_mon
[i
], len
*sizeof(*out
));
813 len
= wcslen(cur
->wstr
.names
.mon
[i
]);
814 memcpy(&out
[size
], cur
->wstr
.names
.mon
[i
], len
*sizeof(*out
));
823 /*********************************************************************
824 * _Gettnames (MSVCRT.@)
826 void* CDECL
_Gettnames(void)
828 __lc_time_data
*ret
, *cur
= get_locinfo()->lc_time_curr
;
829 unsigned int i
, len
, size
= sizeof(__lc_time_data
);
833 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++)
834 size
+= strlen(cur
->str
.str
[i
])+1;
835 #if _MSVCR_VER >= 110
836 for(i
=0; i
<ARRAY_SIZE(cur
->wstr
.wstr
); i
++)
837 size
+= (wcslen(cur
->wstr
.wstr
[i
]) + 1) * sizeof(wchar_t);
843 memcpy(ret
, cur
, sizeof(*ret
));
846 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++) {
847 len
= strlen(cur
->str
.str
[i
])+1;
848 memcpy(&ret
->data
[size
], cur
->str
.str
[i
], len
);
849 ret
->str
.str
[i
] = &ret
->data
[size
];
852 #if _MSVCR_VER >= 110
853 for(i
=0; i
<ARRAY_SIZE(cur
->wstr
.wstr
); i
++) {
854 len
= (wcslen(cur
->wstr
.wstr
[i
]) + 1) * sizeof(wchar_t);
855 memcpy(&ret
->data
[size
], cur
->wstr
.wstr
[i
], len
);
856 ret
->wstr
.wstr
[i
] = (wchar_t*)&ret
->data
[size
];
864 #if _MSVCR_VER >= 110
865 /*********************************************************************
866 * _W_Gettnames (MSVCR110.@)
868 void* CDECL
_W_Gettnames(void)
874 /*********************************************************************
875 * __crtLCMapStringA (MSVCRT.@)
877 int CDECL
__crtLCMapStringA(
878 LCID lcid
, DWORD mapflags
, const char* src
, int srclen
, char* dst
,
879 int dstlen
, unsigned int codepage
, int xflag
881 WCHAR buf_in
[32], *in
= buf_in
;
882 WCHAR buf_out
[32], *out
= buf_out
;
883 int in_len
, out_len
, r
;
885 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
886 lcid
, mapflags
, src
, srclen
, dst
, dstlen
, codepage
, xflag
);
888 in_len
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, NULL
, 0);
889 if (!in_len
) return 0;
890 if (in_len
> ARRAY_SIZE(buf_in
))
892 in
= malloc(in_len
* sizeof(WCHAR
));
896 r
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, in
, in_len
);
899 if (mapflags
& LCMAP_SORTKEY
)
901 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, (WCHAR
*)dst
, dstlen
);
905 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, NULL
, 0);
908 if (r
> ARRAY_SIZE(buf_out
))
910 out
= malloc(r
* sizeof(WCHAR
));
918 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, out
, out_len
);
921 r
= WideCharToMultiByte(codepage
, 0, out
, out_len
, dst
, dstlen
, NULL
, NULL
);
924 if (in
!= buf_in
) free(in
);
925 if (out
!= buf_out
) free(out
);
929 /*********************************************************************
930 * __crtLCMapStringW (MSVCRT.@)
932 int CDECL
__crtLCMapStringW(LCID lcid
, DWORD mapflags
, const wchar_t *src
,
933 int srclen
, wchar_t *dst
, int dstlen
, unsigned int codepage
, int xflag
)
935 FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
936 lcid
, mapflags
, debugstr_w(src
), srclen
, dst
, dstlen
, codepage
, xflag
);
938 return LCMapStringW(lcid
, mapflags
, src
, srclen
, dst
, dstlen
);
941 /*********************************************************************
942 * __crtCompareStringA (MSVCRT.@)
944 int CDECL
__crtCompareStringA( LCID lcid
, DWORD flags
, const char *src1
, int len1
,
945 const char *src2
, int len2
)
947 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
948 lcid
, flags
, debugstr_a(src1
), len1
, debugstr_a(src2
), len2
);
949 /* FIXME: probably not entirely right */
950 return CompareStringA( lcid
, flags
, src1
, len1
, src2
, len2
);
953 /*********************************************************************
954 * __crtCompareStringW (MSVCRT.@)
956 int CDECL
__crtCompareStringW( LCID lcid
, DWORD flags
, const wchar_t *src1
, int len1
,
957 const wchar_t *src2
, int len2
)
959 FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n",
960 lcid
, flags
, debugstr_w(src1
), len1
, debugstr_w(src2
), len2
);
961 /* FIXME: probably not entirely right */
962 return CompareStringW( lcid
, flags
, src1
, len1
, src2
, len2
);
965 /*********************************************************************
966 * __crtGetLocaleInfoW (MSVCRT.@)
968 int CDECL
__crtGetLocaleInfoW( LCID lcid
, LCTYPE type
, wchar_t *buffer
, int len
)
970 FIXME("(lcid %lx, type %lx, %p(%d), partial stub\n", lcid
, type
, buffer
, len
);
971 /* FIXME: probably not entirely right */
972 return GetLocaleInfoW( lcid
, type
, buffer
, len
);
975 #if _MSVCR_VER >= 110
976 /*********************************************************************
977 * __crtGetLocaleInfoEx (MSVC110.@)
979 int CDECL
__crtGetLocaleInfoEx( const WCHAR
*locale
, LCTYPE type
, wchar_t *buffer
, int len
)
981 TRACE("(%s, %lx, %p, %d)\n", debugstr_w(locale
), type
, buffer
, len
);
982 return GetLocaleInfoEx(locale
, type
, buffer
, len
);
986 /*********************************************************************
987 * __crtGetStringTypeW(MSVCRT.@)
989 * This function was accepting different number of arguments in older
990 * versions of msvcrt.
992 BOOL CDECL
__crtGetStringTypeW(DWORD unk
, DWORD type
,
993 wchar_t *buffer
, int len
, WORD
*out
)
995 FIXME("(unk %lx, type %lx, wstr %p(%d), %p) partial stub\n",
996 unk
, type
, buffer
, len
, out
);
998 return GetStringTypeW(type
, buffer
, len
, out
);
1001 /*********************************************************************
1002 * localeconv (MSVCRT.@)
1004 struct lconv
* CDECL
localeconv(void)
1006 return get_locinfo()->lconv
;
1009 /*********************************************************************
1010 * __lconv_init (MSVCRT.@)
1012 int CDECL
__lconv_init(void)
1014 /* this is used to make chars unsigned */
1015 cloc_lconv
.int_frac_digits
= (char)UCHAR_MAX
;
1016 cloc_lconv
.frac_digits
= (char)UCHAR_MAX
;
1017 cloc_lconv
.p_cs_precedes
= (char)UCHAR_MAX
;
1018 cloc_lconv
.p_sep_by_space
= (char)UCHAR_MAX
;
1019 cloc_lconv
.n_cs_precedes
= (char)UCHAR_MAX
;
1020 cloc_lconv
.n_sep_by_space
= (char)UCHAR_MAX
;
1021 cloc_lconv
.p_sign_posn
= (char)UCHAR_MAX
;
1022 cloc_lconv
.n_sign_posn
= (char)UCHAR_MAX
;
1026 /*********************************************************************
1027 * ___lc_handle_func (MSVCRT.@)
1029 LCID
* CDECL
___lc_handle_func(void)
1031 return (LCID
*)get_locinfo()->lc_handle
;
1034 #if _MSVCR_VER >= 110
1035 /*********************************************************************
1036 * ___lc_locale_name_func (MSVCR110.@)
1038 wchar_t** CDECL
___lc_locale_name_func(void)
1040 return get_locinfo()->lc_name
;
1044 /*********************************************************************
1045 * ___lc_codepage_func (MSVCRT.@)
1047 unsigned int CDECL
___lc_codepage_func(void)
1049 return get_locinfo()->lc_codepage
;
1052 /*********************************************************************
1053 * ___lc_collate_cp_func (MSVCRT.@)
1055 int CDECL
___lc_collate_cp_func(void)
1057 return get_locinfo()->lc_collate_cp
;
1060 /* INTERNAL: frees pthreadlocinfo struct */
1061 void free_locinfo(pthreadlocinfo locinfo
)
1068 for(i
=LC_MIN
+1; i
<=LC_MAX
; i
++) {
1069 if(!locinfo
->lc_category
[i
].refcount
1070 || !InterlockedDecrement((LONG
*)locinfo
->lc_category
[i
].refcount
)) {
1071 free(locinfo
->lc_category
[i
].locale
);
1072 free(locinfo
->lc_category
[i
].refcount
);
1074 if(!locinfo
->lc_category
[i
].wrefcount
1075 || !InterlockedDecrement((LONG
*)locinfo
->lc_category
[i
].wrefcount
)) {
1076 #if _MSVCR_VER >= 110
1077 free(locinfo
->lc_name
[i
]);
1079 free(locinfo
->lc_category
[i
].wrefcount
);
1083 if(locinfo
->lconv_num_refcount
1084 && !InterlockedDecrement((LONG
*)locinfo
->lconv_num_refcount
)) {
1085 free(locinfo
->lconv
->decimal_point
);
1086 free(locinfo
->lconv
->thousands_sep
);
1087 free(locinfo
->lconv
->grouping
);
1088 #if _MSVCR_VER >= 100
1089 free(locinfo
->lconv
->_W_decimal_point
);
1090 free(locinfo
->lconv
->_W_thousands_sep
);
1092 free(locinfo
->lconv_num_refcount
);
1094 if(locinfo
->lconv_mon_refcount
1095 && !InterlockedDecrement((LONG
*)locinfo
->lconv_mon_refcount
)) {
1096 free(locinfo
->lconv
->int_curr_symbol
);
1097 free(locinfo
->lconv
->currency_symbol
);
1098 free(locinfo
->lconv
->mon_decimal_point
);
1099 free(locinfo
->lconv
->mon_thousands_sep
);
1100 free(locinfo
->lconv
->mon_grouping
);
1101 free(locinfo
->lconv
->positive_sign
);
1102 free(locinfo
->lconv
->negative_sign
);
1103 #if _MSVCR_VER >= 100
1104 free(locinfo
->lconv
->_W_int_curr_symbol
);
1105 free(locinfo
->lconv
->_W_currency_symbol
);
1106 free(locinfo
->lconv
->_W_mon_decimal_point
);
1107 free(locinfo
->lconv
->_W_mon_thousands_sep
);
1108 free(locinfo
->lconv
->_W_positive_sign
);
1109 free(locinfo
->lconv
->_W_negative_sign
);
1111 free(locinfo
->lconv_mon_refcount
);
1113 if(locinfo
->lconv_intl_refcount
1114 && !InterlockedDecrement((LONG
*)locinfo
->lconv_intl_refcount
)) {
1115 free(locinfo
->lconv_intl_refcount
);
1116 free(locinfo
->lconv
);
1119 if(locinfo
->ctype1_refcount
1120 && !InterlockedDecrement((LONG
*)locinfo
->ctype1_refcount
)) {
1121 free(locinfo
->ctype1_refcount
);
1122 free(locinfo
->ctype1
);
1123 free((void*)locinfo
->pclmap
);
1124 free((void*)locinfo
->pcumap
);
1127 if(locinfo
->lc_time_curr
&& !InterlockedDecrement(&locinfo
->lc_time_curr
->refcount
)
1128 && locinfo
->lc_time_curr
!= &cloc_time_data
)
1129 free(locinfo
->lc_time_curr
);
1131 if(InterlockedDecrement((LONG
*)&locinfo
->refcount
))
1137 /* INTERNAL: frees pthreadmbcinfo struct */
1138 void free_mbcinfo(pthreadmbcinfo mbcinfo
)
1143 if(InterlockedDecrement(&mbcinfo
->refcount
))
1149 _locale_t CDECL
get_current_locale_noalloc(_locale_t locale
)
1151 thread_data_t
*data
= msvcrt_get_thread_data();
1153 update_thread_locale(data
);
1154 locale
->locinfo
= data
->locinfo
;
1155 locale
->mbcinfo
= data
->mbcinfo
;
1157 grab_locinfo(locale
->locinfo
);
1158 InterlockedIncrement(&locale
->mbcinfo
->refcount
);
1162 void CDECL
free_locale_noalloc(_locale_t locale
)
1164 free_locinfo(locale
->locinfo
);
1165 free_mbcinfo(locale
->mbcinfo
);
1168 /*********************************************************************
1169 * _get_current_locale (MSVCRT.@)
1171 _locale_t CDECL
_get_current_locale(void)
1173 _locale_t loc
= malloc(sizeof(_locale_tstruct
));
1177 return get_current_locale_noalloc(loc
);
1180 /*********************************************************************
1181 * _free_locale (MSVCRT.@)
1183 void CDECL
_free_locale(_locale_t locale
)
1188 free_locale_noalloc(locale
);
1192 static inline BOOL
category_needs_update(int cat
,
1193 const threadlocinfo
*locinfo
, WCHAR
*sname
, unsigned short cp
)
1195 #if _MSVCR_VER < 110
1198 if(!locinfo
) return TRUE
;
1199 #if _MSVCR_VER >= 110
1200 if(!locinfo
->lc_name
[cat
] || !sname
) return TRUE
;
1201 return wcscmp(sname
, locinfo
->lc_name
[cat
]) != 0 || cp
!=locinfo
->lc_id
[cat
].wCodePage
;
1203 lcid
= sname
? LocaleNameToLCID(sname
, 0) : 0;
1204 return lcid
!=locinfo
->lc_handle
[cat
] || cp
!=locinfo
->lc_id
[cat
].wCodePage
;
1208 static __lc_time_data
* create_time_data(WCHAR
*sname
)
1210 static const DWORD time_data
[] = {
1211 LOCALE_SABBREVDAYNAME7
, LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
,
1212 LOCALE_SABBREVDAYNAME3
, LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
,
1213 LOCALE_SABBREVDAYNAME6
,
1214 LOCALE_SDAYNAME7
, LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
1215 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
,
1216 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
1217 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
1218 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
1219 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
1220 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
, LOCALE_SMONTHNAME4
,
1221 LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
, LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
,
1222 LOCALE_SMONTHNAME9
, LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
1223 LOCALE_S1159
, LOCALE_S2359
,
1224 LOCALE_SSHORTDATE
, LOCALE_SLONGDATE
,
1228 __lc_time_data
*cur
;
1230 LCID lcid
= LocaleNameToLCID(sname
, LCID_CONVERSION_FLAGS
);
1233 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1234 ret
= GetLocaleInfoA(lcid
, time_data
[i
], NULL
, 0);
1239 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1240 ret
= GetLocaleInfoEx(sname
, time_data
[i
], NULL
, 0);
1243 size
+= ret
*sizeof(wchar_t);
1246 #if _MSVCR_VER >= 110
1247 size
+= (wcslen(sname
) + 1) * sizeof(wchar_t);
1250 cur
= malloc(FIELD_OFFSET(__lc_time_data
, data
[size
]));
1255 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1256 cur
->str
.str
[i
] = &cur
->data
[ret
];
1257 ret
+= GetLocaleInfoA(lcid
, time_data
[i
], &cur
->data
[ret
], size
-ret
);
1259 #if _MSVCR_VER == 0 || _MSVCR_VER >= 100
1260 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1261 cur
->wstr
.wstr
[i
] = (wchar_t*)&cur
->data
[ret
];
1262 ret
+= GetLocaleInfoEx(sname
, time_data
[i
], (wchar_t*)&cur
->data
[ret
],
1263 (size
- ret
) / sizeof(wchar_t)) * sizeof(wchar_t);
1266 #if _MSVCR_VER >= 110
1267 cur
->locname
= (wchar_t*)&cur
->data
[ret
];
1268 wcscpy((wchar_t*)&cur
->data
[ret
], sname
);
1278 static pthreadlocinfo
create_locinfo(int category
,
1279 const char *locale
, const threadlocinfo
*old_locinfo
)
1281 static const char collate
[] = "COLLATE=";
1282 static const char ctype
[] = "CTYPE=";
1283 static const char monetary
[] = "MONETARY=";
1284 static const char numeric
[] = "NUMERIC=";
1285 static const char time
[] = "TIME=";
1287 pthreadlocinfo locinfo
= NULL
;
1288 unsigned short cp
[6] = { 0 };
1289 const char *locale_name
[6] = { 0 };
1290 WCHAR
*locale_sname
[6] = { 0 };
1291 int val
, locale_len
[6] = { 0 };
1297 TRACE("(%d %s)\n", category
, locale
);
1299 if(category
<LC_MIN
|| category
>LC_MAX
|| !locale
)
1302 if(locale
[0]=='C' && !locale
[1]) {
1303 locale_sname
[0] = NULL
;
1305 } else if (locale
[0] == 'L' && locale
[1] == 'C' && locale
[2] == '_') {
1309 locale
+= 3; /* LC_ */
1310 if(!memcmp(locale
, collate
, sizeof(collate
)-1)) {
1312 locale
+= sizeof(collate
)-1;
1313 } else if(!memcmp(locale
, ctype
, sizeof(ctype
)-1)) {
1315 locale
+= sizeof(ctype
)-1;
1316 } else if(!memcmp(locale
, monetary
, sizeof(monetary
)-1)) {
1318 locale
+= sizeof(monetary
)-1;
1319 } else if(!memcmp(locale
, numeric
, sizeof(numeric
)-1)) {
1321 locale
+= sizeof(numeric
)-1;
1322 } else if(!memcmp(locale
, time
, sizeof(time
)-1)) {
1324 locale
+= sizeof(time
)-1;
1328 p
= strchr(locale
, ';');
1329 if(locale
[0]=='C' && (locale
[1]==';' || locale
[1]=='\0')) {
1330 locale_sname
[i
] = NULL
;
1333 BOOL locale_found
= FALSE
;
1336 memcpy(buf
, locale
, p
-locale
);
1337 buf
[p
-locale
] = '\0';
1338 locale_found
= locale_to_sname(buf
, &cp
[i
], &sname_match
, wbuf
);
1340 locale_found
= locale_to_sname(locale
, &cp
[i
], &sname_match
, wbuf
);
1343 if(!locale_found
|| !(locale_sname
[i
] = wcsdup(wbuf
)))
1346 locale_name
[i
] = locale
;
1347 locale_len
[i
] = p
? p
-locale
: strlen(locale
);
1351 if(!p
|| *(p
+1)!='L' || *(p
+2)!='C' || *(p
+3)!='_')
1357 BOOL locale_found
= locale_to_sname(locale
, &cp
[0], &sname_match
, wbuf
);
1362 locale_sname
[0] = wcsdup(wbuf
);
1363 if(!locale_sname
[0])
1367 locale_name
[0] = locale
;
1368 locale_len
[0] = strlen(locale
);
1371 for(i
=1; i
<6; i
++) {
1372 locale_sname
[i
] = wcsdup(locale_sname
[0]);
1373 if(!locale_sname
[i
])
1377 locale_name
[i
] = locale_name
[0];
1378 locale_len
[i
] = locale_len
[0];
1382 for(i
=1; i
<6; i
++) {
1383 #if _MSVCR_VER < 140
1384 if(i
==LC_CTYPE
&& cp
[i
]==CP_UTF8
) {
1385 #if _MSVCR_VER >= 110
1387 locale_sname
[i
] = wcsdup(old_locinfo
->lc_name
[i
]);
1388 if (old_locinfo
->lc_name
[i
] && !locale_sname
[i
])
1393 if(old_locinfo
&& old_locinfo
->lc_handle
[i
]) {
1394 sname_size
= LCIDToLocaleName(old_locinfo
->lc_handle
[i
], NULL
, 0, 0);
1395 locale_sname
[i
] = malloc(sname_size
* sizeof(WCHAR
));
1396 if(!locale_sname
[i
])
1398 LCIDToLocaleName(old_locinfo
->lc_handle
[i
], locale_sname
[i
], sname_size
, 0);
1400 locale_sname
[i
] = NULL
;
1404 locale_name
[i
] = NULL
;
1406 cp
[i
] = old_locinfo
? old_locinfo
->lc_id
[i
].wCodePage
: 0;
1409 if(category
!=LC_ALL
&& category
!=i
) {
1411 #if _MSVCR_VER >= 110
1412 locale_sname
[i
] = wcsdup(old_locinfo
->lc_name
[i
]);
1413 if(old_locinfo
->lc_name
[i
] && !locale_sname
[i
])
1417 if(old_locinfo
->lc_handle
[i
]) {
1418 sname_size
= LCIDToLocaleName(old_locinfo
->lc_handle
[i
], NULL
, 0, 0);
1419 locale_sname
[i
] = malloc(sname_size
* sizeof(WCHAR
));
1420 if(!locale_sname
[i
])
1422 LCIDToLocaleName(old_locinfo
->lc_handle
[i
], locale_sname
[i
], sname_size
, 0);
1424 locale_sname
[i
] = NULL
;
1427 cp
[i
] = old_locinfo
->lc_id
[i
].wCodePage
;
1429 locale_sname
[i
] = NULL
;
1435 locinfo
= malloc(sizeof(threadlocinfo
));
1439 memset(locinfo
, 0, sizeof(threadlocinfo
));
1440 locinfo
->refcount
= 1;
1442 if(locale_name
[LC_COLLATE
] &&
1443 !init_category_name(locale_name
[LC_COLLATE
],
1444 locale_len
[LC_COLLATE
], locinfo
, LC_COLLATE
)) {
1448 if(!category_needs_update(LC_COLLATE
, old_locinfo
,
1449 locale_sname
[LC_COLLATE
], cp
[LC_COLLATE
])) {
1450 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_COLLATE
);
1451 locinfo
->lc_collate_cp
= old_locinfo
->lc_collate_cp
;
1452 } else if(locale_sname
[LC_COLLATE
]) {
1453 if(!update_threadlocinfo_category(locale_sname
[LC_COLLATE
],
1454 cp
[LC_COLLATE
], locinfo
, LC_COLLATE
)) {
1458 locinfo
->lc_collate_cp
= locinfo
->lc_id
[LC_COLLATE
].wCodePage
;
1460 if(!init_category_name("C", 1, locinfo
, LC_COLLATE
)) {
1465 if(locale_name
[LC_CTYPE
] &&
1466 !init_category_name(locale_name
[LC_CTYPE
],
1467 locale_len
[LC_CTYPE
], locinfo
, LC_CTYPE
)) {
1471 if(!category_needs_update(LC_CTYPE
, old_locinfo
,
1472 locale_sname
[LC_CTYPE
], cp
[LC_CTYPE
])) {
1473 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_CTYPE
);
1474 locinfo
->lc_codepage
= old_locinfo
->lc_codepage
;
1475 locinfo
->lc_clike
= old_locinfo
->lc_clike
;
1476 locinfo
->mb_cur_max
= old_locinfo
->mb_cur_max
;
1477 locinfo
->ctype1
= old_locinfo
->ctype1
;
1478 locinfo
->ctype1_refcount
= old_locinfo
->ctype1_refcount
;
1479 locinfo
->pctype
= old_locinfo
->pctype
;
1480 locinfo
->pclmap
= old_locinfo
->pclmap
;
1481 locinfo
->pcumap
= old_locinfo
->pcumap
;
1482 if(locinfo
->ctype1_refcount
)
1483 InterlockedIncrement((LONG
*)locinfo
->ctype1_refcount
);
1484 } else if(locale_sname
[LC_CTYPE
]) {
1488 if(!update_threadlocinfo_category(locale_sname
[LC_CTYPE
],
1489 cp
[LC_CTYPE
], locinfo
, LC_CTYPE
)) {
1493 locinfo
->lc_codepage
= locinfo
->lc_id
[LC_CTYPE
].wCodePage
;
1494 locinfo
->lc_clike
= 1;
1495 if(!GetCPInfo(locinfo
->lc_codepage
, &cp_info
)) {
1498 locinfo
->mb_cur_max
= cp_info
.MaxCharSize
;
1500 locinfo
->ctype1_refcount
= malloc(sizeof(int));
1501 if(!locinfo
->ctype1_refcount
) {
1504 *locinfo
->ctype1_refcount
= 1;
1506 locinfo
->ctype1
= malloc(sizeof(short[257]));
1507 locinfo
->pclmap
= malloc(sizeof(char[256]));
1508 locinfo
->pcumap
= malloc(sizeof(char[256]));
1509 if(!locinfo
->ctype1
|| !locinfo
->pclmap
|| !locinfo
->pcumap
) {
1513 locinfo
->ctype1
[0] = 0;
1514 locinfo
->pctype
= locinfo
->ctype1
+1;
1516 buf
[1] = buf
[2] = '\0';
1517 for(i
=1; i
<257; i
++) {
1520 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1521 locinfo
->ctype1
[i
] = 0;
1523 GetStringTypeA(locinfo
->lc_handle
[LC_CTYPE
], CT_CTYPE1
, buf
,
1524 1, locinfo
->ctype1
+i
);
1527 for(i
=0; cp_info
.LeadByte
[i
+1]!=0; i
+=2)
1528 for(j
=cp_info
.LeadByte
[i
]; j
<=cp_info
.LeadByte
[i
+1]; j
++)
1529 locinfo
->ctype1
[j
+1] |= _LEADBYTE
;
1531 for(i
=0; i
<256; i
++) {
1532 if(locinfo
->pctype
[i
] & _LEADBYTE
)
1538 LCMapStringA(locinfo
->lc_handle
[LC_CTYPE
], LCMAP_LOWERCASE
, buf
, 256,
1539 (char*)locinfo
->pclmap
, 256);
1540 LCMapStringA(locinfo
->lc_handle
[LC_CTYPE
], LCMAP_UPPERCASE
, buf
, 256,
1541 (char*)locinfo
->pcumap
, 256);
1543 locinfo
->lc_clike
= 1;
1544 locinfo
->mb_cur_max
= 1;
1545 locinfo
->pctype
= MSVCRT__ctype
+1;
1546 locinfo
->pclmap
= cloc_clmap
;
1547 locinfo
->pcumap
= cloc_cumap
;
1548 if(!init_category_name("C", 1, locinfo
, LC_CTYPE
)) {
1553 if(!category_needs_update(LC_MONETARY
, old_locinfo
,
1554 locale_sname
[LC_MONETARY
], cp
[LC_MONETARY
]) &&
1555 !category_needs_update(LC_NUMERIC
, old_locinfo
,
1556 locale_sname
[LC_NUMERIC
], cp
[LC_NUMERIC
])) {
1557 locinfo
->lconv
= old_locinfo
->lconv
;
1558 locinfo
->lconv_intl_refcount
= old_locinfo
->lconv_intl_refcount
;
1559 if(locinfo
->lconv_intl_refcount
)
1560 InterlockedIncrement((LONG
*)locinfo
->lconv_intl_refcount
);
1561 } else if(locale_sname
[LC_MONETARY
] || locale_sname
[LC_NUMERIC
]) {
1562 locinfo
->lconv
= malloc(sizeof(struct lconv
));
1563 locinfo
->lconv_intl_refcount
= malloc(sizeof(int));
1564 if(!locinfo
->lconv
|| !locinfo
->lconv_intl_refcount
) {
1565 free(locinfo
->lconv
);
1566 free(locinfo
->lconv_intl_refcount
);
1567 locinfo
->lconv
= NULL
;
1568 locinfo
->lconv_intl_refcount
= NULL
;
1571 memset(locinfo
->lconv
, 0, sizeof(struct lconv
));
1572 *locinfo
->lconv_intl_refcount
= 1;
1574 locinfo
->lconv
= &cloc_lconv
;
1577 if(locale_name
[LC_MONETARY
] &&
1578 !init_category_name(locale_name
[LC_MONETARY
],
1579 locale_len
[LC_MONETARY
], locinfo
, LC_MONETARY
)) {
1583 if(!category_needs_update(LC_MONETARY
, old_locinfo
,
1584 locale_sname
[LC_MONETARY
], cp
[LC_MONETARY
])) {
1585 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_MONETARY
);
1586 locinfo
->lconv_mon_refcount
= old_locinfo
->lconv_mon_refcount
;
1587 if(locinfo
->lconv_mon_refcount
)
1588 InterlockedIncrement((LONG
*)locinfo
->lconv_mon_refcount
);
1589 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1590 locinfo
->lconv
->int_curr_symbol
= old_locinfo
->lconv
->int_curr_symbol
;
1591 locinfo
->lconv
->currency_symbol
= old_locinfo
->lconv
->currency_symbol
;
1592 locinfo
->lconv
->mon_decimal_point
= old_locinfo
->lconv
->mon_decimal_point
;
1593 locinfo
->lconv
->mon_thousands_sep
= old_locinfo
->lconv
->mon_thousands_sep
;
1594 locinfo
->lconv
->mon_grouping
= old_locinfo
->lconv
->mon_grouping
;
1595 locinfo
->lconv
->positive_sign
= old_locinfo
->lconv
->positive_sign
;
1596 locinfo
->lconv
->negative_sign
= old_locinfo
->lconv
->negative_sign
;
1597 locinfo
->lconv
->int_frac_digits
= old_locinfo
->lconv
->int_frac_digits
;
1598 locinfo
->lconv
->frac_digits
= old_locinfo
->lconv
->frac_digits
;
1599 locinfo
->lconv
->p_cs_precedes
= old_locinfo
->lconv
->p_cs_precedes
;
1600 locinfo
->lconv
->p_sep_by_space
= old_locinfo
->lconv
->p_sep_by_space
;
1601 locinfo
->lconv
->n_cs_precedes
= old_locinfo
->lconv
->n_cs_precedes
;
1602 locinfo
->lconv
->n_sep_by_space
= old_locinfo
->lconv
->n_sep_by_space
;
1603 locinfo
->lconv
->p_sign_posn
= old_locinfo
->lconv
->p_sign_posn
;
1604 locinfo
->lconv
->n_sign_posn
= old_locinfo
->lconv
->n_sign_posn
;
1605 #if _MSVCR_VER >= 100
1606 locinfo
->lconv
->_W_int_curr_symbol
= old_locinfo
->lconv
->_W_int_curr_symbol
;
1607 locinfo
->lconv
->_W_currency_symbol
= old_locinfo
->lconv
->_W_currency_symbol
;
1608 locinfo
->lconv
->_W_mon_decimal_point
= old_locinfo
->lconv
->_W_mon_decimal_point
;
1609 locinfo
->lconv
->_W_mon_thousands_sep
= old_locinfo
->lconv
->_W_mon_thousands_sep
;
1610 locinfo
->lconv
->_W_positive_sign
= old_locinfo
->lconv
->_W_positive_sign
;
1611 locinfo
->lconv
->_W_negative_sign
= old_locinfo
->lconv
->_W_negative_sign
;
1614 } else if(locale_sname
[LC_MONETARY
]) {
1615 if(!update_threadlocinfo_category(locale_sname
[LC_MONETARY
],
1616 cp
[LC_MONETARY
], locinfo
, LC_MONETARY
)) {
1620 locinfo
->lconv_mon_refcount
= malloc(sizeof(int));
1621 if(!locinfo
->lconv_mon_refcount
) {
1625 *locinfo
->lconv_mon_refcount
= 1;
1627 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SINTLSYMBOL
1628 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1629 i
= WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1630 if(i
&& (locinfo
->lconv
->int_curr_symbol
= malloc(i
)))
1631 WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, locinfo
->lconv
->int_curr_symbol
, i
, NULL
, NULL
);
1636 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SCURRENCY
1637 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1638 i
= WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1639 if(i
&& (locinfo
->lconv
->currency_symbol
= malloc(i
)))
1640 WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, locinfo
->lconv
->currency_symbol
, i
, NULL
, NULL
);
1645 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SMONDECIMALSEP
1646 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1647 i
= WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1648 if(i
&& (locinfo
->lconv
->mon_decimal_point
= malloc(i
)))
1649 WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, locinfo
->lconv
->mon_decimal_point
, i
, NULL
, NULL
);
1654 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1655 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1656 i
= WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1657 if(i
&& (locinfo
->lconv
->mon_thousands_sep
= malloc(i
)))
1658 WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, locinfo
->lconv
->mon_thousands_sep
, i
, NULL
, NULL
);
1663 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SMONGROUPING
1664 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1665 WideCharToMultiByte(CP_ACP
, 0, wbuf
, -1, buf
, 256, NULL
, NULL
);
1667 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1668 if(i
&& (locinfo
->lconv
->mon_grouping
= malloc(i
))) {
1669 for(i
=0; buf
[i
+1]==';'; i
+=2)
1670 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1671 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1673 locinfo
->lconv
->mon_grouping
[i
/2+1] = 127;
1678 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SPOSITIVESIGN
1679 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1680 i
= WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1681 if(i
&& (locinfo
->lconv
->positive_sign
= malloc(i
)))
1682 WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, locinfo
->lconv
->positive_sign
, i
, NULL
, NULL
);
1687 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SNEGATIVESIGN
1688 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1689 i
= WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1690 if(i
&& (locinfo
->lconv
->negative_sign
= malloc(i
)))
1691 WideCharToMultiByte(cp
[LC_MONETARY
], 0, wbuf
, -1, locinfo
->lconv
->negative_sign
, i
, NULL
, NULL
);
1696 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_IINTLCURRDIGITS
1697 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1698 locinfo
->lconv
->int_frac_digits
= val
;
1703 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_ICURRDIGITS
1704 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1705 locinfo
->lconv
->frac_digits
= val
;
1710 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_IPOSSYMPRECEDES
1711 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1712 locinfo
->lconv
->p_cs_precedes
= val
;
1717 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_IPOSSEPBYSPACE
1718 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1719 locinfo
->lconv
->p_sep_by_space
= val
;
1724 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_INEGSYMPRECEDES
1725 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1726 locinfo
->lconv
->n_cs_precedes
= val
;
1731 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_INEGSEPBYSPACE
1732 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1733 locinfo
->lconv
->n_sep_by_space
= val
;
1738 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_IPOSSIGNPOSN
1739 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1740 locinfo
->lconv
->p_sign_posn
= val
;
1745 if(GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_INEGSIGNPOSN
1746 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1747 locinfo
->lconv
->n_sign_posn
= val
;
1752 #if _MSVCR_VER >= 100
1753 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SINTLSYMBOL
1754 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1755 if(i
&& (locinfo
->lconv
->_W_int_curr_symbol
= malloc(i
* sizeof(wchar_t))))
1756 memcpy(locinfo
->lconv
->_W_int_curr_symbol
, wbuf
, i
* sizeof(wchar_t));
1761 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SCURRENCY
1762 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1763 if(i
&& (locinfo
->lconv
->_W_currency_symbol
= malloc(i
* sizeof(wchar_t))))
1764 memcpy(locinfo
->lconv
->_W_currency_symbol
, wbuf
, i
* sizeof(wchar_t));
1769 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SMONDECIMALSEP
1770 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1771 if(i
&& (locinfo
->lconv
->_W_mon_decimal_point
= malloc(i
* sizeof(wchar_t))))
1772 memcpy(locinfo
->lconv
->_W_mon_decimal_point
, wbuf
, i
* sizeof(wchar_t));
1777 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1778 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1779 if(i
&& (locinfo
->lconv
->_W_mon_thousands_sep
= malloc(i
* sizeof(wchar_t))))
1780 memcpy(locinfo
->lconv
->_W_mon_thousands_sep
, wbuf
, i
* sizeof(wchar_t));
1785 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SPOSITIVESIGN
1786 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1787 if(i
&& (locinfo
->lconv
->_W_positive_sign
= malloc(i
* sizeof(wchar_t))))
1788 memcpy(locinfo
->lconv
->_W_positive_sign
, wbuf
, i
* sizeof(wchar_t));
1793 i
= GetLocaleInfoEx(locale_sname
[LC_MONETARY
], LOCALE_SNEGATIVESIGN
1794 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1795 if(i
&& (locinfo
->lconv
->_W_negative_sign
= malloc(i
* sizeof(wchar_t))))
1796 memcpy(locinfo
->lconv
->_W_negative_sign
, wbuf
, i
* sizeof(wchar_t));
1802 if (locinfo
->lconv
!= &cloc_lconv
) {
1803 locinfo
->lconv
->int_curr_symbol
= cloc_lconv
.int_curr_symbol
;
1804 locinfo
->lconv
->currency_symbol
= cloc_lconv
.currency_symbol
;
1805 locinfo
->lconv
->mon_decimal_point
= cloc_lconv
.mon_decimal_point
;
1806 locinfo
->lconv
->mon_thousands_sep
= cloc_lconv
.mon_thousands_sep
;
1807 locinfo
->lconv
->mon_grouping
= cloc_lconv
.mon_grouping
;
1808 locinfo
->lconv
->positive_sign
= cloc_lconv
.positive_sign
;
1809 locinfo
->lconv
->negative_sign
= cloc_lconv
.negative_sign
;
1810 locinfo
->lconv
->int_frac_digits
= cloc_lconv
.int_frac_digits
;
1811 locinfo
->lconv
->frac_digits
= cloc_lconv
.frac_digits
;
1812 locinfo
->lconv
->p_cs_precedes
= cloc_lconv
.p_cs_precedes
;
1813 locinfo
->lconv
->p_sep_by_space
= cloc_lconv
.p_sep_by_space
;
1814 locinfo
->lconv
->n_cs_precedes
= cloc_lconv
.n_cs_precedes
;
1815 locinfo
->lconv
->n_sep_by_space
= cloc_lconv
.n_sep_by_space
;
1816 locinfo
->lconv
->p_sign_posn
= cloc_lconv
.p_sign_posn
;
1817 locinfo
->lconv
->n_sign_posn
= cloc_lconv
.n_sign_posn
;
1819 #if _MSVCR_VER >= 100
1820 locinfo
->lconv
->_W_int_curr_symbol
= cloc_lconv
._W_int_curr_symbol
;
1821 locinfo
->lconv
->_W_currency_symbol
= cloc_lconv
._W_currency_symbol
;
1822 locinfo
->lconv
->_W_mon_decimal_point
= cloc_lconv
._W_mon_decimal_point
;
1823 locinfo
->lconv
->_W_mon_thousands_sep
= cloc_lconv
._W_mon_thousands_sep
;
1824 locinfo
->lconv
->_W_positive_sign
= cloc_lconv
._W_positive_sign
;
1825 locinfo
->lconv
->_W_negative_sign
= cloc_lconv
._W_negative_sign
;
1829 if(!init_category_name("C", 1, locinfo
, LC_MONETARY
)) {
1834 if(locale_name
[LC_NUMERIC
] &&
1835 !init_category_name(locale_name
[LC_NUMERIC
],
1836 locale_len
[LC_NUMERIC
], locinfo
, LC_NUMERIC
)) {
1840 if(!category_needs_update(LC_NUMERIC
, old_locinfo
,
1841 locale_sname
[LC_NUMERIC
], cp
[LC_NUMERIC
])) {
1842 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_NUMERIC
);
1843 locinfo
->lconv_num_refcount
= old_locinfo
->lconv_num_refcount
;
1844 if(locinfo
->lconv_num_refcount
)
1845 InterlockedIncrement((LONG
*)locinfo
->lconv_num_refcount
);
1846 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1847 locinfo
->lconv
->decimal_point
= old_locinfo
->lconv
->decimal_point
;
1848 locinfo
->lconv
->thousands_sep
= old_locinfo
->lconv
->thousands_sep
;
1849 locinfo
->lconv
->grouping
= old_locinfo
->lconv
->grouping
;
1850 #if _MSVCR_VER >= 100
1851 locinfo
->lconv
->_W_decimal_point
= old_locinfo
->lconv
->_W_decimal_point
;
1852 locinfo
->lconv
->_W_thousands_sep
= old_locinfo
->lconv
->_W_thousands_sep
;
1855 } else if(locale_sname
[LC_NUMERIC
]) {
1856 if(!update_threadlocinfo_category(locale_sname
[LC_NUMERIC
],
1857 cp
[LC_NUMERIC
], locinfo
, LC_NUMERIC
)) {
1861 locinfo
->lconv_num_refcount
= malloc(sizeof(int));
1862 if(!locinfo
->lconv_num_refcount
) {
1866 *locinfo
->lconv_num_refcount
= 1;
1868 i
= GetLocaleInfoEx(locale_sname
[LC_NUMERIC
], LOCALE_SDECIMAL
1869 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1870 i
= WideCharToMultiByte(cp
[LC_NUMERIC
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1871 if(i
&& (locinfo
->lconv
->decimal_point
= malloc(i
)))
1872 WideCharToMultiByte(cp
[LC_NUMERIC
], 0, wbuf
, -1, locinfo
->lconv
->decimal_point
, i
, NULL
, NULL
);
1877 i
= GetLocaleInfoEx(locale_sname
[LC_NUMERIC
], LOCALE_STHOUSAND
1878 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1879 i
= WideCharToMultiByte(cp
[LC_NUMERIC
], 0, wbuf
, -1, NULL
, 0, NULL
, NULL
);
1880 if(i
&& (locinfo
->lconv
->thousands_sep
= malloc(i
)))
1881 WideCharToMultiByte(cp
[LC_NUMERIC
], 0, wbuf
, -1, locinfo
->lconv
->thousands_sep
, i
, NULL
, NULL
);
1886 i
= GetLocaleInfoEx(locale_sname
[LC_NUMERIC
], LOCALE_SGROUPING
1887 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1888 WideCharToMultiByte(cp
[LC_NUMERIC
], 0, wbuf
, -1, buf
, 256, NULL
, NULL
);
1890 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1891 if(i
&& (locinfo
->lconv
->grouping
= malloc(i
))) {
1892 for(i
=0; buf
[i
+1]==';'; i
+=2)
1893 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1894 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1896 locinfo
->lconv
->grouping
[i
/2+1] = 127;
1901 #if _MSVCR_VER >= 100
1902 i
= GetLocaleInfoEx(locale_sname
[LC_NUMERIC
], LOCALE_SDECIMAL
1903 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1904 if(i
&& (locinfo
->lconv
->_W_decimal_point
= malloc(i
* sizeof(wchar_t))))
1905 memcpy(locinfo
->lconv
->_W_decimal_point
, wbuf
, i
* sizeof(wchar_t));
1910 i
= GetLocaleInfoEx(locale_sname
[LC_NUMERIC
], LOCALE_STHOUSAND
1911 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1912 if(i
&& (locinfo
->lconv
->_W_thousands_sep
= malloc(i
* sizeof(wchar_t))))
1913 memcpy(locinfo
->lconv
->_W_thousands_sep
, wbuf
, i
* sizeof(wchar_t));
1919 if (locinfo
->lconv
!= &cloc_lconv
) {
1920 locinfo
->lconv
->decimal_point
= cloc_lconv
.decimal_point
;
1921 locinfo
->lconv
->thousands_sep
= cloc_lconv
.thousands_sep
;
1922 locinfo
->lconv
->grouping
= cloc_lconv
.grouping
;
1924 #if _MSVCR_VER >= 100
1925 locinfo
->lconv
->_W_decimal_point
= cloc_lconv
._W_decimal_point
;
1926 locinfo
->lconv
->_W_thousands_sep
= cloc_lconv
._W_thousands_sep
;
1930 if (!init_category_name("C", 1, locinfo
, LC_NUMERIC
)) {
1935 if(locale_name
[LC_TIME
] &&
1936 !init_category_name(locale_name
[LC_TIME
],
1937 locale_len
[LC_TIME
], locinfo
, LC_TIME
)) {
1941 if(!category_needs_update(LC_TIME
, old_locinfo
,
1942 locale_sname
[LC_TIME
], cp
[LC_TIME
])) {
1943 copy_threadlocinfo_category(locinfo
, old_locinfo
, LC_TIME
);
1944 locinfo
->lc_time_curr
= old_locinfo
->lc_time_curr
;
1945 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
1946 } else if(locale_sname
[LC_TIME
]) {
1947 if(!update_threadlocinfo_category(locale_sname
[LC_TIME
],
1948 cp
[LC_TIME
], locinfo
, LC_TIME
)) {
1952 locinfo
->lc_time_curr
= create_time_data(locale_sname
[LC_TIME
]);
1953 if(!locinfo
->lc_time_curr
) {
1957 if(!init_category_name("C", 1, locinfo
, LC_TIME
)) {
1960 locinfo
->lc_time_curr
= &cloc_time_data
;
1961 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
1964 for (i
= 0; i
< LC_MAX
; i
++)
1965 free(locale_sname
[i
]);
1970 free_locinfo(locinfo
);
1972 for (i
= 0; i
< LC_MAX
; i
++)
1973 free(locale_sname
[i
]);
1978 /*********************************************************************
1979 * _create_locale (MSVCRT.@)
1981 _locale_t CDECL
_create_locale(int category
, const char *locale
)
1985 loc
= malloc(sizeof(_locale_tstruct
));
1989 loc
->locinfo
= create_locinfo(category
, locale
, NULL
);
1995 loc
->mbcinfo
= create_mbcinfo(loc
->locinfo
->lc_id
[LC_CTYPE
].wCodePage
,
1996 loc
->locinfo
->lc_handle
[LC_CTYPE
], NULL
);
1998 free_locinfo(loc
->locinfo
);
2005 #if _MSVCR_VER >= 110
2006 /*********************************************************************
2007 * _wcreate_locale (MSVCR110.@)
2009 _locale_t CDECL
_wcreate_locale(int category
, const wchar_t *locale
)
2015 if(category
<LC_MIN
|| category
>LC_MAX
|| !locale
)
2018 len
= wcstombs(NULL
, locale
, 0);
2021 if(!(str
= malloc(++len
)))
2023 wcstombs(str
, locale
, len
);
2025 loc
= _create_locale(category
, str
);
2032 /*********************************************************************
2033 * setlocale (MSVCRT.@)
2035 char* CDECL
setlocale(int category
, const char* locale
)
2037 thread_data_t
*data
= msvcrt_get_thread_data();
2038 pthreadlocinfo locinfo
= get_locinfo(), newlocinfo
;
2040 if(category
<LC_MIN
|| category
>LC_MAX
)
2044 if(category
== LC_ALL
)
2045 return construct_lc_all(locinfo
);
2047 return locinfo
->lc_category
[category
].locale
;
2050 newlocinfo
= create_locinfo(category
, locale
, locinfo
);
2052 WARN("%d %s failed\n", category
, locale
);
2056 if(locale
[0] != 'C' || locale
[1] != '\0')
2057 initial_locale
= FALSE
;
2059 if(data
->locale_flags
& LOCALE_THREAD
)
2061 if(data
->locale_flags
& LOCALE_FREE
)
2062 free_locinfo(data
->locinfo
);
2063 data
->locinfo
= newlocinfo
;
2070 free_locinfo(MSVCRT_locale
->locinfo
);
2071 MSVCRT_locale
->locinfo
= newlocinfo
;
2073 MSVCRT___lc_codepage
= newlocinfo
->lc_codepage
;
2074 MSVCRT___lc_collate_cp
= newlocinfo
->lc_collate_cp
;
2075 MSVCRT___mb_cur_max
= newlocinfo
->mb_cur_max
;
2076 MSVCRT__pctype
= newlocinfo
->pctype
;
2077 for(i
=LC_MIN
; i
<=LC_MAX
; i
++)
2078 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2080 update_thread_locale(data
);
2083 if(category
== LC_ALL
)
2084 return construct_lc_all(data
->locinfo
);
2086 return data
->locinfo
->lc_category
[category
].locale
;
2089 /*********************************************************************
2090 * _wsetlocale (MSVCRT.@)
2092 wchar_t* CDECL
_wsetlocale(int category
, const wchar_t* wlocale
)
2094 static wchar_t current_lc_all
[MAX_LOCALE_LENGTH
];
2096 char *locale
= NULL
;
2101 len
= wcstombs(NULL
, wlocale
, 0);
2105 locale
= malloc(++len
);
2109 wcstombs(locale
, wlocale
, len
);
2113 ret
= setlocale(category
, locale
);
2116 if(ret
&& mbstowcs(current_lc_all
, ret
, MAX_LOCALE_LENGTH
)==-1)
2120 return ret
? current_lc_all
: NULL
;
2123 #if _MSVCR_VER >= 80
2124 /*********************************************************************
2125 * _configthreadlocale (MSVCR80.@)
2127 int CDECL
_configthreadlocale(int type
)
2129 thread_data_t
*data
= msvcrt_get_thread_data();
2132 ret
= (data
->locale_flags
& LOCALE_THREAD
? _ENABLE_PER_THREAD_LOCALE
:
2133 _DISABLE_PER_THREAD_LOCALE
);
2135 if(type
== _ENABLE_PER_THREAD_LOCALE
)
2136 data
->locale_flags
|= LOCALE_THREAD
;
2137 else if(type
== _DISABLE_PER_THREAD_LOCALE
)
2138 data
->locale_flags
&= ~LOCALE_THREAD
;
2146 BOOL
msvcrt_init_locale(void)
2151 MSVCRT_locale
= _create_locale(0, "C");
2156 MSVCRT___lc_codepage
= MSVCRT_locale
->locinfo
->lc_codepage
;
2157 MSVCRT___lc_collate_cp
= MSVCRT_locale
->locinfo
->lc_collate_cp
;
2158 MSVCRT___mb_cur_max
= MSVCRT_locale
->locinfo
->mb_cur_max
;
2159 MSVCRT__pctype
= MSVCRT_locale
->locinfo
->pctype
;
2160 for(i
=LC_MIN
; i
<=LC_MAX
; i
++)
2161 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2162 _setmbcp(_MB_CP_ANSI
);
2166 #if _MSVCR_VER >= 120
2167 /*********************************************************************
2168 * wctrans (MSVCR120.@)
2170 wctrans_t CDECL
wctrans(const char *property
)
2172 static const char str_tolower
[] = "tolower";
2173 static const char str_toupper
[] = "toupper";
2175 if(!strcmp(property
, str_tolower
))
2177 if(!strcmp(property
, str_toupper
))