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 MSVCRT__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
[MSVCRT_LC_MAX
- MSVCRT_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 static const MSVCRT_wchar_t sun
[] = {'S','u','n',0};
55 static const MSVCRT_wchar_t mon
[] = {'M','o','n',0};
56 static const MSVCRT_wchar_t tue
[] = {'T','u','e',0};
57 static const MSVCRT_wchar_t wed
[] = {'W','e','d',0};
58 static const MSVCRT_wchar_t thu
[] = {'T','h','u',0};
59 static const MSVCRT_wchar_t fri
[] = {'F','r','i',0};
60 static const MSVCRT_wchar_t sat
[] = {'S','a','t',0};
61 static const MSVCRT_wchar_t sunday
[] = {'S','u','n','d','a','y',0};
62 static const MSVCRT_wchar_t monday
[] = {'M','o','n','d','a','y',0};
63 static const MSVCRT_wchar_t tuesday
[] = {'T','u','e','s','d','a','y',0};
64 static const MSVCRT_wchar_t wednesday
[] = {'W','e','d','n','e','s','d','a','y',0};
65 static const MSVCRT_wchar_t thursday
[] = {'T','h','u','r','s','d','a','y',0};
66 static const MSVCRT_wchar_t friday
[] = {'F','r','i','d','a','y',0};
67 static const MSVCRT_wchar_t saturday
[] = {'S','a','t','u','r','d','a','y',0};
68 static const MSVCRT_wchar_t jan
[] = {'J','a','n',0};
69 static const MSVCRT_wchar_t feb
[] = {'F','e','b',0};
70 static const MSVCRT_wchar_t mar
[] = {'M','a','r',0};
71 static const MSVCRT_wchar_t apr
[] = {'A','p','r',0};
72 static const MSVCRT_wchar_t may
[] = {'M','a','y',0};
73 static const MSVCRT_wchar_t jun
[] = {'J','u','n',0};
74 static const MSVCRT_wchar_t jul
[] = {'J','u','l',0};
75 static const MSVCRT_wchar_t aug
[] = {'A','u','g',0};
76 static const MSVCRT_wchar_t sep
[] = {'S','e','p',0};
77 static const MSVCRT_wchar_t oct
[] = {'O','c','t',0};
78 static const MSVCRT_wchar_t nov
[] = {'N','o','v',0};
79 static const MSVCRT_wchar_t dec
[] = {'D','e','c',0};
80 static const MSVCRT_wchar_t january
[] = {'J','a','n','u','a','r','y',0};
81 static const MSVCRT_wchar_t february
[] = {'F','e','b','r','u','a','r','y',0};
82 static const MSVCRT_wchar_t march
[] = {'M','a','r','c','h',0};
83 static const MSVCRT_wchar_t april
[] = {'A','p','r','i','l',0};
84 static const MSVCRT_wchar_t june
[] = {'J','u','n','e',0};
85 static const MSVCRT_wchar_t july
[] = {'J','u','l','y',0};
86 static const MSVCRT_wchar_t august
[] = {'A','u','g','u','s','t',0};
87 static const MSVCRT_wchar_t september
[] = {'S','e','p','t','e','m','b','e','r',0};
88 static const MSVCRT_wchar_t october
[] = {'O','c','t','o','b','e','r',0};
89 static const MSVCRT_wchar_t november
[] = {'N','o','v','e','m','b','e','r',0};
90 static const MSVCRT_wchar_t december
[] = {'D','e','c','e','m','b','e','r',0};
91 static const MSVCRT_wchar_t am
[] = {'A','M',0};
92 static const MSVCRT_wchar_t pm
[] = {'P','M',0};
93 static const MSVCRT_wchar_t cloc_short_date
[] = {'M','M','/','d','d','/','y','y',0};
94 static const MSVCRT_wchar_t cloc_date
[] = {'d','d','d','d',',',' ','M','M','M','M',' ','d','d',',',' ','y','y','y','y',0};
95 static const MSVCRT_wchar_t cloc_time
[] = {'H','H',':','m','m',':','s','s',0};
98 static const MSVCRT_wchar_t en_us
[] = {'e','n','-','U','S',0};
101 MSVCRT___lc_time_data cloc_time_data
=
103 {{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
104 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
105 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
106 "January", "February", "March", "April", "May", "June", "July",
107 "August", "September", "October", "November", "December",
108 "AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"}},
110 MAKELCID(LANG_ENGLISH
, SORT_DEFAULT
),
113 {{sun
, mon
, tue
, wed
, thu
, fri
, sat
,
114 sunday
, monday
, tuesday
, wednesday
, thursday
, friday
, saturday
,
115 jan
, feb
, mar
, apr
, may
, jun
, jul
, aug
, sep
, oct
, nov
, dec
,
116 january
, february
, march
, april
, may
, june
, july
,
117 august
, september
, october
, november
, december
,
118 am
, pm
, cloc_short_date
, cloc_date
, cloc_time
}},
119 #if _MSVCR_VER >= 110
124 static const unsigned char cloc_clmap
[256] =
126 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
127 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
128 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
129 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
130 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
131 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
132 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
133 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
134 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
135 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
136 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
137 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
138 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
139 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
140 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
141 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
142 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
143 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
144 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
145 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
146 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
147 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
148 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
149 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
150 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
151 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
152 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
153 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
154 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
155 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
156 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
157 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
160 static const unsigned char cloc_cumap
[256] =
162 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
163 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
164 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
165 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
166 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
167 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
168 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
169 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
170 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
171 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
172 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
173 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
174 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
175 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
176 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
177 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
178 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
179 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
180 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
181 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
182 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
183 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
184 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
185 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
186 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
187 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
188 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
189 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
190 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
191 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
192 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
193 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
196 static char empty
[] = "";
197 static char cloc_dec_point
[] = ".";
198 #if _MSVCR_VER >= 100
199 static MSVCRT_wchar_t emptyW
[] = {0};
200 static MSVCRT_wchar_t cloc_dec_pointW
[] = {'.', 0};
202 static struct MSVCRT_lconv cloc_lconv
=
204 cloc_dec_point
, empty
, empty
, empty
, empty
, empty
, empty
, empty
, empty
, empty
,
205 CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
,
206 #if _MSVCR_VER >= 100
207 cloc_dec_pointW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
, emptyW
211 /* Friendly country strings & language names abbreviations. */
212 static const char * const _country_synonyms
[] =
215 "american english", "enu",
216 "american-english", "enu",
217 "english-american", "enu",
219 "english-usa", "enu",
223 "english-aus", "ena",
225 "french-belgian", "frb",
227 "english-can", "enc",
228 "french-canadian", "frc",
230 "chinese-simplified", "chs",
231 "chinese-traditional", "cht",
232 "dutch-belgian", "nlb",
236 "french-swiss", "frs",
238 "german-swiss", "des",
239 "italian-swiss", "its",
240 "german-austrian", "dea",
242 "portuguese-brazil", "ptb",
243 "spanish-mexican", "esm",
244 "norwegian-bokmal", "nor",
245 "norwegian-nynorsk", "non",
246 "spanish-modern", "esn"
249 /* INTERNAL: Map a synonym to an ISO code */
250 static void remap_synonym(char *name
)
253 for (i
= 0; i
< ARRAY_SIZE(_country_synonyms
); i
+= 2)
255 if (!MSVCRT__stricmp(_country_synonyms
[i
],name
))
257 TRACE(":Mapping synonym %s to %s\n",name
,_country_synonyms
[i
+1]);
258 strcpy(name
, _country_synonyms
[i
+1]);
264 /* Note: Flags are weighted in order of matching importance */
265 #define FOUND_SNAME 0x4
266 #define FOUND_LANGUAGE 0x2
267 #define FOUND_COUNTRY 0x1
270 char search_language
[MAX_ELEM_LEN
];
271 char search_country
[MAX_ELEM_LEN
];
272 DWORD found_codepage
;
273 unsigned int match_flags
;
274 LANGID found_lang_id
;
278 #define CONTINUE_LOOKING TRUE
279 #define STOP_LOOKING FALSE
281 /* INTERNAL: Get and compare locale info with a given string */
282 static int compare_info(LCID lcid
, DWORD flags
, char* buff
, const char* cmp
, BOOL exact
)
290 GetLocaleInfoA(lcid
, flags
|LOCALE_NOUSEROVERRIDE
, buff
, MAX_ELEM_LEN
);
294 /* Partial matches are only allowed on language/country names */
297 return !MSVCRT__stricmp(cmp
, buff
);
299 return !MSVCRT__strnicmp(cmp
, buff
, len
);
303 find_best_locale_proc(HMODULE hModule
, LPCSTR type
, LPCSTR name
, WORD LangID
, LONG_PTR lParam
)
305 locale_search_t
*res
= (locale_search_t
*)lParam
;
306 const LCID lcid
= MAKELCID(LangID
, SORT_DEFAULT
);
307 char buff
[MAX_ELEM_LEN
];
308 unsigned int flags
= 0;
310 if(PRIMARYLANGID(LangID
) == LANG_NEUTRAL
)
311 return CONTINUE_LOOKING
;
313 #if _MSVCR_VER >= 110
314 if (res
->allow_sname
&& compare_info(lcid
,LOCALE_SNAME
,buff
,res
->search_language
, TRUE
))
316 TRACE(":Found locale: %s->%s\n", res
->search_language
, buff
);
317 res
->match_flags
= FOUND_SNAME
;
318 res
->found_lang_id
= LangID
;
324 if (compare_info(lcid
,LOCALE_SISO639LANGNAME
,buff
,res
->search_language
, TRUE
) ||
325 compare_info(lcid
,LOCALE_SABBREVLANGNAME
,buff
,res
->search_language
, TRUE
) ||
326 compare_info(lcid
,LOCALE_SENGLANGUAGE
,buff
,res
->search_language
, FALSE
))
328 TRACE(":Found language: %s->%s\n", res
->search_language
, buff
);
329 flags
|= FOUND_LANGUAGE
;
331 else if (res
->match_flags
& FOUND_LANGUAGE
)
333 return CONTINUE_LOOKING
;
337 if (compare_info(lcid
,LOCALE_SISO3166CTRYNAME
,buff
,res
->search_country
, TRUE
) ||
338 compare_info(lcid
,LOCALE_SABBREVCTRYNAME
,buff
,res
->search_country
, TRUE
) ||
339 compare_info(lcid
,LOCALE_SENGCOUNTRY
,buff
,res
->search_country
, FALSE
))
341 TRACE("Found country:%s->%s\n", res
->search_country
, buff
);
342 flags
|= FOUND_COUNTRY
;
344 else if (!flags
&& (res
->match_flags
& FOUND_COUNTRY
))
346 return CONTINUE_LOOKING
;
349 if (flags
> res
->match_flags
)
351 /* Found a better match than previously */
352 res
->match_flags
= flags
;
353 res
->found_lang_id
= LangID
;
355 if ((flags
& (FOUND_LANGUAGE
| FOUND_COUNTRY
)) ==
356 (FOUND_LANGUAGE
| FOUND_COUNTRY
))
358 TRACE(":found exact locale match\n");
361 return CONTINUE_LOOKING
;
364 /* Internal: Find the LCID for a locale specification */
365 LCID
MSVCRT_locale_to_LCID(const char *locale
, unsigned short *codepage
, BOOL
*sname
)
367 thread_data_t
*data
= msvcrt_get_thread_data();
368 const char *cp
, *region
;
369 BOOL is_sname
= FALSE
;
373 if (!strcmp(locale
, data
->cached_locale
)) {
375 *codepage
= data
->cached_cp
;
377 *sname
= data
->cached_sname
;
378 return data
->cached_lcid
;
381 cp
= strchr(locale
, '.');
382 region
= strchr(locale
, '_');
384 if(!locale
[0] || (cp
== locale
&& !region
)) {
385 lcid
= GetUserDefaultLCID();
387 locale_search_t search
;
389 memset(&search
, 0, sizeof(locale_search_t
));
390 lstrcpynA(search
.search_language
, locale
, MAX_ELEM_LEN
);
392 lstrcpynA(search
.search_country
, region
+1, MAX_ELEM_LEN
);
393 if(region
-locale
< MAX_ELEM_LEN
)
394 search
.search_language
[region
-locale
] = '\0';
396 search
.search_country
[0] = '\0';
399 if(region
&& cp
-region
-1<MAX_ELEM_LEN
)
400 search
.search_country
[cp
-region
-1] = '\0';
401 if(cp
-locale
< MAX_ELEM_LEN
)
402 search
.search_language
[cp
-locale
] = '\0';
407 remap_synonym(search
.search_language
);
408 search
.allow_sname
= TRUE
;
411 if(!MSVCRT__stricmp(search
.search_country
, "China"))
412 strcpy(search
.search_country
, "People's Republic of China");
414 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR
)RT_STRING
,
415 (LPCSTR
)LOCALE_ILANGUAGE
,find_best_locale_proc
,
418 if (!search
.match_flags
)
421 /* If we were given something that didn't match, fail */
422 if (search
.search_language
[0] && !(search
.match_flags
& (FOUND_SNAME
| FOUND_LANGUAGE
)))
424 if (search
.search_country
[0] && !(search
.match_flags
& FOUND_COUNTRY
))
427 lcid
= MAKELCID(search
.found_lang_id
, SORT_DEFAULT
);
428 is_sname
= (search
.match_flags
& FOUND_SNAME
) != 0;
431 /* Obtain code page */
432 if (!cp
|| !cp
[1] || !MSVCRT__strnicmp(cp
, ".ACP", 4)) {
433 GetLocaleInfoW(lcid
, LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
434 (WCHAR
*)&locale_cp
, sizeof(DWORD
)/sizeof(WCHAR
));
436 locale_cp
= GetACP();
437 } else if (!MSVCRT__strnicmp(cp
, ".OCP", 4)) {
438 GetLocaleInfoW(lcid
, LOCALE_IDEFAULTCODEPAGE
| LOCALE_RETURN_NUMBER
,
439 (WCHAR
*)&locale_cp
, sizeof(DWORD
)/sizeof(WCHAR
));
440 #if _MSVCR_VER >= 140
441 } else if (!MSVCRT__strnicmp(cp
, ".UTF-8", 6)
442 || !MSVCRT__strnicmp(cp
, ".UTF8", 5)) {
446 locale_cp
= MSVCRT_atoi(cp
+ 1);
448 if (!IsValidCodePage(locale_cp
))
455 *codepage
= locale_cp
;
459 if (strlen(locale
) < sizeof(data
->cached_locale
)) {
460 strcpy(data
->cached_locale
, locale
);
461 data
->cached_lcid
= lcid
;
462 data
->cached_cp
= locale_cp
;
463 data
->cached_sname
= is_sname
;
469 static void copy_threadlocinfo_category(MSVCRT_pthreadlocinfo locinfo
,
470 const MSVCRT_threadlocinfo
*old_locinfo
, int category
)
472 locinfo
->lc_handle
[category
] = old_locinfo
->lc_handle
[category
];
473 locinfo
->lc_id
[category
] = old_locinfo
->lc_id
[category
];
474 if(!locinfo
->lc_category
[category
].locale
) {
475 locinfo
->lc_category
[category
].locale
= old_locinfo
->lc_category
[category
].locale
;
476 locinfo
->lc_category
[category
].refcount
= old_locinfo
->lc_category
[category
].refcount
;
477 InterlockedIncrement(locinfo
->lc_category
[category
].refcount
);
479 #if _MSVCR_VER >= 110
480 locinfo
->lc_name
[category
] = old_locinfo
->lc_name
[category
];
481 locinfo
->lc_category
[category
].wrefcount
= old_locinfo
->lc_category
[category
].wrefcount
;
482 if(locinfo
->lc_category
[category
].wrefcount
)
483 InterlockedIncrement(locinfo
->lc_category
[category
].wrefcount
);
487 static BOOL
init_category_name(const char *name
, int len
,
488 MSVCRT_pthreadlocinfo locinfo
, int category
)
490 locinfo
->lc_category
[category
].locale
= MSVCRT_malloc(len
+1);
491 locinfo
->lc_category
[category
].refcount
= MSVCRT_malloc(sizeof(int));
492 if(!locinfo
->lc_category
[category
].locale
493 || !locinfo
->lc_category
[category
].refcount
) {
494 MSVCRT_free(locinfo
->lc_category
[category
].locale
);
495 MSVCRT_free(locinfo
->lc_category
[category
].refcount
);
496 locinfo
->lc_category
[category
].locale
= NULL
;
497 locinfo
->lc_category
[category
].refcount
= NULL
;
501 memcpy(locinfo
->lc_category
[category
].locale
, name
, len
);
502 locinfo
->lc_category
[category
].locale
[len
] = 0;
503 *locinfo
->lc_category
[category
].refcount
= 1;
507 #if _MSVCR_VER >= 110
508 static inline BOOL
set_lc_locale_name(MSVCRT_pthreadlocinfo locinfo
, int cat
)
510 LCID lcid
= locinfo
->lc_handle
[cat
];
514 locinfo
->lc_category
[cat
].wrefcount
= MSVCRT_malloc(sizeof(int));
515 if(!locinfo
->lc_category
[cat
].wrefcount
)
517 *locinfo
->lc_category
[cat
].wrefcount
= 1;
519 len
= GetLocaleInfoW(lcid
, LOCALE_SISO639LANGNAME
520 |LOCALE_NOUSEROVERRIDE
, buf
, 100);
521 if(!len
) return FALSE
;
523 if(LocaleNameToLCID(buf
, 0) != lcid
)
524 len
= LCIDToLocaleName(lcid
, buf
, 100, 0);
526 if(!len
|| !(locinfo
->lc_name
[cat
] = MSVCRT_malloc(len
*sizeof(MSVCRT_wchar_t
))))
529 memcpy(locinfo
->lc_name
[cat
], buf
, len
*sizeof(MSVCRT_wchar_t
));
533 static inline BOOL
set_lc_locale_name(MSVCRT_pthreadlocinfo locinfo
, int cat
)
539 /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */
540 static BOOL
update_threadlocinfo_category(LCID lcid
, unsigned short cp
,
541 MSVCRT_pthreadlocinfo locinfo
, int category
)
545 if(GetLocaleInfoA(lcid
, LOCALE_ILANGUAGE
|LOCALE_NOUSEROVERRIDE
, buf
, 256)) {
548 locinfo
->lc_id
[category
].wLanguage
= 0;
550 locinfo
->lc_id
[category
].wLanguage
*= 16;
553 locinfo
->lc_id
[category
].wLanguage
+= *p
-'0';
555 locinfo
->lc_id
[category
].wLanguage
+= *p
-'a'+10;
560 locinfo
->lc_id
[category
].wCountry
=
561 locinfo
->lc_id
[category
].wLanguage
;
564 locinfo
->lc_id
[category
].wCodePage
= cp
;
566 locinfo
->lc_handle
[category
] = lcid
;
568 set_lc_locale_name(locinfo
, category
);
570 if(!locinfo
->lc_category
[category
].locale
) {
573 len
+= GetLocaleInfoA(lcid
, LOCALE_SENGLANGUAGE
574 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
576 len
+= GetLocaleInfoA(lcid
, LOCALE_SENGCOUNTRY
577 |LOCALE_NOUSEROVERRIDE
, &buf
[len
], 256-len
);
579 MSVCRT_sprintf(buf
+len
, "%d", cp
);
580 len
+= strlen(buf
+len
);
582 return init_category_name(buf
, len
, locinfo
, category
);
588 /*********************************************************************
589 * _lock_locales (UCRTBASE.@)
591 void CDECL
_lock_locales(void)
593 _lock(_SETLOCALE_LOCK
);
596 /*********************************************************************
597 * _unlock_locales (UCRTBASE.@)
599 void CDECL
_unlock_locales(void)
601 _unlock(_SETLOCALE_LOCK
);
604 static void CDECL
grab_locinfo(MSVCRT_pthreadlocinfo locinfo
)
608 InterlockedIncrement(&locinfo
->refcount
);
609 for(i
=MSVCRT_LC_MIN
+1; i
<=MSVCRT_LC_MAX
; i
++)
611 InterlockedIncrement(locinfo
->lc_category
[i
].refcount
);
612 if(locinfo
->lc_category
[i
].wrefcount
)
613 InterlockedIncrement(locinfo
->lc_category
[i
].wrefcount
);
615 if(locinfo
->lconv_intl_refcount
)
616 InterlockedIncrement(locinfo
->lconv_intl_refcount
);
617 if(locinfo
->lconv_num_refcount
)
618 InterlockedIncrement(locinfo
->lconv_num_refcount
);
619 if(locinfo
->lconv_mon_refcount
)
620 InterlockedIncrement(locinfo
->lconv_mon_refcount
);
621 if(locinfo
->ctype1_refcount
)
622 InterlockedIncrement(locinfo
->ctype1_refcount
);
623 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
626 static void CDECL
update_thread_locale(thread_data_t
*data
)
628 if((data
->locale_flags
& LOCALE_FREE
) && ((data
->locale_flags
& LOCALE_THREAD
) ||
629 (data
->locinfo
== MSVCRT_locale
->locinfo
&& data
->mbcinfo
== MSVCRT_locale
->mbcinfo
)))
632 if(data
->locale_flags
& LOCALE_FREE
)
634 free_locinfo(data
->locinfo
);
635 free_mbcinfo(data
->mbcinfo
);
639 data
->locinfo
= MSVCRT_locale
->locinfo
;
640 grab_locinfo(data
->locinfo
);
644 data
->mbcinfo
= MSVCRT_locale
->mbcinfo
;
645 InterlockedIncrement(&data
->mbcinfo
->refcount
);
646 _unlock(_MB_CP_LOCK
);
648 data
->locale_flags
|= LOCALE_FREE
;
651 /* INTERNAL: returns threadlocinfo struct */
652 MSVCRT_pthreadlocinfo CDECL
get_locinfo(void) {
653 thread_data_t
*data
= msvcrt_get_thread_data();
654 update_thread_locale(data
);
655 return data
->locinfo
;
658 /* INTERNAL: returns pthreadmbcinfo struct */
659 MSVCRT_pthreadmbcinfo CDECL
get_mbcinfo(void) {
660 thread_data_t
*data
= msvcrt_get_thread_data();
661 update_thread_locale(data
);
662 return data
->mbcinfo
;
665 /* INTERNAL: constructs string returned by setlocale */
666 static inline char* construct_lc_all(MSVCRT_pthreadlocinfo locinfo
) {
667 static char current_lc_all
[MAX_LOCALE_LENGTH
];
671 for(i
=MSVCRT_LC_MIN
+1; i
<MSVCRT_LC_MAX
; i
++) {
672 if(strcmp(locinfo
->lc_category
[i
].locale
,
673 locinfo
->lc_category
[i
+1].locale
))
678 return locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
;
680 MSVCRT_sprintf(current_lc_all
,
681 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
682 locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
,
683 locinfo
->lc_category
[MSVCRT_LC_CTYPE
].locale
,
684 locinfo
->lc_category
[MSVCRT_LC_MONETARY
].locale
,
685 locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].locale
,
686 locinfo
->lc_category
[MSVCRT_LC_TIME
].locale
);
688 return current_lc_all
;
692 /*********************************************************************
693 * _Getdays (MSVCRT.@)
695 char* CDECL
_Getdays(void)
697 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
698 int i
, len
, size
= 0;
704 size
+= strlen(cur
->str
.names
.short_wday
[i
]) + 1;
705 size
+= strlen(cur
->str
.names
.wday
[i
]) + 1;
707 out
= MSVCRT_malloc(size
+1);
714 len
= strlen(cur
->str
.names
.short_wday
[i
]);
715 memcpy(&out
[size
], cur
->str
.names
.short_wday
[i
], len
);
719 len
= strlen(cur
->str
.names
.wday
[i
]);
720 memcpy(&out
[size
], cur
->str
.names
.wday
[i
], len
);
728 #if _MSVCR_VER >= 110
729 /*********************************************************************
730 * _W_Getdays (MSVCR110.@)
732 MSVCRT_wchar_t
* CDECL
_W_Getdays(void)
734 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
736 int i
, len
, size
= 0;
741 size
+= MSVCRT_wcslen(cur
->wstr
.names
.short_wday
[i
]) + 1;
742 size
+= MSVCRT_wcslen(cur
->wstr
.names
.wday
[i
]) + 1;
744 out
= MSVCRT_malloc((size
+1)*sizeof(*out
));
751 len
= MSVCRT_wcslen(cur
->wstr
.names
.short_wday
[i
]);
752 memcpy(&out
[size
], cur
->wstr
.names
.short_wday
[i
], len
*sizeof(*out
));
756 len
= MSVCRT_wcslen(cur
->wstr
.names
.wday
[i
]);
757 memcpy(&out
[size
], cur
->wstr
.names
.wday
[i
], len
*sizeof(*out
));
766 /*********************************************************************
767 * _Getmonths (MSVCRT.@)
769 char* CDECL
_Getmonths(void)
771 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
772 int i
, len
, size
= 0;
777 for(i
=0; i
<12; i
++) {
778 size
+= strlen(cur
->str
.names
.short_mon
[i
]) + 1;
779 size
+= strlen(cur
->str
.names
.mon
[i
]) + 1;
781 out
= MSVCRT_malloc(size
+1);
786 for(i
=0; i
<12; i
++) {
788 len
= strlen(cur
->str
.names
.short_mon
[i
]);
789 memcpy(&out
[size
], cur
->str
.names
.short_mon
[i
], len
);
793 len
= strlen(cur
->str
.names
.mon
[i
]);
794 memcpy(&out
[size
], cur
->str
.names
.mon
[i
], len
);
802 #if _MSVCR_VER >= 110
803 /*********************************************************************
804 * _W_Getmonths (MSVCR110.@)
806 MSVCRT_wchar_t
* CDECL
_W_Getmonths(void)
808 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
810 int i
, len
, size
= 0;
814 for(i
=0; i
<12; i
++) {
815 size
+= MSVCRT_wcslen(cur
->wstr
.names
.short_mon
[i
]) + 1;
816 size
+= MSVCRT_wcslen(cur
->wstr
.names
.mon
[i
]) + 1;
818 out
= MSVCRT_malloc((size
+1)*sizeof(*out
));
823 for(i
=0; i
<12; i
++) {
825 len
= MSVCRT_wcslen(cur
->wstr
.names
.short_mon
[i
]);
826 memcpy(&out
[size
], cur
->wstr
.names
.short_mon
[i
], len
*sizeof(*out
));
830 len
= MSVCRT_wcslen(cur
->wstr
.names
.mon
[i
]);
831 memcpy(&out
[size
], cur
->wstr
.names
.mon
[i
], len
*sizeof(*out
));
840 /*********************************************************************
841 * _Gettnames (MSVCRT.@)
843 void* CDECL
_Gettnames(void)
845 MSVCRT___lc_time_data
*ret
, *cur
= get_locinfo()->lc_time_curr
;
846 unsigned int i
, len
, size
= sizeof(MSVCRT___lc_time_data
);
850 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++)
851 size
+= strlen(cur
->str
.str
[i
])+1;
853 ret
= MSVCRT_malloc(size
);
856 memcpy(ret
, cur
, sizeof(*ret
));
859 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++) {
860 len
= strlen(cur
->str
.str
[i
])+1;
861 memcpy(&ret
->data
[size
], cur
->str
.str
[i
], len
);
862 ret
->str
.str
[i
] = &ret
->data
[size
];
869 #if _MSVCR_VER >= 110
870 /*********************************************************************
871 * _W_Gettnames (MSVCR110.@)
873 void* CDECL
_W_Gettnames(void)
879 /*********************************************************************
880 * __crtLCMapStringA (MSVCRT.@)
882 int CDECL
__crtLCMapStringA(
883 LCID lcid
, DWORD mapflags
, const char* src
, int srclen
, char* dst
,
884 int dstlen
, unsigned int codepage
, int xflag
886 WCHAR buf_in
[32], *in
= buf_in
;
887 WCHAR buf_out
[32], *out
= buf_out
;
888 int in_len
, out_len
, r
;
890 TRACE("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
891 lcid
, mapflags
, src
, srclen
, dst
, dstlen
, codepage
, xflag
);
893 in_len
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, NULL
, 0);
894 if (!in_len
) return 0;
895 if (in_len
> ARRAY_SIZE(buf_in
))
897 in
= MSVCRT_malloc(in_len
* sizeof(WCHAR
));
901 r
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, in
, in_len
);
904 if (mapflags
& LCMAP_SORTKEY
)
906 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, (WCHAR
*)dst
, dstlen
);
910 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, NULL
, 0);
913 if (r
> ARRAY_SIZE(buf_out
))
915 out
= MSVCRT_malloc(r
* sizeof(WCHAR
));
923 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, out
, out_len
);
926 r
= WideCharToMultiByte(codepage
, 0, out
, out_len
, dst
, dstlen
, NULL
, NULL
);
929 if (in
!= buf_in
) MSVCRT_free(in
);
930 if (out
!= buf_out
) MSVCRT_free(out
);
934 /*********************************************************************
935 * __crtLCMapStringW (MSVCRT.@)
937 int CDECL
__crtLCMapStringW(LCID lcid
, DWORD mapflags
, const MSVCRT_wchar_t
*src
,
938 int srclen
, MSVCRT_wchar_t
*dst
, int dstlen
, unsigned int codepage
, int xflag
)
940 FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
941 lcid
, mapflags
, debugstr_w(src
), srclen
, dst
, dstlen
, codepage
, xflag
);
943 return LCMapStringW(lcid
, mapflags
, src
, srclen
, dst
, dstlen
);
946 /*********************************************************************
947 * __crtCompareStringA (MSVCRT.@)
949 int CDECL
__crtCompareStringA( LCID lcid
, DWORD flags
, const char *src1
, int len1
,
950 const char *src2
, int len2
)
952 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
953 lcid
, flags
, debugstr_a(src1
), len1
, debugstr_a(src2
), len2
);
954 /* FIXME: probably not entirely right */
955 return CompareStringA( lcid
, flags
, src1
, len1
, src2
, len2
);
958 /*********************************************************************
959 * __crtCompareStringW (MSVCRT.@)
961 int CDECL
__crtCompareStringW( LCID lcid
, DWORD flags
, const MSVCRT_wchar_t
*src1
, int len1
,
962 const MSVCRT_wchar_t
*src2
, int len2
)
964 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
965 lcid
, flags
, debugstr_w(src1
), len1
, debugstr_w(src2
), len2
);
966 /* FIXME: probably not entirely right */
967 return CompareStringW( lcid
, flags
, src1
, len1
, src2
, len2
);
970 /*********************************************************************
971 * __crtGetLocaleInfoW (MSVCRT.@)
973 int CDECL
__crtGetLocaleInfoW( LCID lcid
, LCTYPE type
, MSVCRT_wchar_t
*buffer
, int len
)
975 FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid
, type
, buffer
, len
);
976 /* FIXME: probably not entirely right */
977 return GetLocaleInfoW( lcid
, type
, buffer
, len
);
980 #if _MSVCR_VER >= 110
981 /*********************************************************************
982 * __crtGetLocaleInfoEx (MSVC110.@)
984 int CDECL
__crtGetLocaleInfoEx( const WCHAR
*locale
, LCTYPE type
, MSVCRT_wchar_t
*buffer
, int len
)
986 TRACE("(%s, %x, %p, %d)\n", debugstr_w(locale
), type
, buffer
, len
);
987 return GetLocaleInfoEx(locale
, type
, buffer
, len
);
991 /*********************************************************************
992 * __crtGetStringTypeW(MSVCRT.@)
994 * This function was accepting different number of arguments in older
995 * versions of msvcrt.
997 BOOL CDECL
__crtGetStringTypeW(DWORD unk
, DWORD type
,
998 MSVCRT_wchar_t
*buffer
, int len
, WORD
*out
)
1000 FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
1001 unk
, type
, buffer
, len
, out
);
1003 return GetStringTypeW(type
, buffer
, len
, out
);
1006 /*********************************************************************
1007 * localeconv (MSVCRT.@)
1009 struct MSVCRT_lconv
* CDECL
MSVCRT_localeconv(void)
1011 return get_locinfo()->lconv
;
1014 /*********************************************************************
1015 * __lconv_init (MSVCRT.@)
1017 int CDECL
__lconv_init(void)
1019 /* this is used to make chars unsigned */
1020 cloc_lconv
.int_frac_digits
= (char)UCHAR_MAX
;
1021 cloc_lconv
.frac_digits
= (char)UCHAR_MAX
;
1022 cloc_lconv
.p_cs_precedes
= (char)UCHAR_MAX
;
1023 cloc_lconv
.p_sep_by_space
= (char)UCHAR_MAX
;
1024 cloc_lconv
.n_cs_precedes
= (char)UCHAR_MAX
;
1025 cloc_lconv
.n_sep_by_space
= (char)UCHAR_MAX
;
1026 cloc_lconv
.p_sign_posn
= (char)UCHAR_MAX
;
1027 cloc_lconv
.n_sign_posn
= (char)UCHAR_MAX
;
1031 /*********************************************************************
1032 * ___lc_handle_func (MSVCRT.@)
1034 LCID
* CDECL
___lc_handle_func(void)
1036 return (LCID
*)get_locinfo()->lc_handle
;
1039 #if _MSVCR_VER >= 110
1040 /*********************************************************************
1041 * ___lc_locale_name_func (MSVCR110.@)
1043 MSVCRT_wchar_t
** CDECL
___lc_locale_name_func(void)
1045 return get_locinfo()->lc_name
;
1049 /*********************************************************************
1050 * ___lc_codepage_func (MSVCRT.@)
1052 unsigned int CDECL
___lc_codepage_func(void)
1054 return get_locinfo()->lc_codepage
;
1057 /*********************************************************************
1058 * ___lc_collate_cp_func (MSVCRT.@)
1060 int CDECL
___lc_collate_cp_func(void)
1062 return get_locinfo()->lc_collate_cp
;
1065 /* INTERNAL: frees MSVCRT_pthreadlocinfo struct */
1066 void free_locinfo(MSVCRT_pthreadlocinfo locinfo
)
1073 for(i
=MSVCRT_LC_MIN
+1; i
<=MSVCRT_LC_MAX
; i
++) {
1074 if(!locinfo
->lc_category
[i
].refcount
1075 || !InterlockedDecrement(locinfo
->lc_category
[i
].refcount
)) {
1076 MSVCRT_free(locinfo
->lc_category
[i
].locale
);
1077 MSVCRT_free(locinfo
->lc_category
[i
].refcount
);
1079 if(!locinfo
->lc_category
[i
].wrefcount
1080 || !InterlockedDecrement(locinfo
->lc_category
[i
].wrefcount
)) {
1081 #if _MSVCR_VER >= 110
1082 MSVCRT_free(locinfo
->lc_name
[i
]);
1084 MSVCRT_free(locinfo
->lc_category
[i
].wrefcount
);
1088 if(locinfo
->lconv_num_refcount
1089 && !InterlockedDecrement(locinfo
->lconv_num_refcount
)) {
1090 MSVCRT_free(locinfo
->lconv
->decimal_point
);
1091 MSVCRT_free(locinfo
->lconv
->thousands_sep
);
1092 MSVCRT_free(locinfo
->lconv
->grouping
);
1093 #if _MSVCR_VER >= 100
1094 MSVCRT_free(locinfo
->lconv
->_W_decimal_point
);
1095 MSVCRT_free(locinfo
->lconv
->_W_thousands_sep
);
1097 MSVCRT_free(locinfo
->lconv_num_refcount
);
1099 if(locinfo
->lconv_mon_refcount
1100 && !InterlockedDecrement(locinfo
->lconv_mon_refcount
)) {
1101 MSVCRT_free(locinfo
->lconv
->int_curr_symbol
);
1102 MSVCRT_free(locinfo
->lconv
->currency_symbol
);
1103 MSVCRT_free(locinfo
->lconv
->mon_decimal_point
);
1104 MSVCRT_free(locinfo
->lconv
->mon_thousands_sep
);
1105 MSVCRT_free(locinfo
->lconv
->mon_grouping
);
1106 MSVCRT_free(locinfo
->lconv
->positive_sign
);
1107 MSVCRT_free(locinfo
->lconv
->negative_sign
);
1108 #if _MSVCR_VER >= 100
1109 MSVCRT_free(locinfo
->lconv
->_W_int_curr_symbol
);
1110 MSVCRT_free(locinfo
->lconv
->_W_currency_symbol
);
1111 MSVCRT_free(locinfo
->lconv
->_W_mon_decimal_point
);
1112 MSVCRT_free(locinfo
->lconv
->_W_mon_thousands_sep
);
1113 MSVCRT_free(locinfo
->lconv
->_W_positive_sign
);
1114 MSVCRT_free(locinfo
->lconv
->_W_negative_sign
);
1116 MSVCRT_free(locinfo
->lconv_mon_refcount
);
1118 if(locinfo
->lconv_intl_refcount
1119 && !InterlockedDecrement(locinfo
->lconv_intl_refcount
)) {
1120 MSVCRT_free(locinfo
->lconv_intl_refcount
);
1121 MSVCRT_free(locinfo
->lconv
);
1124 if(locinfo
->ctype1_refcount
1125 && !InterlockedDecrement(locinfo
->ctype1_refcount
)) {
1126 MSVCRT_free(locinfo
->ctype1_refcount
);
1127 MSVCRT_free(locinfo
->ctype1
);
1128 MSVCRT_free((void*)locinfo
->pclmap
);
1129 MSVCRT_free((void*)locinfo
->pcumap
);
1132 if(locinfo
->lc_time_curr
&& !InterlockedDecrement(&locinfo
->lc_time_curr
->refcount
)
1133 && locinfo
->lc_time_curr
!= &cloc_time_data
)
1134 MSVCRT_free(locinfo
->lc_time_curr
);
1136 if(InterlockedDecrement(&locinfo
->refcount
))
1139 MSVCRT_free(locinfo
);
1142 /* INTERNAL: frees MSVCRT_pthreadmbcinfo struct */
1143 void free_mbcinfo(MSVCRT_pthreadmbcinfo mbcinfo
)
1148 if(InterlockedDecrement(&mbcinfo
->refcount
))
1151 MSVCRT_free(mbcinfo
);
1154 MSVCRT__locale_t CDECL
get_current_locale_noalloc(MSVCRT__locale_t locale
)
1156 thread_data_t
*data
= msvcrt_get_thread_data();
1158 update_thread_locale(data
);
1159 locale
->locinfo
= data
->locinfo
;
1160 locale
->mbcinfo
= data
->mbcinfo
;
1162 grab_locinfo(locale
->locinfo
);
1163 InterlockedIncrement(&locale
->mbcinfo
->refcount
);
1167 void CDECL
free_locale_noalloc(MSVCRT__locale_t locale
)
1169 free_locinfo(locale
->locinfo
);
1170 free_mbcinfo(locale
->mbcinfo
);
1173 /*********************************************************************
1174 * _get_current_locale (MSVCRT.@)
1176 MSVCRT__locale_t CDECL
MSVCRT__get_current_locale(void)
1178 MSVCRT__locale_t loc
= MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct
));
1182 return get_current_locale_noalloc(loc
);
1185 /*********************************************************************
1186 * _free_locale (MSVCRT.@)
1188 void CDECL
MSVCRT__free_locale(MSVCRT__locale_t locale
)
1193 free_locale_noalloc(locale
);
1194 MSVCRT_free(locale
);
1197 static inline BOOL
category_needs_update(int cat
,
1198 const MSVCRT_threadlocinfo
*locinfo
, LCID lcid
, unsigned short cp
)
1200 if(!locinfo
) return TRUE
;
1201 return lcid
!=locinfo
->lc_handle
[cat
] || cp
!=locinfo
->lc_id
[cat
].wCodePage
;
1204 static MSVCRT___lc_time_data
* create_time_data(LCID lcid
)
1206 static const DWORD time_data
[] = {
1207 LOCALE_SABBREVDAYNAME7
, LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
,
1208 LOCALE_SABBREVDAYNAME3
, LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
,
1209 LOCALE_SABBREVDAYNAME6
,
1210 LOCALE_SDAYNAME7
, LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
1211 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
,
1212 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
1213 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
1214 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
1215 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
1216 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
, LOCALE_SMONTHNAME4
,
1217 LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
, LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
,
1218 LOCALE_SMONTHNAME9
, LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
1219 LOCALE_S1159
, LOCALE_S2359
,
1220 LOCALE_SSHORTDATE
, LOCALE_SLONGDATE
,
1224 MSVCRT___lc_time_data
*cur
;
1227 size
= sizeof(MSVCRT___lc_time_data
);
1228 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1229 ret
= GetLocaleInfoA(lcid
, time_data
[i
], NULL
, 0);
1234 ret
= GetLocaleInfoW(lcid
, time_data
[i
], NULL
, 0);
1237 size
+= ret
*sizeof(MSVCRT_wchar_t
);
1239 #if _MSVCR_VER >= 110
1240 size
+= LCIDToLocaleName(lcid
, NULL
, 0, 0)*sizeof(MSVCRT_wchar_t
);
1243 cur
= MSVCRT_malloc(size
);
1248 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1249 cur
->str
.str
[i
] = &cur
->data
[ret
];
1250 ret
+= GetLocaleInfoA(lcid
, time_data
[i
], &cur
->data
[ret
], size
-ret
);
1252 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1253 cur
->wstr
.wstr
[i
] = (MSVCRT_wchar_t
*)&cur
->data
[ret
];
1254 ret
+= GetLocaleInfoW(lcid
, time_data
[i
],
1255 (MSVCRT_wchar_t
*)&cur
->data
[ret
], size
-ret
)*sizeof(MSVCRT_wchar_t
);
1257 #if _MSVCR_VER >= 110
1258 cur
->locname
= (MSVCRT_wchar_t
*)&cur
->data
[ret
];
1259 LCIDToLocaleName(lcid
, (MSVCRT_wchar_t
*)&cur
->data
[ret
], (size
-ret
)/sizeof(MSVCRT_wchar_t
), 0);
1269 static MSVCRT_pthreadlocinfo
create_locinfo(int category
,
1270 const char *locale
, const MSVCRT_threadlocinfo
*old_locinfo
)
1272 static const char collate
[] = "COLLATE=";
1273 static const char ctype
[] = "CTYPE=";
1274 static const char monetary
[] = "MONETARY=";
1275 static const char numeric
[] = "NUMERIC=";
1276 static const char time
[] = "TIME=";
1278 MSVCRT_pthreadlocinfo locinfo
;
1279 LCID lcid
[6] = { 0 };
1280 unsigned short cp
[6] = { 0 };
1281 const char *locale_name
[6] = { 0 };
1282 int val
, locale_len
[6] = { 0 };
1285 #if _MSVCR_VER >= 100
1286 MSVCRT_wchar_t wbuf
[256];
1290 TRACE("(%d %s)\n", category
, locale
);
1292 if(category
<MSVCRT_LC_MIN
|| category
>MSVCRT_LC_MAX
|| !locale
)
1295 if(locale
[0]=='C' && !locale
[1]) {
1298 } else if (locale
[0] == 'L' && locale
[1] == 'C' && locale
[2] == '_') {
1302 locale
+= 3; /* LC_ */
1303 if(!memcmp(locale
, collate
, sizeof(collate
)-1)) {
1304 i
= MSVCRT_LC_COLLATE
;
1305 locale
+= sizeof(collate
)-1;
1306 } else if(!memcmp(locale
, ctype
, sizeof(ctype
)-1)) {
1307 i
= MSVCRT_LC_CTYPE
;
1308 locale
+= sizeof(ctype
)-1;
1309 } else if(!memcmp(locale
, monetary
, sizeof(monetary
)-1)) {
1310 i
= MSVCRT_LC_MONETARY
;
1311 locale
+= sizeof(monetary
)-1;
1312 } else if(!memcmp(locale
, numeric
, sizeof(numeric
)-1)) {
1313 i
= MSVCRT_LC_NUMERIC
;
1314 locale
+= sizeof(numeric
)-1;
1315 } else if(!memcmp(locale
, time
, sizeof(time
)-1)) {
1317 locale
+= sizeof(time
)-1;
1321 p
= strchr(locale
, ';');
1322 if(locale
[0]=='C' && (locale
[1]==';' || locale
[1]=='\0')) {
1326 memcpy(buf
, locale
, p
-locale
);
1327 buf
[p
-locale
] = '\0';
1328 lcid
[i
] = MSVCRT_locale_to_LCID(buf
, &cp
[i
], &sname
);
1330 locale_name
[i
] = locale
;
1331 locale_len
[i
] = p
-locale
;
1334 lcid
[i
] = MSVCRT_locale_to_LCID(locale
, &cp
[i
], &sname
);
1336 locale_name
[i
] = locale
;
1337 locale_len
[i
] = strlen(locale
);
1344 if(!p
|| *(p
+1)!='L' || *(p
+2)!='C' || *(p
+3)!='_')
1350 lcid
[0] = MSVCRT_locale_to_LCID(locale
, &cp
[0], &sname
);
1354 locale_name
[0] = locale
;
1355 locale_len
[0] = strlen(locale
);
1358 for(i
=1; i
<6; i
++) {
1361 locale_name
[i
] = locale_name
[0];
1362 locale_len
[i
] = locale_len
[0];
1366 for(i
=1; i
<6; i
++) {
1367 if(category
!=MSVCRT_LC_ALL
&& category
!=i
) {
1369 lcid
[i
] = old_locinfo
->lc_handle
[i
];
1370 cp
[i
] = old_locinfo
->lc_id
[i
].wCodePage
;
1378 locinfo
= MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo
));
1382 memset(locinfo
, 0, sizeof(MSVCRT_threadlocinfo
));
1383 locinfo
->refcount
= 1;
1385 if(locale_name
[MSVCRT_LC_COLLATE
] &&
1386 !init_category_name(locale_name
[MSVCRT_LC_COLLATE
],
1387 locale_len
[MSVCRT_LC_COLLATE
], locinfo
, MSVCRT_LC_COLLATE
)) {
1388 free_locinfo(locinfo
);
1392 if(!category_needs_update(MSVCRT_LC_COLLATE
, old_locinfo
,
1393 lcid
[MSVCRT_LC_COLLATE
], cp
[MSVCRT_LC_COLLATE
])) {
1394 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_COLLATE
);
1395 locinfo
->lc_collate_cp
= old_locinfo
->lc_collate_cp
;
1396 } else if(lcid
[MSVCRT_LC_COLLATE
]) {
1397 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_COLLATE
],
1398 cp
[MSVCRT_LC_COLLATE
], locinfo
, MSVCRT_LC_COLLATE
)) {
1399 free_locinfo(locinfo
);
1403 locinfo
->lc_collate_cp
= locinfo
->lc_id
[MSVCRT_LC_COLLATE
].wCodePage
;
1405 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_COLLATE
)) {
1406 free_locinfo(locinfo
);
1411 if(locale_name
[MSVCRT_LC_CTYPE
] &&
1412 !init_category_name(locale_name
[MSVCRT_LC_CTYPE
],
1413 locale_len
[MSVCRT_LC_CTYPE
], locinfo
, MSVCRT_LC_CTYPE
)) {
1414 free_locinfo(locinfo
);
1418 if(!category_needs_update(MSVCRT_LC_CTYPE
, old_locinfo
,
1419 lcid
[MSVCRT_LC_CTYPE
], cp
[MSVCRT_LC_CTYPE
])) {
1420 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_CTYPE
);
1421 locinfo
->lc_codepage
= old_locinfo
->lc_codepage
;
1422 locinfo
->lc_clike
= old_locinfo
->lc_clike
;
1423 locinfo
->mb_cur_max
= old_locinfo
->mb_cur_max
;
1424 locinfo
->ctype1
= old_locinfo
->ctype1
;
1425 locinfo
->ctype1_refcount
= old_locinfo
->ctype1_refcount
;
1426 locinfo
->pctype
= old_locinfo
->pctype
;
1427 locinfo
->pclmap
= old_locinfo
->pclmap
;
1428 locinfo
->pcumap
= old_locinfo
->pcumap
;
1429 if(locinfo
->ctype1_refcount
)
1430 InterlockedIncrement(locinfo
->ctype1_refcount
);
1431 } else if(lcid
[MSVCRT_LC_CTYPE
]) {
1435 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_CTYPE
],
1436 cp
[MSVCRT_LC_CTYPE
], locinfo
, MSVCRT_LC_CTYPE
)) {
1437 free_locinfo(locinfo
);
1441 locinfo
->lc_codepage
= locinfo
->lc_id
[MSVCRT_LC_CTYPE
].wCodePage
;
1442 locinfo
->lc_clike
= 1;
1443 if(!GetCPInfo(locinfo
->lc_codepage
, &cp_info
)) {
1444 free_locinfo(locinfo
);
1447 locinfo
->mb_cur_max
= cp_info
.MaxCharSize
;
1449 locinfo
->ctype1_refcount
= MSVCRT_malloc(sizeof(int));
1450 if(!locinfo
->ctype1_refcount
) {
1451 free_locinfo(locinfo
);
1454 *locinfo
->ctype1_refcount
= 1;
1456 locinfo
->ctype1
= MSVCRT_malloc(sizeof(short[257]));
1457 locinfo
->pclmap
= MSVCRT_malloc(sizeof(char[256]));
1458 locinfo
->pcumap
= MSVCRT_malloc(sizeof(char[256]));
1459 if(!locinfo
->ctype1
|| !locinfo
->pclmap
|| !locinfo
->pcumap
) {
1460 free_locinfo(locinfo
);
1464 locinfo
->ctype1
[0] = 0;
1465 locinfo
->pctype
= locinfo
->ctype1
+1;
1467 buf
[1] = buf
[2] = '\0';
1468 for(i
=1; i
<257; i
++) {
1471 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1472 locinfo
->ctype1
[i
] = 0;
1474 GetStringTypeA(lcid
[MSVCRT_LC_CTYPE
], CT_CTYPE1
, buf
,
1475 1, locinfo
->ctype1
+i
);
1478 for(i
=0; cp_info
.LeadByte
[i
+1]!=0; i
+=2)
1479 for(j
=cp_info
.LeadByte
[i
]; j
<=cp_info
.LeadByte
[i
+1]; j
++)
1480 locinfo
->ctype1
[j
+1] |= MSVCRT__LEADBYTE
;
1482 for(i
=0; i
<256; i
++) {
1483 if(locinfo
->pctype
[i
] & MSVCRT__LEADBYTE
)
1489 LCMapStringA(lcid
[MSVCRT_LC_CTYPE
], LCMAP_LOWERCASE
, buf
, 256,
1490 (char*)locinfo
->pclmap
, 256);
1491 LCMapStringA(lcid
[MSVCRT_LC_CTYPE
], LCMAP_UPPERCASE
, buf
, 256,
1492 (char*)locinfo
->pcumap
, 256);
1494 locinfo
->lc_clike
= 1;
1495 locinfo
->mb_cur_max
= 1;
1496 locinfo
->pctype
= MSVCRT__ctype
+1;
1497 locinfo
->pclmap
= cloc_clmap
;
1498 locinfo
->pcumap
= cloc_cumap
;
1499 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_CTYPE
)) {
1500 free_locinfo(locinfo
);
1505 if(!category_needs_update(MSVCRT_LC_MONETARY
, old_locinfo
,
1506 lcid
[MSVCRT_LC_MONETARY
], cp
[MSVCRT_LC_MONETARY
]) &&
1507 !category_needs_update(MSVCRT_LC_NUMERIC
, old_locinfo
,
1508 lcid
[MSVCRT_LC_NUMERIC
], cp
[MSVCRT_LC_NUMERIC
])) {
1509 locinfo
->lconv
= old_locinfo
->lconv
;
1510 locinfo
->lconv_intl_refcount
= old_locinfo
->lconv_intl_refcount
;
1511 if(locinfo
->lconv_intl_refcount
)
1512 InterlockedIncrement(locinfo
->lconv_intl_refcount
);
1513 } else if(lcid
[MSVCRT_LC_MONETARY
] || lcid
[MSVCRT_LC_NUMERIC
]) {
1514 locinfo
->lconv
= MSVCRT_malloc(sizeof(struct MSVCRT_lconv
));
1515 locinfo
->lconv_intl_refcount
= MSVCRT_malloc(sizeof(int));
1516 if(!locinfo
->lconv
|| !locinfo
->lconv_intl_refcount
) {
1517 MSVCRT_free(locinfo
->lconv
);
1518 MSVCRT_free(locinfo
->lconv_intl_refcount
);
1519 locinfo
->lconv
= NULL
;
1520 locinfo
->lconv_intl_refcount
= NULL
;
1521 free_locinfo(locinfo
);
1524 memset(locinfo
->lconv
, 0, sizeof(struct MSVCRT_lconv
));
1525 *locinfo
->lconv_intl_refcount
= 1;
1527 locinfo
->lconv
= &cloc_lconv
;
1530 if(locale_name
[MSVCRT_LC_MONETARY
] &&
1531 !init_category_name(locale_name
[MSVCRT_LC_MONETARY
],
1532 locale_len
[MSVCRT_LC_MONETARY
], locinfo
, MSVCRT_LC_MONETARY
)) {
1533 free_locinfo(locinfo
);
1537 if(!category_needs_update(MSVCRT_LC_MONETARY
, old_locinfo
,
1538 lcid
[MSVCRT_LC_MONETARY
], cp
[MSVCRT_LC_MONETARY
])) {
1539 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_MONETARY
);
1540 locinfo
->lconv_mon_refcount
= old_locinfo
->lconv_mon_refcount
;
1541 if(locinfo
->lconv_mon_refcount
)
1542 InterlockedIncrement(locinfo
->lconv_mon_refcount
);
1543 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1544 locinfo
->lconv
->int_curr_symbol
= old_locinfo
->lconv
->int_curr_symbol
;
1545 locinfo
->lconv
->currency_symbol
= old_locinfo
->lconv
->currency_symbol
;
1546 locinfo
->lconv
->mon_decimal_point
= old_locinfo
->lconv
->mon_decimal_point
;
1547 locinfo
->lconv
->mon_thousands_sep
= old_locinfo
->lconv
->mon_thousands_sep
;
1548 locinfo
->lconv
->mon_grouping
= old_locinfo
->lconv
->mon_grouping
;
1549 locinfo
->lconv
->positive_sign
= old_locinfo
->lconv
->positive_sign
;
1550 locinfo
->lconv
->negative_sign
= old_locinfo
->lconv
->negative_sign
;
1551 locinfo
->lconv
->int_frac_digits
= old_locinfo
->lconv
->int_frac_digits
;
1552 locinfo
->lconv
->frac_digits
= old_locinfo
->lconv
->frac_digits
;
1553 locinfo
->lconv
->p_cs_precedes
= old_locinfo
->lconv
->p_cs_precedes
;
1554 locinfo
->lconv
->p_sep_by_space
= old_locinfo
->lconv
->p_sep_by_space
;
1555 locinfo
->lconv
->n_cs_precedes
= old_locinfo
->lconv
->n_cs_precedes
;
1556 locinfo
->lconv
->n_sep_by_space
= old_locinfo
->lconv
->n_sep_by_space
;
1557 locinfo
->lconv
->p_sign_posn
= old_locinfo
->lconv
->p_sign_posn
;
1558 locinfo
->lconv
->n_sign_posn
= old_locinfo
->lconv
->n_sign_posn
;
1559 #if _MSVCR_VER >= 100
1560 locinfo
->lconv
->_W_int_curr_symbol
= old_locinfo
->lconv
->_W_int_curr_symbol
;
1561 locinfo
->lconv
->_W_currency_symbol
= old_locinfo
->lconv
->_W_currency_symbol
;
1562 locinfo
->lconv
->_W_mon_decimal_point
= old_locinfo
->lconv
->_W_mon_decimal_point
;
1563 locinfo
->lconv
->_W_mon_thousands_sep
= old_locinfo
->lconv
->_W_mon_thousands_sep
;
1564 locinfo
->lconv
->_W_positive_sign
= old_locinfo
->lconv
->_W_positive_sign
;
1565 locinfo
->lconv
->_W_negative_sign
= old_locinfo
->lconv
->_W_negative_sign
;
1568 } else if(lcid
[MSVCRT_LC_MONETARY
]) {
1569 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_MONETARY
],
1570 cp
[MSVCRT_LC_MONETARY
], locinfo
, MSVCRT_LC_MONETARY
)) {
1571 free_locinfo(locinfo
);
1575 locinfo
->lconv_mon_refcount
= MSVCRT_malloc(sizeof(int));
1576 if(!locinfo
->lconv_mon_refcount
) {
1577 free_locinfo(locinfo
);
1581 *locinfo
->lconv_mon_refcount
= 1;
1583 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SINTLSYMBOL
1584 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1585 if(i
&& (locinfo
->lconv
->int_curr_symbol
= MSVCRT_malloc(i
)))
1586 memcpy(locinfo
->lconv
->int_curr_symbol
, buf
, i
);
1588 free_locinfo(locinfo
);
1592 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SCURRENCY
1593 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1594 if(i
&& (locinfo
->lconv
->currency_symbol
= MSVCRT_malloc(i
)))
1595 memcpy(locinfo
->lconv
->currency_symbol
, buf
, i
);
1597 free_locinfo(locinfo
);
1601 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONDECIMALSEP
1602 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1603 if(i
&& (locinfo
->lconv
->mon_decimal_point
= MSVCRT_malloc(i
)))
1604 memcpy(locinfo
->lconv
->mon_decimal_point
, buf
, i
);
1606 free_locinfo(locinfo
);
1610 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1611 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1612 if(i
&& (locinfo
->lconv
->mon_thousands_sep
= MSVCRT_malloc(i
)))
1613 memcpy(locinfo
->lconv
->mon_thousands_sep
, buf
, i
);
1615 free_locinfo(locinfo
);
1619 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONGROUPING
1620 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1622 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1623 if(i
&& (locinfo
->lconv
->mon_grouping
= MSVCRT_malloc(i
))) {
1624 for(i
=0; buf
[i
+1]==';'; i
+=2)
1625 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1626 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1628 locinfo
->lconv
->mon_grouping
[i
/2+1] = 127;
1630 free_locinfo(locinfo
);
1634 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SPOSITIVESIGN
1635 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1636 if(i
&& (locinfo
->lconv
->positive_sign
= MSVCRT_malloc(i
)))
1637 memcpy(locinfo
->lconv
->positive_sign
, buf
, i
);
1639 free_locinfo(locinfo
);
1643 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SNEGATIVESIGN
1644 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1645 if(i
&& (locinfo
->lconv
->negative_sign
= MSVCRT_malloc(i
)))
1646 memcpy(locinfo
->lconv
->negative_sign
, buf
, i
);
1648 free_locinfo(locinfo
);
1652 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IINTLCURRDIGITS
1653 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1654 locinfo
->lconv
->int_frac_digits
= val
;
1656 free_locinfo(locinfo
);
1660 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_ICURRDIGITS
1661 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1662 locinfo
->lconv
->frac_digits
= val
;
1664 free_locinfo(locinfo
);
1668 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IPOSSYMPRECEDES
1669 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1670 locinfo
->lconv
->p_cs_precedes
= val
;
1672 free_locinfo(locinfo
);
1676 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IPOSSEPBYSPACE
1677 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1678 locinfo
->lconv
->p_sep_by_space
= val
;
1680 free_locinfo(locinfo
);
1684 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_INEGSYMPRECEDES
1685 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1686 locinfo
->lconv
->n_cs_precedes
= val
;
1688 free_locinfo(locinfo
);
1692 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_INEGSEPBYSPACE
1693 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1694 locinfo
->lconv
->n_sep_by_space
= val
;
1696 free_locinfo(locinfo
);
1700 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IPOSSIGNPOSN
1701 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1702 locinfo
->lconv
->p_sign_posn
= val
;
1704 free_locinfo(locinfo
);
1708 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_INEGSIGNPOSN
1709 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1710 locinfo
->lconv
->n_sign_posn
= val
;
1712 free_locinfo(locinfo
);
1716 #if _MSVCR_VER >= 100
1717 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SINTLSYMBOL
1718 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1719 if(i
&& (locinfo
->lconv
->_W_int_curr_symbol
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1720 memcpy(locinfo
->lconv
->_W_int_curr_symbol
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1722 free_locinfo(locinfo
);
1726 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SCURRENCY
1727 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1728 if(i
&& (locinfo
->lconv
->_W_currency_symbol
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1729 memcpy(locinfo
->lconv
->_W_currency_symbol
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1731 free_locinfo(locinfo
);
1735 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONDECIMALSEP
1736 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1737 if(i
&& (locinfo
->lconv
->_W_mon_decimal_point
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1738 memcpy(locinfo
->lconv
->_W_mon_decimal_point
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1740 free_locinfo(locinfo
);
1744 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1745 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1746 if(i
&& (locinfo
->lconv
->_W_mon_thousands_sep
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1747 memcpy(locinfo
->lconv
->_W_mon_thousands_sep
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1749 free_locinfo(locinfo
);
1753 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SPOSITIVESIGN
1754 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1755 if(i
&& (locinfo
->lconv
->_W_positive_sign
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1756 memcpy(locinfo
->lconv
->_W_positive_sign
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1758 free_locinfo(locinfo
);
1762 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SNEGATIVESIGN
1763 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1764 if(i
&& (locinfo
->lconv
->_W_negative_sign
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1765 memcpy(locinfo
->lconv
->_W_negative_sign
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1767 free_locinfo(locinfo
);
1772 if (locinfo
->lconv
!= &cloc_lconv
) {
1773 locinfo
->lconv
->int_curr_symbol
= cloc_lconv
.int_curr_symbol
;
1774 locinfo
->lconv
->currency_symbol
= cloc_lconv
.currency_symbol
;
1775 locinfo
->lconv
->mon_decimal_point
= cloc_lconv
.mon_decimal_point
;
1776 locinfo
->lconv
->mon_thousands_sep
= cloc_lconv
.mon_thousands_sep
;
1777 locinfo
->lconv
->mon_grouping
= cloc_lconv
.mon_grouping
;
1778 locinfo
->lconv
->positive_sign
= cloc_lconv
.positive_sign
;
1779 locinfo
->lconv
->negative_sign
= cloc_lconv
.negative_sign
;
1780 locinfo
->lconv
->int_frac_digits
= cloc_lconv
.int_frac_digits
;
1781 locinfo
->lconv
->frac_digits
= cloc_lconv
.frac_digits
;
1782 locinfo
->lconv
->p_cs_precedes
= cloc_lconv
.p_cs_precedes
;
1783 locinfo
->lconv
->p_sep_by_space
= cloc_lconv
.p_sep_by_space
;
1784 locinfo
->lconv
->n_cs_precedes
= cloc_lconv
.n_cs_precedes
;
1785 locinfo
->lconv
->n_sep_by_space
= cloc_lconv
.n_sep_by_space
;
1786 locinfo
->lconv
->p_sign_posn
= cloc_lconv
.p_sign_posn
;
1787 locinfo
->lconv
->n_sign_posn
= cloc_lconv
.n_sign_posn
;
1789 #if _MSVCR_VER >= 100
1790 locinfo
->lconv
->_W_int_curr_symbol
= cloc_lconv
._W_int_curr_symbol
;
1791 locinfo
->lconv
->_W_currency_symbol
= cloc_lconv
._W_currency_symbol
;
1792 locinfo
->lconv
->_W_mon_decimal_point
= cloc_lconv
._W_mon_decimal_point
;
1793 locinfo
->lconv
->_W_mon_thousands_sep
= cloc_lconv
._W_mon_thousands_sep
;
1794 locinfo
->lconv
->_W_positive_sign
= cloc_lconv
._W_positive_sign
;
1795 locinfo
->lconv
->_W_negative_sign
= cloc_lconv
._W_negative_sign
;
1799 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_MONETARY
)) {
1800 free_locinfo(locinfo
);
1805 if(locale_name
[MSVCRT_LC_NUMERIC
] &&
1806 !init_category_name(locale_name
[MSVCRT_LC_NUMERIC
],
1807 locale_len
[MSVCRT_LC_NUMERIC
], locinfo
, MSVCRT_LC_NUMERIC
)) {
1808 free_locinfo(locinfo
);
1812 if(!category_needs_update(MSVCRT_LC_NUMERIC
, old_locinfo
,
1813 lcid
[MSVCRT_LC_NUMERIC
], cp
[MSVCRT_LC_NUMERIC
])) {
1814 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_NUMERIC
);
1815 locinfo
->lconv_num_refcount
= old_locinfo
->lconv_num_refcount
;
1816 if(locinfo
->lconv_num_refcount
)
1817 InterlockedIncrement(locinfo
->lconv_num_refcount
);
1818 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1819 locinfo
->lconv
->decimal_point
= old_locinfo
->lconv
->decimal_point
;
1820 locinfo
->lconv
->thousands_sep
= old_locinfo
->lconv
->thousands_sep
;
1821 locinfo
->lconv
->grouping
= old_locinfo
->lconv
->grouping
;
1822 #if _MSVCR_VER >= 100
1823 locinfo
->lconv
->_W_decimal_point
= old_locinfo
->lconv
->_W_decimal_point
;
1824 locinfo
->lconv
->_W_thousands_sep
= old_locinfo
->lconv
->_W_thousands_sep
;
1827 } else if(lcid
[MSVCRT_LC_NUMERIC
]) {
1828 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_NUMERIC
],
1829 cp
[MSVCRT_LC_NUMERIC
], locinfo
, MSVCRT_LC_NUMERIC
)) {
1830 free_locinfo(locinfo
);
1834 locinfo
->lconv_num_refcount
= MSVCRT_malloc(sizeof(int));
1835 if(!locinfo
->lconv_num_refcount
) {
1836 free_locinfo(locinfo
);
1840 *locinfo
->lconv_num_refcount
= 1;
1842 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_SDECIMAL
1843 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1844 if(i
&& (locinfo
->lconv
->decimal_point
= MSVCRT_malloc(i
)))
1845 memcpy(locinfo
->lconv
->decimal_point
, buf
, i
);
1847 free_locinfo(locinfo
);
1851 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_STHOUSAND
1852 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1853 if(i
&& (locinfo
->lconv
->thousands_sep
= MSVCRT_malloc(i
)))
1854 memcpy(locinfo
->lconv
->thousands_sep
, buf
, i
);
1856 free_locinfo(locinfo
);
1860 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_SGROUPING
1861 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1863 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1864 if(i
&& (locinfo
->lconv
->grouping
= MSVCRT_malloc(i
))) {
1865 for(i
=0; buf
[i
+1]==';'; i
+=2)
1866 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1867 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1869 locinfo
->lconv
->grouping
[i
/2+1] = 127;
1871 free_locinfo(locinfo
);
1875 #if _MSVCR_VER >= 100
1876 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_SDECIMAL
1877 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1878 if(i
&& (locinfo
->lconv
->_W_decimal_point
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1879 memcpy(locinfo
->lconv
->_W_decimal_point
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1881 free_locinfo(locinfo
);
1885 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_STHOUSAND
1886 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1887 if(i
&& (locinfo
->lconv
->_W_thousands_sep
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1888 memcpy(locinfo
->lconv
->_W_thousands_sep
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1890 free_locinfo(locinfo
);
1895 if (locinfo
->lconv
!= &cloc_lconv
) {
1896 locinfo
->lconv
->decimal_point
= cloc_lconv
.decimal_point
;
1897 locinfo
->lconv
->thousands_sep
= cloc_lconv
.thousands_sep
;
1898 locinfo
->lconv
->grouping
= cloc_lconv
.grouping
;
1900 #if _MSVCR_VER >= 100
1901 locinfo
->lconv
->_W_decimal_point
= cloc_lconv
._W_decimal_point
;
1902 locinfo
->lconv
->_W_thousands_sep
= cloc_lconv
._W_thousands_sep
;
1906 if (!init_category_name("C", 1, locinfo
, MSVCRT_LC_NUMERIC
)) {
1907 free_locinfo(locinfo
);
1912 if(locale_name
[MSVCRT_LC_TIME
] &&
1913 !init_category_name(locale_name
[MSVCRT_LC_TIME
],
1914 locale_len
[MSVCRT_LC_TIME
], locinfo
, MSVCRT_LC_TIME
)) {
1915 free_locinfo(locinfo
);
1919 if(!category_needs_update(MSVCRT_LC_TIME
, old_locinfo
,
1920 lcid
[MSVCRT_LC_TIME
], cp
[MSVCRT_LC_TIME
])) {
1921 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_TIME
);
1922 locinfo
->lc_time_curr
= old_locinfo
->lc_time_curr
;
1923 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
1924 } else if(lcid
[MSVCRT_LC_TIME
]) {
1925 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_TIME
],
1926 cp
[MSVCRT_LC_TIME
], locinfo
, MSVCRT_LC_TIME
)) {
1927 free_locinfo(locinfo
);
1931 locinfo
->lc_time_curr
= create_time_data(lcid
[MSVCRT_LC_TIME
]);
1932 if(!locinfo
->lc_time_curr
) {
1933 free_locinfo(locinfo
);
1937 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_TIME
)) {
1938 free_locinfo(locinfo
);
1941 locinfo
->lc_time_curr
= &cloc_time_data
;
1942 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
1948 /*********************************************************************
1949 * _create_locale (MSVCRT.@)
1951 MSVCRT__locale_t CDECL
MSVCRT__create_locale(int category
, const char *locale
)
1953 MSVCRT__locale_t loc
;
1955 loc
= MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct
));
1959 loc
->locinfo
= create_locinfo(category
, locale
, NULL
);
1965 loc
->mbcinfo
= create_mbcinfo(loc
->locinfo
->lc_id
[MSVCRT_LC_CTYPE
].wCodePage
,
1966 loc
->locinfo
->lc_handle
[MSVCRT_LC_CTYPE
], NULL
);
1968 free_locinfo(loc
->locinfo
);
1975 #if _MSVCR_VER >= 110
1976 /*********************************************************************
1977 * _wcreate_locale (MSVCR110.@)
1979 MSVCRT__locale_t CDECL
MSVCRT__wcreate_locale(int category
, const MSVCRT_wchar_t
*locale
)
1981 MSVCRT__locale_t loc
;
1985 if(category
<MSVCRT_LC_MIN
|| category
>MSVCRT_LC_MAX
|| !locale
)
1988 len
= MSVCRT_wcstombs(NULL
, locale
, 0);
1991 if(!(str
= MSVCRT_malloc(++len
)))
1993 MSVCRT_wcstombs(str
, locale
, len
);
1995 loc
= MSVCRT__create_locale(category
, str
);
2002 /*********************************************************************
2003 * setlocale (MSVCRT.@)
2005 char* CDECL
MSVCRT_setlocale(int category
, const char* locale
)
2007 thread_data_t
*data
= msvcrt_get_thread_data();
2008 MSVCRT_pthreadlocinfo locinfo
= get_locinfo(), newlocinfo
;
2010 if(category
<MSVCRT_LC_MIN
|| category
>MSVCRT_LC_MAX
)
2014 if(category
== MSVCRT_LC_ALL
)
2015 return construct_lc_all(locinfo
);
2017 return locinfo
->lc_category
[category
].locale
;
2020 newlocinfo
= create_locinfo(category
, locale
, locinfo
);
2022 WARN("%d %s failed\n", category
, locale
);
2026 if(locale
[0] != 'C' || locale
[1] != '\0')
2027 initial_locale
= FALSE
;
2029 if(data
->locale_flags
& LOCALE_THREAD
)
2031 if(data
->locale_flags
& LOCALE_FREE
)
2032 free_locinfo(data
->locinfo
);
2033 data
->locinfo
= newlocinfo
;
2040 free_locinfo(MSVCRT_locale
->locinfo
);
2041 MSVCRT_locale
->locinfo
= newlocinfo
;
2043 MSVCRT___lc_codepage
= newlocinfo
->lc_codepage
;
2044 MSVCRT___lc_collate_cp
= newlocinfo
->lc_collate_cp
;
2045 MSVCRT___mb_cur_max
= newlocinfo
->mb_cur_max
;
2046 MSVCRT__pctype
= newlocinfo
->pctype
;
2047 for(i
=MSVCRT_LC_MIN
; i
<=MSVCRT_LC_MAX
; i
++)
2048 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2050 update_thread_locale(data
);
2053 if(category
== MSVCRT_LC_ALL
)
2054 return construct_lc_all(data
->locinfo
);
2056 return data
->locinfo
->lc_category
[category
].locale
;
2059 /*********************************************************************
2060 * _wsetlocale (MSVCRT.@)
2062 MSVCRT_wchar_t
* CDECL
MSVCRT__wsetlocale(int category
, const MSVCRT_wchar_t
* wlocale
)
2064 static MSVCRT_wchar_t current_lc_all
[MAX_LOCALE_LENGTH
];
2066 char *locale
= NULL
;
2071 len
= MSVCRT_wcstombs(NULL
, wlocale
, 0);
2075 locale
= MSVCRT_malloc(++len
);
2079 MSVCRT_wcstombs(locale
, wlocale
, len
);
2083 ret
= MSVCRT_setlocale(category
, locale
);
2084 MSVCRT_free(locale
);
2086 if(ret
&& MSVCRT_mbstowcs(current_lc_all
, ret
, MAX_LOCALE_LENGTH
)==-1)
2090 return ret
? current_lc_all
: NULL
;
2093 #if _MSVCR_VER >= 80
2094 /*********************************************************************
2095 * _configthreadlocale (MSVCR80.@)
2097 int CDECL
_configthreadlocale(int type
)
2099 thread_data_t
*data
= msvcrt_get_thread_data();
2102 ret
= (data
->locale_flags
& LOCALE_THREAD
? MSVCRT__ENABLE_PER_THREAD_LOCALE
:
2103 MSVCRT__DISABLE_PER_THREAD_LOCALE
);
2105 if(type
== MSVCRT__ENABLE_PER_THREAD_LOCALE
)
2106 data
->locale_flags
|= LOCALE_THREAD
;
2107 else if(type
== MSVCRT__DISABLE_PER_THREAD_LOCALE
)
2108 data
->locale_flags
&= ~LOCALE_THREAD
;
2116 BOOL
msvcrt_init_locale(void)
2121 MSVCRT_locale
= MSVCRT__create_locale(0, "C");
2126 MSVCRT___lc_codepage
= MSVCRT_locale
->locinfo
->lc_codepage
;
2127 MSVCRT___lc_collate_cp
= MSVCRT_locale
->locinfo
->lc_collate_cp
;
2128 MSVCRT___mb_cur_max
= MSVCRT_locale
->locinfo
->mb_cur_max
;
2129 MSVCRT__pctype
= MSVCRT_locale
->locinfo
->pctype
;
2130 for(i
=MSVCRT_LC_MIN
; i
<=MSVCRT_LC_MAX
; i
++)
2131 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2132 _setmbcp(_MB_CP_ANSI
);
2136 #if _MSVCR_VER >= 120
2137 /*********************************************************************
2138 * wctrans (MSVCR120.@)
2140 wctrans_t CDECL
MSVCR120_wctrans(const char *property
)
2142 static const char str_tolower
[] = "tolower";
2143 static const char str_toupper
[] = "toupper";
2145 if(!strcmp(property
, str_tolower
))
2147 if(!strcmp(property
, str_toupper
))