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 static MSVCRT_pthreadlocinfo
* CDECL
get_locinfo_ptr(void) {
589 thread_data_t
*data
= msvcrt_get_thread_data();
591 if(!data
|| !data
->have_locale
)
592 return &MSVCRT_locale
->locinfo
;
594 return &data
->locinfo
;
597 /* INTERNAL: returns threadlocinfo struct */
598 MSVCRT_pthreadlocinfo CDECL
get_locinfo(void) {
599 return *get_locinfo_ptr();
602 MSVCRT_pthreadmbcinfo
* CDECL
get_mbcinfo_ptr(void) {
603 thread_data_t
*data
= msvcrt_get_thread_data();
605 if(!data
|| !data
->have_locale
)
606 return &MSVCRT_locale
->mbcinfo
;
608 return &data
->mbcinfo
;
611 /* INTERNAL: returns pthreadmbcinfo struct */
612 MSVCRT_pthreadmbcinfo CDECL
get_mbcinfo(void) {
613 return *get_mbcinfo_ptr();
616 /* INTERNAL: constructs string returned by setlocale */
617 static inline char* construct_lc_all(MSVCRT_pthreadlocinfo locinfo
) {
618 static char current_lc_all
[MAX_LOCALE_LENGTH
];
622 for(i
=MSVCRT_LC_MIN
+1; i
<MSVCRT_LC_MAX
; i
++) {
623 if(strcmp(locinfo
->lc_category
[i
].locale
,
624 locinfo
->lc_category
[i
+1].locale
))
629 return locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
;
631 MSVCRT_sprintf(current_lc_all
,
632 "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s",
633 locinfo
->lc_category
[MSVCRT_LC_COLLATE
].locale
,
634 locinfo
->lc_category
[MSVCRT_LC_CTYPE
].locale
,
635 locinfo
->lc_category
[MSVCRT_LC_MONETARY
].locale
,
636 locinfo
->lc_category
[MSVCRT_LC_NUMERIC
].locale
,
637 locinfo
->lc_category
[MSVCRT_LC_TIME
].locale
);
639 return current_lc_all
;
643 /*********************************************************************
644 * _Getdays (MSVCRT.@)
646 char* CDECL
_Getdays(void)
648 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
649 int i
, len
, size
= 0;
655 size
+= strlen(cur
->str
.names
.short_wday
[i
]) + 1;
656 size
+= strlen(cur
->str
.names
.wday
[i
]) + 1;
658 out
= MSVCRT_malloc(size
+1);
665 len
= strlen(cur
->str
.names
.short_wday
[i
]);
666 memcpy(&out
[size
], cur
->str
.names
.short_wday
[i
], len
);
670 len
= strlen(cur
->str
.names
.wday
[i
]);
671 memcpy(&out
[size
], cur
->str
.names
.wday
[i
], len
);
679 #if _MSVCR_VER >= 110
680 /*********************************************************************
681 * _W_Getdays (MSVCR110.@)
683 MSVCRT_wchar_t
* CDECL
_W_Getdays(void)
685 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
687 int i
, len
, size
= 0;
692 size
+= MSVCRT_wcslen(cur
->wstr
.names
.short_wday
[i
]) + 1;
693 size
+= MSVCRT_wcslen(cur
->wstr
.names
.wday
[i
]) + 1;
695 out
= MSVCRT_malloc((size
+1)*sizeof(*out
));
702 len
= MSVCRT_wcslen(cur
->wstr
.names
.short_wday
[i
]);
703 memcpy(&out
[size
], cur
->wstr
.names
.short_wday
[i
], len
*sizeof(*out
));
707 len
= MSVCRT_wcslen(cur
->wstr
.names
.wday
[i
]);
708 memcpy(&out
[size
], cur
->wstr
.names
.wday
[i
], len
*sizeof(*out
));
717 /*********************************************************************
718 * _Getmonths (MSVCRT.@)
720 char* CDECL
_Getmonths(void)
722 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
723 int i
, len
, size
= 0;
728 for(i
=0; i
<12; i
++) {
729 size
+= strlen(cur
->str
.names
.short_mon
[i
]) + 1;
730 size
+= strlen(cur
->str
.names
.mon
[i
]) + 1;
732 out
= MSVCRT_malloc(size
+1);
737 for(i
=0; i
<12; i
++) {
739 len
= strlen(cur
->str
.names
.short_mon
[i
]);
740 memcpy(&out
[size
], cur
->str
.names
.short_mon
[i
], len
);
744 len
= strlen(cur
->str
.names
.mon
[i
]);
745 memcpy(&out
[size
], cur
->str
.names
.mon
[i
], len
);
753 #if _MSVCR_VER >= 110
754 /*********************************************************************
755 * _W_Getmonths (MSVCR110.@)
757 MSVCRT_wchar_t
* CDECL
_W_Getmonths(void)
759 MSVCRT___lc_time_data
*cur
= get_locinfo()->lc_time_curr
;
761 int i
, len
, size
= 0;
765 for(i
=0; i
<12; i
++) {
766 size
+= MSVCRT_wcslen(cur
->wstr
.names
.short_mon
[i
]) + 1;
767 size
+= MSVCRT_wcslen(cur
->wstr
.names
.mon
[i
]) + 1;
769 out
= MSVCRT_malloc((size
+1)*sizeof(*out
));
774 for(i
=0; i
<12; i
++) {
776 len
= MSVCRT_wcslen(cur
->wstr
.names
.short_mon
[i
]);
777 memcpy(&out
[size
], cur
->wstr
.names
.short_mon
[i
], len
*sizeof(*out
));
781 len
= MSVCRT_wcslen(cur
->wstr
.names
.mon
[i
]);
782 memcpy(&out
[size
], cur
->wstr
.names
.mon
[i
], len
*sizeof(*out
));
791 /*********************************************************************
792 * _Gettnames (MSVCRT.@)
794 void* CDECL
_Gettnames(void)
796 MSVCRT___lc_time_data
*ret
, *cur
= get_locinfo()->lc_time_curr
;
797 unsigned int i
, len
, size
= sizeof(MSVCRT___lc_time_data
);
801 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++)
802 size
+= strlen(cur
->str
.str
[i
])+1;
804 ret
= MSVCRT_malloc(size
);
807 memcpy(ret
, cur
, sizeof(*ret
));
810 for(i
=0; i
<ARRAY_SIZE(cur
->str
.str
); i
++) {
811 len
= strlen(cur
->str
.str
[i
])+1;
812 memcpy(&ret
->data
[size
], cur
->str
.str
[i
], len
);
813 ret
->str
.str
[i
] = &ret
->data
[size
];
820 #if _MSVCR_VER >= 110
821 /*********************************************************************
822 * _W_Gettnames (MSVCR110.@)
824 void* CDECL
_W_Gettnames(void)
830 /*********************************************************************
831 * __crtLCMapStringA (MSVCRT.@)
833 int CDECL
__crtLCMapStringA(
834 LCID lcid
, DWORD mapflags
, const char* src
, int srclen
, char* dst
,
835 int dstlen
, unsigned int codepage
, int xflag
837 WCHAR buf_in
[32], *in
= buf_in
;
838 WCHAR buf_out
[32], *out
= buf_out
;
839 int in_len
, out_len
, r
;
841 TRACE("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
842 lcid
, mapflags
, src
, srclen
, dst
, dstlen
, codepage
, xflag
);
844 in_len
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, NULL
, 0);
845 if (!in_len
) return 0;
846 if (in_len
> ARRAY_SIZE(buf_in
))
848 in
= MSVCRT_malloc(in_len
* sizeof(WCHAR
));
852 r
= MultiByteToWideChar(codepage
, MB_ERR_INVALID_CHARS
, src
, srclen
, in
, in_len
);
855 if (mapflags
& LCMAP_SORTKEY
)
857 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, (WCHAR
*)dst
, dstlen
);
861 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, NULL
, 0);
864 if (r
> ARRAY_SIZE(buf_out
))
866 out
= MSVCRT_malloc(r
* sizeof(WCHAR
));
874 r
= LCMapStringW(lcid
, mapflags
, in
, in_len
, out
, out_len
);
877 r
= WideCharToMultiByte(codepage
, 0, out
, out_len
, dst
, dstlen
, NULL
, NULL
);
880 if (in
!= buf_in
) MSVCRT_free(in
);
881 if (out
!= buf_out
) MSVCRT_free(out
);
885 /*********************************************************************
886 * __crtLCMapStringW (MSVCRT.@)
888 int CDECL
__crtLCMapStringW(LCID lcid
, DWORD mapflags
, const MSVCRT_wchar_t
*src
,
889 int srclen
, MSVCRT_wchar_t
*dst
, int dstlen
, unsigned int codepage
, int xflag
)
891 FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
892 lcid
, mapflags
, debugstr_w(src
), srclen
, dst
, dstlen
, codepage
, xflag
);
894 return LCMapStringW(lcid
, mapflags
, src
, srclen
, dst
, dstlen
);
897 /*********************************************************************
898 * __crtCompareStringA (MSVCRT.@)
900 int CDECL
__crtCompareStringA( LCID lcid
, DWORD flags
, const char *src1
, int len1
,
901 const char *src2
, int len2
)
903 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
904 lcid
, flags
, debugstr_a(src1
), len1
, debugstr_a(src2
), len2
);
905 /* FIXME: probably not entirely right */
906 return CompareStringA( lcid
, flags
, src1
, len1
, src2
, len2
);
909 /*********************************************************************
910 * __crtCompareStringW (MSVCRT.@)
912 int CDECL
__crtCompareStringW( LCID lcid
, DWORD flags
, const MSVCRT_wchar_t
*src1
, int len1
,
913 const MSVCRT_wchar_t
*src2
, int len2
)
915 FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n",
916 lcid
, flags
, debugstr_w(src1
), len1
, debugstr_w(src2
), len2
);
917 /* FIXME: probably not entirely right */
918 return CompareStringW( lcid
, flags
, src1
, len1
, src2
, len2
);
921 /*********************************************************************
922 * __crtGetLocaleInfoW (MSVCRT.@)
924 int CDECL
__crtGetLocaleInfoW( LCID lcid
, LCTYPE type
, MSVCRT_wchar_t
*buffer
, int len
)
926 FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid
, type
, buffer
, len
);
927 /* FIXME: probably not entirely right */
928 return GetLocaleInfoW( lcid
, type
, buffer
, len
);
931 #if _MSVCR_VER >= 110
932 /*********************************************************************
933 * __crtGetLocaleInfoEx (MSVC110.@)
935 int CDECL
__crtGetLocaleInfoEx( const WCHAR
*locale
, LCTYPE type
, MSVCRT_wchar_t
*buffer
, int len
)
937 TRACE("(%s, %x, %p, %d)\n", debugstr_w(locale
), type
, buffer
, len
);
938 return GetLocaleInfoEx(locale
, type
, buffer
, len
);
942 /*********************************************************************
945 MSVCRT_wint_t CDECL
MSVCRT_btowc(int c
)
947 unsigned char letter
= c
;
952 if(!get_locinfo()->lc_codepage
)
954 if(!MultiByteToWideChar(get_locinfo()->lc_codepage
,
955 MB_ERR_INVALID_CHARS
, (LPCSTR
)&letter
, 1, &ret
, 1))
961 /*********************************************************************
962 * __crtGetStringTypeW(MSVCRT.@)
964 * This function was accepting different number of arguments in older
965 * versions of msvcrt.
967 BOOL CDECL
__crtGetStringTypeW(DWORD unk
, DWORD type
,
968 MSVCRT_wchar_t
*buffer
, int len
, WORD
*out
)
970 FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
971 unk
, type
, buffer
, len
, out
);
973 return GetStringTypeW(type
, buffer
, len
, out
);
976 /*********************************************************************
977 * localeconv (MSVCRT.@)
979 struct MSVCRT_lconv
* CDECL
MSVCRT_localeconv(void)
981 return get_locinfo()->lconv
;
984 /*********************************************************************
985 * __lconv_init (MSVCRT.@)
987 int CDECL
__lconv_init(void)
989 /* this is used to make chars unsigned */
990 cloc_lconv
.int_frac_digits
= (char)UCHAR_MAX
;
991 cloc_lconv
.frac_digits
= (char)UCHAR_MAX
;
992 cloc_lconv
.p_cs_precedes
= (char)UCHAR_MAX
;
993 cloc_lconv
.p_sep_by_space
= (char)UCHAR_MAX
;
994 cloc_lconv
.n_cs_precedes
= (char)UCHAR_MAX
;
995 cloc_lconv
.n_sep_by_space
= (char)UCHAR_MAX
;
996 cloc_lconv
.p_sign_posn
= (char)UCHAR_MAX
;
997 cloc_lconv
.n_sign_posn
= (char)UCHAR_MAX
;
1001 /*********************************************************************
1002 * ___lc_handle_func (MSVCRT.@)
1004 LCID
* CDECL
___lc_handle_func(void)
1006 return (LCID
*)get_locinfo()->lc_handle
;
1009 #if _MSVCR_VER >= 110
1010 /*********************************************************************
1011 * ___lc_locale_name_func (MSVCR110.@)
1013 MSVCRT_wchar_t
** CDECL
___lc_locale_name_func(void)
1015 return get_locinfo()->lc_name
;
1019 /*********************************************************************
1020 * ___lc_codepage_func (MSVCRT.@)
1022 unsigned int CDECL
___lc_codepage_func(void)
1024 return get_locinfo()->lc_codepage
;
1027 /*********************************************************************
1028 * ___lc_collate_cp_func (MSVCRT.@)
1030 int CDECL
___lc_collate_cp_func(void)
1032 return get_locinfo()->lc_collate_cp
;
1035 /* INTERNAL: frees MSVCRT_pthreadlocinfo struct */
1036 void free_locinfo(MSVCRT_pthreadlocinfo locinfo
)
1043 for(i
=MSVCRT_LC_MIN
+1; i
<=MSVCRT_LC_MAX
; i
++) {
1044 if(!locinfo
->lc_category
[i
].refcount
1045 || !InterlockedDecrement(locinfo
->lc_category
[i
].refcount
)) {
1046 MSVCRT_free(locinfo
->lc_category
[i
].locale
);
1047 MSVCRT_free(locinfo
->lc_category
[i
].refcount
);
1049 if(!locinfo
->lc_category
[i
].wrefcount
1050 || !InterlockedDecrement(locinfo
->lc_category
[i
].wrefcount
)) {
1051 #if _MSVCR_VER >= 110
1052 MSVCRT_free(locinfo
->lc_name
[i
]);
1054 MSVCRT_free(locinfo
->lc_category
[i
].wrefcount
);
1058 if(locinfo
->lconv_num_refcount
1059 && !InterlockedDecrement(locinfo
->lconv_num_refcount
)) {
1060 MSVCRT_free(locinfo
->lconv
->decimal_point
);
1061 MSVCRT_free(locinfo
->lconv
->thousands_sep
);
1062 MSVCRT_free(locinfo
->lconv
->grouping
);
1063 #if _MSVCR_VER >= 100
1064 MSVCRT_free(locinfo
->lconv
->_W_decimal_point
);
1065 MSVCRT_free(locinfo
->lconv
->_W_thousands_sep
);
1067 MSVCRT_free(locinfo
->lconv_num_refcount
);
1069 if(locinfo
->lconv_mon_refcount
1070 && !InterlockedDecrement(locinfo
->lconv_mon_refcount
)) {
1071 MSVCRT_free(locinfo
->lconv
->int_curr_symbol
);
1072 MSVCRT_free(locinfo
->lconv
->currency_symbol
);
1073 MSVCRT_free(locinfo
->lconv
->mon_decimal_point
);
1074 MSVCRT_free(locinfo
->lconv
->mon_thousands_sep
);
1075 MSVCRT_free(locinfo
->lconv
->mon_grouping
);
1076 MSVCRT_free(locinfo
->lconv
->positive_sign
);
1077 MSVCRT_free(locinfo
->lconv
->negative_sign
);
1078 #if _MSVCR_VER >= 100
1079 MSVCRT_free(locinfo
->lconv
->_W_int_curr_symbol
);
1080 MSVCRT_free(locinfo
->lconv
->_W_currency_symbol
);
1081 MSVCRT_free(locinfo
->lconv
->_W_mon_decimal_point
);
1082 MSVCRT_free(locinfo
->lconv
->_W_mon_thousands_sep
);
1083 MSVCRT_free(locinfo
->lconv
->_W_positive_sign
);
1084 MSVCRT_free(locinfo
->lconv
->_W_negative_sign
);
1086 MSVCRT_free(locinfo
->lconv_mon_refcount
);
1088 if(locinfo
->lconv_intl_refcount
1089 && !InterlockedDecrement(locinfo
->lconv_intl_refcount
)) {
1090 MSVCRT_free(locinfo
->lconv_intl_refcount
);
1091 MSVCRT_free(locinfo
->lconv
);
1094 if(locinfo
->ctype1_refcount
1095 && !InterlockedDecrement(locinfo
->ctype1_refcount
)) {
1096 MSVCRT_free(locinfo
->ctype1_refcount
);
1097 MSVCRT_free(locinfo
->ctype1
);
1098 MSVCRT_free((void*)locinfo
->pclmap
);
1099 MSVCRT_free((void*)locinfo
->pcumap
);
1102 if(locinfo
->lc_time_curr
&& locinfo
->lc_time_curr
!= &cloc_time_data
1103 && !InterlockedDecrement(&locinfo
->lc_time_curr
->refcount
))
1104 MSVCRT_free(locinfo
->lc_time_curr
);
1106 if(InterlockedDecrement(&locinfo
->refcount
))
1109 MSVCRT_free(locinfo
);
1112 /* INTERNAL: frees MSVCRT_pthreadmbcinfo struct */
1113 void free_mbcinfo(MSVCRT_pthreadmbcinfo mbcinfo
)
1118 if(InterlockedDecrement(&mbcinfo
->refcount
))
1121 MSVCRT_free(mbcinfo
);
1124 /*********************************************************************
1125 * _lock_locales (UCRTBASE.@)
1127 void CDECL
_lock_locales(void)
1129 _lock(_SETLOCALE_LOCK
);
1132 /*********************************************************************
1133 * _unlock_locales (UCRTBASE.@)
1135 void CDECL
_unlock_locales(void)
1137 _unlock(_SETLOCALE_LOCK
);
1140 MSVCRT__locale_t CDECL
get_current_locale_noalloc(MSVCRT__locale_t locale
)
1142 thread_data_t
*data
= msvcrt_get_thread_data();
1145 if(!data
|| !data
->have_locale
)
1148 *locale
= *MSVCRT_locale
;
1152 locale
->locinfo
= data
->locinfo
;
1153 locale
->mbcinfo
= data
->mbcinfo
;
1156 InterlockedIncrement(&locale
->locinfo
->refcount
);
1157 for(i
=MSVCRT_LC_MIN
+1; i
<=MSVCRT_LC_MAX
; i
++)
1159 InterlockedIncrement(locale
->locinfo
->lc_category
[i
].refcount
);
1160 if(locale
->locinfo
->lc_category
[i
].wrefcount
)
1161 InterlockedIncrement(locale
->locinfo
->lc_category
[i
].wrefcount
);
1163 if(locale
->locinfo
->lconv_intl_refcount
)
1164 InterlockedIncrement(locale
->locinfo
->lconv_intl_refcount
);
1165 if(locale
->locinfo
->lconv_num_refcount
)
1166 InterlockedIncrement(locale
->locinfo
->lconv_num_refcount
);
1167 if(locale
->locinfo
->lconv_mon_refcount
)
1168 InterlockedIncrement(locale
->locinfo
->lconv_mon_refcount
);
1169 if(locale
->locinfo
->ctype1_refcount
)
1170 InterlockedIncrement(locale
->locinfo
->ctype1_refcount
);
1171 InterlockedIncrement(&locale
->locinfo
->lc_time_curr
->refcount
);
1172 if(locale
->locinfo
== MSVCRT_locale
->locinfo
)
1175 InterlockedIncrement(&locale
->mbcinfo
->refcount
);
1179 void CDECL
free_locale_noalloc(MSVCRT__locale_t locale
)
1181 free_locinfo(locale
->locinfo
);
1182 free_mbcinfo(locale
->mbcinfo
);
1185 /*********************************************************************
1186 * _get_current_locale (MSVCRT.@)
1188 MSVCRT__locale_t CDECL
MSVCRT__get_current_locale(void)
1190 MSVCRT__locale_t loc
= MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct
));
1194 return get_current_locale_noalloc(loc
);
1197 /*********************************************************************
1198 * _free_locale (MSVCRT.@)
1200 void CDECL
MSVCRT__free_locale(MSVCRT__locale_t locale
)
1205 free_locale_noalloc(locale
);
1206 MSVCRT_free(locale
);
1209 static inline BOOL
category_needs_update(int cat
,
1210 const MSVCRT_threadlocinfo
*locinfo
, LCID lcid
, unsigned short cp
)
1212 if(!locinfo
) return TRUE
;
1213 return lcid
!=locinfo
->lc_handle
[cat
] || cp
!=locinfo
->lc_id
[cat
].wCodePage
;
1216 static MSVCRT___lc_time_data
* create_time_data(LCID lcid
)
1218 static const DWORD time_data
[] = {
1219 LOCALE_SABBREVDAYNAME7
, LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
,
1220 LOCALE_SABBREVDAYNAME3
, LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
,
1221 LOCALE_SABBREVDAYNAME6
,
1222 LOCALE_SDAYNAME7
, LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
1223 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
,
1224 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
1225 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
1226 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
1227 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
1228 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
, LOCALE_SMONTHNAME4
,
1229 LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
, LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
,
1230 LOCALE_SMONTHNAME9
, LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
1231 LOCALE_S1159
, LOCALE_S2359
,
1232 LOCALE_SSHORTDATE
, LOCALE_SLONGDATE
,
1236 MSVCRT___lc_time_data
*cur
;
1239 size
= sizeof(MSVCRT___lc_time_data
);
1240 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1241 ret
= GetLocaleInfoA(lcid
, time_data
[i
], NULL
, 0);
1246 ret
= GetLocaleInfoW(lcid
, time_data
[i
], NULL
, 0);
1249 size
+= ret
*sizeof(MSVCRT_wchar_t
);
1251 #if _MSVCR_VER >= 110
1252 size
+= LCIDToLocaleName(lcid
, NULL
, 0, 0)*sizeof(MSVCRT_wchar_t
);
1255 cur
= MSVCRT_malloc(size
);
1260 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1261 cur
->str
.str
[i
] = &cur
->data
[ret
];
1262 ret
+= GetLocaleInfoA(lcid
, time_data
[i
], &cur
->data
[ret
], size
-ret
);
1264 for(i
=0; i
<ARRAY_SIZE(time_data
); i
++) {
1265 cur
->wstr
.wstr
[i
] = (MSVCRT_wchar_t
*)&cur
->data
[ret
];
1266 ret
+= GetLocaleInfoW(lcid
, time_data
[i
],
1267 (MSVCRT_wchar_t
*)&cur
->data
[ret
], size
-ret
)*sizeof(MSVCRT_wchar_t
);
1269 #if _MSVCR_VER >= 110
1270 cur
->locname
= (MSVCRT_wchar_t
*)&cur
->data
[ret
];
1271 LCIDToLocaleName(lcid
, (MSVCRT_wchar_t
*)&cur
->data
[ret
], (size
-ret
)/sizeof(MSVCRT_wchar_t
), 0);
1281 static MSVCRT_pthreadlocinfo
create_locinfo(int category
,
1282 const char *locale
, const MSVCRT_threadlocinfo
*old_locinfo
)
1284 static const char collate
[] = "COLLATE=";
1285 static const char ctype
[] = "CTYPE=";
1286 static const char monetary
[] = "MONETARY=";
1287 static const char numeric
[] = "NUMERIC=";
1288 static const char time
[] = "TIME=";
1290 MSVCRT_pthreadlocinfo locinfo
;
1291 LCID lcid
[6] = { 0 };
1292 unsigned short cp
[6] = { 0 };
1293 const char *locale_name
[6] = { 0 };
1294 int val
, locale_len
[6] = { 0 };
1297 #if _MSVCR_VER >= 100
1298 MSVCRT_wchar_t wbuf
[256];
1302 TRACE("(%d %s)\n", category
, locale
);
1304 if(category
<MSVCRT_LC_MIN
|| category
>MSVCRT_LC_MAX
|| !locale
)
1307 if(locale
[0]=='C' && !locale
[1]) {
1310 } else if (locale
[0] == 'L' && locale
[1] == 'C' && locale
[2] == '_') {
1314 locale
+= 3; /* LC_ */
1315 if(!memcmp(locale
, collate
, sizeof(collate
)-1)) {
1316 i
= MSVCRT_LC_COLLATE
;
1317 locale
+= sizeof(collate
)-1;
1318 } else if(!memcmp(locale
, ctype
, sizeof(ctype
)-1)) {
1319 i
= MSVCRT_LC_CTYPE
;
1320 locale
+= sizeof(ctype
)-1;
1321 } else if(!memcmp(locale
, monetary
, sizeof(monetary
)-1)) {
1322 i
= MSVCRT_LC_MONETARY
;
1323 locale
+= sizeof(monetary
)-1;
1324 } else if(!memcmp(locale
, numeric
, sizeof(numeric
)-1)) {
1325 i
= MSVCRT_LC_NUMERIC
;
1326 locale
+= sizeof(numeric
)-1;
1327 } else if(!memcmp(locale
, time
, sizeof(time
)-1)) {
1329 locale
+= sizeof(time
)-1;
1333 p
= strchr(locale
, ';');
1334 if(locale
[0]=='C' && (locale
[1]==';' || locale
[1]=='\0')) {
1338 memcpy(buf
, locale
, p
-locale
);
1339 buf
[p
-locale
] = '\0';
1340 lcid
[i
] = MSVCRT_locale_to_LCID(buf
, &cp
[i
], &sname
);
1342 locale_name
[i
] = locale
;
1343 locale_len
[i
] = p
-locale
;
1346 lcid
[i
] = MSVCRT_locale_to_LCID(locale
, &cp
[i
], &sname
);
1348 locale_name
[i
] = locale
;
1349 locale_len
[i
] = strlen(locale
);
1356 if(!p
|| *(p
+1)!='L' || *(p
+2)!='C' || *(p
+3)!='_')
1362 lcid
[0] = MSVCRT_locale_to_LCID(locale
, &cp
[0], &sname
);
1366 locale_name
[0] = locale
;
1367 locale_len
[0] = strlen(locale
);
1370 for(i
=1; i
<6; i
++) {
1373 locale_name
[i
] = locale_name
[0];
1374 locale_len
[i
] = locale_len
[0];
1378 for(i
=1; i
<6; i
++) {
1379 if(category
!=MSVCRT_LC_ALL
&& category
!=i
) {
1381 lcid
[i
] = old_locinfo
->lc_handle
[i
];
1382 cp
[i
] = old_locinfo
->lc_id
[i
].wCodePage
;
1390 locinfo
= MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo
));
1394 memset(locinfo
, 0, sizeof(MSVCRT_threadlocinfo
));
1395 locinfo
->refcount
= 1;
1397 if(locale_name
[MSVCRT_LC_COLLATE
] &&
1398 !init_category_name(locale_name
[MSVCRT_LC_COLLATE
],
1399 locale_len
[MSVCRT_LC_COLLATE
], locinfo
, MSVCRT_LC_COLLATE
)) {
1400 free_locinfo(locinfo
);
1404 if(!category_needs_update(MSVCRT_LC_COLLATE
, old_locinfo
,
1405 lcid
[MSVCRT_LC_COLLATE
], cp
[MSVCRT_LC_COLLATE
])) {
1406 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_COLLATE
);
1407 locinfo
->lc_collate_cp
= old_locinfo
->lc_collate_cp
;
1408 } else if(lcid
[MSVCRT_LC_COLLATE
]) {
1409 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_COLLATE
],
1410 cp
[MSVCRT_LC_COLLATE
], locinfo
, MSVCRT_LC_COLLATE
)) {
1411 free_locinfo(locinfo
);
1415 locinfo
->lc_collate_cp
= locinfo
->lc_id
[MSVCRT_LC_COLLATE
].wCodePage
;
1417 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_COLLATE
)) {
1418 free_locinfo(locinfo
);
1423 if(locale_name
[MSVCRT_LC_CTYPE
] &&
1424 !init_category_name(locale_name
[MSVCRT_LC_CTYPE
],
1425 locale_len
[MSVCRT_LC_CTYPE
], locinfo
, MSVCRT_LC_CTYPE
)) {
1426 free_locinfo(locinfo
);
1430 if(!category_needs_update(MSVCRT_LC_CTYPE
, old_locinfo
,
1431 lcid
[MSVCRT_LC_CTYPE
], cp
[MSVCRT_LC_CTYPE
])) {
1432 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_CTYPE
);
1433 locinfo
->lc_codepage
= old_locinfo
->lc_codepage
;
1434 locinfo
->lc_clike
= old_locinfo
->lc_clike
;
1435 locinfo
->mb_cur_max
= old_locinfo
->mb_cur_max
;
1436 locinfo
->ctype1
= old_locinfo
->ctype1
;
1437 locinfo
->ctype1_refcount
= old_locinfo
->ctype1_refcount
;
1438 locinfo
->pctype
= old_locinfo
->pctype
;
1439 locinfo
->pclmap
= old_locinfo
->pclmap
;
1440 locinfo
->pcumap
= old_locinfo
->pcumap
;
1441 if(locinfo
->ctype1_refcount
)
1442 InterlockedIncrement(locinfo
->ctype1_refcount
);
1443 } else if(lcid
[MSVCRT_LC_CTYPE
]) {
1447 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_CTYPE
],
1448 cp
[MSVCRT_LC_CTYPE
], locinfo
, MSVCRT_LC_CTYPE
)) {
1449 free_locinfo(locinfo
);
1453 locinfo
->lc_codepage
= locinfo
->lc_id
[MSVCRT_LC_CTYPE
].wCodePage
;
1454 locinfo
->lc_clike
= 1;
1455 if(!GetCPInfo(locinfo
->lc_codepage
, &cp_info
)) {
1456 free_locinfo(locinfo
);
1459 locinfo
->mb_cur_max
= cp_info
.MaxCharSize
;
1461 locinfo
->ctype1_refcount
= MSVCRT_malloc(sizeof(int));
1462 if(!locinfo
->ctype1_refcount
) {
1463 free_locinfo(locinfo
);
1466 *locinfo
->ctype1_refcount
= 1;
1468 locinfo
->ctype1
= MSVCRT_malloc(sizeof(short[257]));
1469 locinfo
->pclmap
= MSVCRT_malloc(sizeof(char[256]));
1470 locinfo
->pcumap
= MSVCRT_malloc(sizeof(char[256]));
1471 if(!locinfo
->ctype1
|| !locinfo
->pclmap
|| !locinfo
->pcumap
) {
1472 free_locinfo(locinfo
);
1476 locinfo
->ctype1
[0] = 0;
1477 locinfo
->pctype
= locinfo
->ctype1
+1;
1479 buf
[1] = buf
[2] = '\0';
1480 for(i
=1; i
<257; i
++) {
1483 /* builtin GetStringTypeA doesn't set output to 0 on invalid input */
1484 locinfo
->ctype1
[i
] = 0;
1486 GetStringTypeA(lcid
[MSVCRT_LC_CTYPE
], CT_CTYPE1
, buf
,
1487 1, locinfo
->ctype1
+i
);
1490 for(i
=0; cp_info
.LeadByte
[i
+1]!=0; i
+=2)
1491 for(j
=cp_info
.LeadByte
[i
]; j
<=cp_info
.LeadByte
[i
+1]; j
++)
1492 locinfo
->ctype1
[j
+1] |= MSVCRT__LEADBYTE
;
1494 for(i
=0; i
<256; i
++) {
1495 if(locinfo
->pctype
[i
] & MSVCRT__LEADBYTE
)
1501 LCMapStringA(lcid
[MSVCRT_LC_CTYPE
], LCMAP_LOWERCASE
, buf
, 256,
1502 (char*)locinfo
->pclmap
, 256);
1503 LCMapStringA(lcid
[MSVCRT_LC_CTYPE
], LCMAP_UPPERCASE
, buf
, 256,
1504 (char*)locinfo
->pcumap
, 256);
1506 locinfo
->lc_clike
= 1;
1507 locinfo
->mb_cur_max
= 1;
1508 locinfo
->pctype
= MSVCRT__ctype
+1;
1509 locinfo
->pclmap
= cloc_clmap
;
1510 locinfo
->pcumap
= cloc_cumap
;
1511 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_CTYPE
)) {
1512 free_locinfo(locinfo
);
1517 if(!category_needs_update(MSVCRT_LC_MONETARY
, old_locinfo
,
1518 lcid
[MSVCRT_LC_MONETARY
], cp
[MSVCRT_LC_MONETARY
]) &&
1519 !category_needs_update(MSVCRT_LC_NUMERIC
, old_locinfo
,
1520 lcid
[MSVCRT_LC_NUMERIC
], cp
[MSVCRT_LC_NUMERIC
])) {
1521 locinfo
->lconv
= old_locinfo
->lconv
;
1522 locinfo
->lconv_intl_refcount
= old_locinfo
->lconv_intl_refcount
;
1523 if(locinfo
->lconv_intl_refcount
)
1524 InterlockedIncrement(locinfo
->lconv_intl_refcount
);
1525 } else if(lcid
[MSVCRT_LC_MONETARY
] || lcid
[MSVCRT_LC_NUMERIC
]) {
1526 locinfo
->lconv
= MSVCRT_malloc(sizeof(struct MSVCRT_lconv
));
1527 locinfo
->lconv_intl_refcount
= MSVCRT_malloc(sizeof(int));
1528 if(!locinfo
->lconv
|| !locinfo
->lconv_intl_refcount
) {
1529 MSVCRT_free(locinfo
->lconv
);
1530 MSVCRT_free(locinfo
->lconv_intl_refcount
);
1531 locinfo
->lconv
= NULL
;
1532 locinfo
->lconv_intl_refcount
= NULL
;
1533 free_locinfo(locinfo
);
1536 memset(locinfo
->lconv
, 0, sizeof(struct MSVCRT_lconv
));
1537 *locinfo
->lconv_intl_refcount
= 1;
1539 locinfo
->lconv
= &cloc_lconv
;
1542 if(locale_name
[MSVCRT_LC_MONETARY
] &&
1543 !init_category_name(locale_name
[MSVCRT_LC_MONETARY
],
1544 locale_len
[MSVCRT_LC_MONETARY
], locinfo
, MSVCRT_LC_MONETARY
)) {
1545 free_locinfo(locinfo
);
1549 if(!category_needs_update(MSVCRT_LC_MONETARY
, old_locinfo
,
1550 lcid
[MSVCRT_LC_MONETARY
], cp
[MSVCRT_LC_MONETARY
])) {
1551 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_MONETARY
);
1552 locinfo
->lconv_mon_refcount
= old_locinfo
->lconv_mon_refcount
;
1553 if(locinfo
->lconv_mon_refcount
)
1554 InterlockedIncrement(locinfo
->lconv_mon_refcount
);
1555 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1556 locinfo
->lconv
->int_curr_symbol
= old_locinfo
->lconv
->int_curr_symbol
;
1557 locinfo
->lconv
->currency_symbol
= old_locinfo
->lconv
->currency_symbol
;
1558 locinfo
->lconv
->mon_decimal_point
= old_locinfo
->lconv
->mon_decimal_point
;
1559 locinfo
->lconv
->mon_thousands_sep
= old_locinfo
->lconv
->mon_thousands_sep
;
1560 locinfo
->lconv
->mon_grouping
= old_locinfo
->lconv
->mon_grouping
;
1561 locinfo
->lconv
->positive_sign
= old_locinfo
->lconv
->positive_sign
;
1562 locinfo
->lconv
->negative_sign
= old_locinfo
->lconv
->negative_sign
;
1563 locinfo
->lconv
->int_frac_digits
= old_locinfo
->lconv
->int_frac_digits
;
1564 locinfo
->lconv
->frac_digits
= old_locinfo
->lconv
->frac_digits
;
1565 locinfo
->lconv
->p_cs_precedes
= old_locinfo
->lconv
->p_cs_precedes
;
1566 locinfo
->lconv
->p_sep_by_space
= old_locinfo
->lconv
->p_sep_by_space
;
1567 locinfo
->lconv
->n_cs_precedes
= old_locinfo
->lconv
->n_cs_precedes
;
1568 locinfo
->lconv
->n_sep_by_space
= old_locinfo
->lconv
->n_sep_by_space
;
1569 locinfo
->lconv
->p_sign_posn
= old_locinfo
->lconv
->p_sign_posn
;
1570 locinfo
->lconv
->n_sign_posn
= old_locinfo
->lconv
->n_sign_posn
;
1571 #if _MSVCR_VER >= 100
1572 locinfo
->lconv
->_W_int_curr_symbol
= old_locinfo
->lconv
->_W_int_curr_symbol
;
1573 locinfo
->lconv
->_W_currency_symbol
= old_locinfo
->lconv
->_W_currency_symbol
;
1574 locinfo
->lconv
->_W_mon_decimal_point
= old_locinfo
->lconv
->_W_mon_decimal_point
;
1575 locinfo
->lconv
->_W_mon_thousands_sep
= old_locinfo
->lconv
->_W_mon_thousands_sep
;
1576 locinfo
->lconv
->_W_positive_sign
= old_locinfo
->lconv
->_W_positive_sign
;
1577 locinfo
->lconv
->_W_negative_sign
= old_locinfo
->lconv
->_W_negative_sign
;
1580 } else if(lcid
[MSVCRT_LC_MONETARY
]) {
1581 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_MONETARY
],
1582 cp
[MSVCRT_LC_MONETARY
], locinfo
, MSVCRT_LC_MONETARY
)) {
1583 free_locinfo(locinfo
);
1587 locinfo
->lconv_mon_refcount
= MSVCRT_malloc(sizeof(int));
1588 if(!locinfo
->lconv_mon_refcount
) {
1589 free_locinfo(locinfo
);
1593 *locinfo
->lconv_mon_refcount
= 1;
1595 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SINTLSYMBOL
1596 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1597 if(i
&& (locinfo
->lconv
->int_curr_symbol
= MSVCRT_malloc(i
)))
1598 memcpy(locinfo
->lconv
->int_curr_symbol
, buf
, i
);
1600 free_locinfo(locinfo
);
1604 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SCURRENCY
1605 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1606 if(i
&& (locinfo
->lconv
->currency_symbol
= MSVCRT_malloc(i
)))
1607 memcpy(locinfo
->lconv
->currency_symbol
, buf
, i
);
1609 free_locinfo(locinfo
);
1613 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONDECIMALSEP
1614 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1615 if(i
&& (locinfo
->lconv
->mon_decimal_point
= MSVCRT_malloc(i
)))
1616 memcpy(locinfo
->lconv
->mon_decimal_point
, buf
, i
);
1618 free_locinfo(locinfo
);
1622 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1623 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1624 if(i
&& (locinfo
->lconv
->mon_thousands_sep
= MSVCRT_malloc(i
)))
1625 memcpy(locinfo
->lconv
->mon_thousands_sep
, buf
, i
);
1627 free_locinfo(locinfo
);
1631 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONGROUPING
1632 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1634 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1635 if(i
&& (locinfo
->lconv
->mon_grouping
= MSVCRT_malloc(i
))) {
1636 for(i
=0; buf
[i
+1]==';'; i
+=2)
1637 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1638 locinfo
->lconv
->mon_grouping
[i
/2] = buf
[i
]-'0';
1640 locinfo
->lconv
->mon_grouping
[i
/2+1] = 127;
1642 free_locinfo(locinfo
);
1646 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SPOSITIVESIGN
1647 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1648 if(i
&& (locinfo
->lconv
->positive_sign
= MSVCRT_malloc(i
)))
1649 memcpy(locinfo
->lconv
->positive_sign
, buf
, i
);
1651 free_locinfo(locinfo
);
1655 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SNEGATIVESIGN
1656 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1657 if(i
&& (locinfo
->lconv
->negative_sign
= MSVCRT_malloc(i
)))
1658 memcpy(locinfo
->lconv
->negative_sign
, buf
, i
);
1660 free_locinfo(locinfo
);
1664 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IINTLCURRDIGITS
1665 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1666 locinfo
->lconv
->int_frac_digits
= val
;
1668 free_locinfo(locinfo
);
1672 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_ICURRDIGITS
1673 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1674 locinfo
->lconv
->frac_digits
= val
;
1676 free_locinfo(locinfo
);
1680 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IPOSSYMPRECEDES
1681 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1682 locinfo
->lconv
->p_cs_precedes
= val
;
1684 free_locinfo(locinfo
);
1688 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IPOSSEPBYSPACE
1689 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1690 locinfo
->lconv
->p_sep_by_space
= val
;
1692 free_locinfo(locinfo
);
1696 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_INEGSYMPRECEDES
1697 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1698 locinfo
->lconv
->n_cs_precedes
= val
;
1700 free_locinfo(locinfo
);
1704 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_INEGSEPBYSPACE
1705 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1706 locinfo
->lconv
->n_sep_by_space
= val
;
1708 free_locinfo(locinfo
);
1712 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_IPOSSIGNPOSN
1713 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1714 locinfo
->lconv
->p_sign_posn
= val
;
1716 free_locinfo(locinfo
);
1720 if(GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_INEGSIGNPOSN
1721 |LOCALE_NOUSEROVERRIDE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&val
, 2))
1722 locinfo
->lconv
->n_sign_posn
= val
;
1724 free_locinfo(locinfo
);
1728 #if _MSVCR_VER >= 100
1729 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SINTLSYMBOL
1730 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1731 if(i
&& (locinfo
->lconv
->_W_int_curr_symbol
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1732 memcpy(locinfo
->lconv
->_W_int_curr_symbol
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1734 free_locinfo(locinfo
);
1738 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SCURRENCY
1739 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1740 if(i
&& (locinfo
->lconv
->_W_currency_symbol
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1741 memcpy(locinfo
->lconv
->_W_currency_symbol
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1743 free_locinfo(locinfo
);
1747 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONDECIMALSEP
1748 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1749 if(i
&& (locinfo
->lconv
->_W_mon_decimal_point
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1750 memcpy(locinfo
->lconv
->_W_mon_decimal_point
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1752 free_locinfo(locinfo
);
1756 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SMONTHOUSANDSEP
1757 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1758 if(i
&& (locinfo
->lconv
->_W_mon_thousands_sep
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1759 memcpy(locinfo
->lconv
->_W_mon_thousands_sep
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1761 free_locinfo(locinfo
);
1765 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SPOSITIVESIGN
1766 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1767 if(i
&& (locinfo
->lconv
->_W_positive_sign
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1768 memcpy(locinfo
->lconv
->_W_positive_sign
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1770 free_locinfo(locinfo
);
1774 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_MONETARY
], LOCALE_SNEGATIVESIGN
1775 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1776 if(i
&& (locinfo
->lconv
->_W_negative_sign
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1777 memcpy(locinfo
->lconv
->_W_negative_sign
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1779 free_locinfo(locinfo
);
1784 if (locinfo
->lconv
!= &cloc_lconv
) {
1785 locinfo
->lconv
->int_curr_symbol
= cloc_lconv
.int_curr_symbol
;
1786 locinfo
->lconv
->currency_symbol
= cloc_lconv
.currency_symbol
;
1787 locinfo
->lconv
->mon_decimal_point
= cloc_lconv
.mon_decimal_point
;
1788 locinfo
->lconv
->mon_thousands_sep
= cloc_lconv
.mon_thousands_sep
;
1789 locinfo
->lconv
->mon_grouping
= cloc_lconv
.mon_grouping
;
1790 locinfo
->lconv
->positive_sign
= cloc_lconv
.positive_sign
;
1791 locinfo
->lconv
->negative_sign
= cloc_lconv
.negative_sign
;
1792 locinfo
->lconv
->int_frac_digits
= cloc_lconv
.int_frac_digits
;
1793 locinfo
->lconv
->frac_digits
= cloc_lconv
.frac_digits
;
1794 locinfo
->lconv
->p_cs_precedes
= cloc_lconv
.p_cs_precedes
;
1795 locinfo
->lconv
->p_sep_by_space
= cloc_lconv
.p_sep_by_space
;
1796 locinfo
->lconv
->n_cs_precedes
= cloc_lconv
.n_cs_precedes
;
1797 locinfo
->lconv
->n_sep_by_space
= cloc_lconv
.n_sep_by_space
;
1798 locinfo
->lconv
->p_sign_posn
= cloc_lconv
.p_sign_posn
;
1799 locinfo
->lconv
->n_sign_posn
= cloc_lconv
.n_sign_posn
;
1801 #if _MSVCR_VER >= 100
1802 locinfo
->lconv
->_W_int_curr_symbol
= cloc_lconv
._W_int_curr_symbol
;
1803 locinfo
->lconv
->_W_currency_symbol
= cloc_lconv
._W_currency_symbol
;
1804 locinfo
->lconv
->_W_mon_decimal_point
= cloc_lconv
._W_mon_decimal_point
;
1805 locinfo
->lconv
->_W_mon_thousands_sep
= cloc_lconv
._W_mon_thousands_sep
;
1806 locinfo
->lconv
->_W_positive_sign
= cloc_lconv
._W_positive_sign
;
1807 locinfo
->lconv
->_W_negative_sign
= cloc_lconv
._W_negative_sign
;
1811 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_MONETARY
)) {
1812 free_locinfo(locinfo
);
1817 if(locale_name
[MSVCRT_LC_NUMERIC
] &&
1818 !init_category_name(locale_name
[MSVCRT_LC_NUMERIC
],
1819 locale_len
[MSVCRT_LC_NUMERIC
], locinfo
, MSVCRT_LC_NUMERIC
)) {
1820 free_locinfo(locinfo
);
1824 if(!category_needs_update(MSVCRT_LC_NUMERIC
, old_locinfo
,
1825 lcid
[MSVCRT_LC_NUMERIC
], cp
[MSVCRT_LC_NUMERIC
])) {
1826 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_NUMERIC
);
1827 locinfo
->lconv_num_refcount
= old_locinfo
->lconv_num_refcount
;
1828 if(locinfo
->lconv_num_refcount
)
1829 InterlockedIncrement(locinfo
->lconv_num_refcount
);
1830 if(locinfo
->lconv
!= &cloc_lconv
&& locinfo
->lconv
!= old_locinfo
->lconv
) {
1831 locinfo
->lconv
->decimal_point
= old_locinfo
->lconv
->decimal_point
;
1832 locinfo
->lconv
->thousands_sep
= old_locinfo
->lconv
->thousands_sep
;
1833 locinfo
->lconv
->grouping
= old_locinfo
->lconv
->grouping
;
1834 #if _MSVCR_VER >= 100
1835 locinfo
->lconv
->_W_decimal_point
= old_locinfo
->lconv
->_W_decimal_point
;
1836 locinfo
->lconv
->_W_thousands_sep
= old_locinfo
->lconv
->_W_thousands_sep
;
1839 } else if(lcid
[MSVCRT_LC_NUMERIC
]) {
1840 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_NUMERIC
],
1841 cp
[MSVCRT_LC_NUMERIC
], locinfo
, MSVCRT_LC_NUMERIC
)) {
1842 free_locinfo(locinfo
);
1846 locinfo
->lconv_num_refcount
= MSVCRT_malloc(sizeof(int));
1847 if(!locinfo
->lconv_num_refcount
) {
1848 free_locinfo(locinfo
);
1852 *locinfo
->lconv_num_refcount
= 1;
1854 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_SDECIMAL
1855 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1856 if(i
&& (locinfo
->lconv
->decimal_point
= MSVCRT_malloc(i
)))
1857 memcpy(locinfo
->lconv
->decimal_point
, buf
, i
);
1859 free_locinfo(locinfo
);
1863 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_STHOUSAND
1864 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1865 if(i
&& (locinfo
->lconv
->thousands_sep
= MSVCRT_malloc(i
)))
1866 memcpy(locinfo
->lconv
->thousands_sep
, buf
, i
);
1868 free_locinfo(locinfo
);
1872 i
= GetLocaleInfoA(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_SGROUPING
1873 |LOCALE_NOUSEROVERRIDE
, buf
, 256);
1875 i
= i
/2 + (buf
[i
-2]=='0'?0:1);
1876 if(i
&& (locinfo
->lconv
->grouping
= MSVCRT_malloc(i
))) {
1877 for(i
=0; buf
[i
+1]==';'; i
+=2)
1878 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1879 locinfo
->lconv
->grouping
[i
/2] = buf
[i
]-'0';
1881 locinfo
->lconv
->grouping
[i
/2+1] = 127;
1883 free_locinfo(locinfo
);
1887 #if _MSVCR_VER >= 100
1888 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_SDECIMAL
1889 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1890 if(i
&& (locinfo
->lconv
->_W_decimal_point
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1891 memcpy(locinfo
->lconv
->_W_decimal_point
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1893 free_locinfo(locinfo
);
1897 i
= GetLocaleInfoW(lcid
[MSVCRT_LC_NUMERIC
], LOCALE_STHOUSAND
1898 |LOCALE_NOUSEROVERRIDE
, wbuf
, 256);
1899 if(i
&& (locinfo
->lconv
->_W_thousands_sep
= MSVCRT_malloc(i
* sizeof(MSVCRT_wchar_t
))))
1900 memcpy(locinfo
->lconv
->_W_thousands_sep
, wbuf
, i
* sizeof(MSVCRT_wchar_t
));
1902 free_locinfo(locinfo
);
1907 if (locinfo
->lconv
!= &cloc_lconv
) {
1908 locinfo
->lconv
->decimal_point
= cloc_lconv
.decimal_point
;
1909 locinfo
->lconv
->thousands_sep
= cloc_lconv
.thousands_sep
;
1910 locinfo
->lconv
->grouping
= cloc_lconv
.grouping
;
1912 #if _MSVCR_VER >= 100
1913 locinfo
->lconv
->_W_decimal_point
= cloc_lconv
._W_decimal_point
;
1914 locinfo
->lconv
->_W_thousands_sep
= cloc_lconv
._W_thousands_sep
;
1918 if (!init_category_name("C", 1, locinfo
, MSVCRT_LC_NUMERIC
)) {
1919 free_locinfo(locinfo
);
1924 if(locale_name
[MSVCRT_LC_TIME
] &&
1925 !init_category_name(locale_name
[MSVCRT_LC_TIME
],
1926 locale_len
[MSVCRT_LC_TIME
], locinfo
, MSVCRT_LC_TIME
)) {
1927 free_locinfo(locinfo
);
1931 if(!category_needs_update(MSVCRT_LC_TIME
, old_locinfo
,
1932 lcid
[MSVCRT_LC_TIME
], cp
[MSVCRT_LC_TIME
])) {
1933 copy_threadlocinfo_category(locinfo
, old_locinfo
, MSVCRT_LC_TIME
);
1934 locinfo
->lc_time_curr
= old_locinfo
->lc_time_curr
;
1935 if(locinfo
->lc_time_curr
!= &cloc_time_data
)
1936 InterlockedIncrement(&locinfo
->lc_time_curr
->refcount
);
1937 } else if(lcid
[MSVCRT_LC_TIME
]) {
1938 if(!update_threadlocinfo_category(lcid
[MSVCRT_LC_TIME
],
1939 cp
[MSVCRT_LC_TIME
], locinfo
, MSVCRT_LC_TIME
)) {
1940 free_locinfo(locinfo
);
1944 locinfo
->lc_time_curr
= create_time_data(lcid
[MSVCRT_LC_TIME
]);
1945 if(!locinfo
->lc_time_curr
) {
1946 free_locinfo(locinfo
);
1950 if(!init_category_name("C", 1, locinfo
, MSVCRT_LC_TIME
)) {
1951 free_locinfo(locinfo
);
1954 locinfo
->lc_time_curr
= &cloc_time_data
;
1960 /*********************************************************************
1961 * _create_locale (MSVCRT.@)
1963 MSVCRT__locale_t CDECL
MSVCRT__create_locale(int category
, const char *locale
)
1965 MSVCRT__locale_t loc
;
1967 loc
= MSVCRT_malloc(sizeof(MSVCRT__locale_tstruct
));
1971 loc
->locinfo
= create_locinfo(category
, locale
, NULL
);
1977 loc
->mbcinfo
= create_mbcinfo(loc
->locinfo
->lc_id
[MSVCRT_LC_CTYPE
].wCodePage
,
1978 loc
->locinfo
->lc_handle
[MSVCRT_LC_CTYPE
], NULL
);
1980 free_locinfo(loc
->locinfo
);
1987 #if _MSVCR_VER >= 110
1988 /*********************************************************************
1989 * _wcreate_locale (MSVCR110.@)
1991 MSVCRT__locale_t CDECL
MSVCRT__wcreate_locale(int category
, const MSVCRT_wchar_t
*locale
)
1993 MSVCRT__locale_t loc
;
1997 if(category
<MSVCRT_LC_MIN
|| category
>MSVCRT_LC_MAX
|| !locale
)
2000 len
= MSVCRT_wcstombs(NULL
, locale
, 0);
2003 if(!(str
= MSVCRT_malloc(++len
)))
2005 MSVCRT_wcstombs(str
, locale
, len
);
2007 loc
= MSVCRT__create_locale(category
, str
);
2014 /*********************************************************************
2015 * setlocale (MSVCRT.@)
2017 char* CDECL
MSVCRT_setlocale(int category
, const char* locale
)
2019 MSVCRT_pthreadlocinfo
*plocinfo
= get_locinfo_ptr();
2020 MSVCRT_pthreadlocinfo locinfo
= *plocinfo
, newlocinfo
;
2022 if(category
<MSVCRT_LC_MIN
|| category
>MSVCRT_LC_MAX
)
2026 if(category
== MSVCRT_LC_ALL
)
2027 return construct_lc_all(locinfo
);
2029 return locinfo
->lc_category
[category
].locale
;
2032 newlocinfo
= create_locinfo(category
, locale
, locinfo
);
2034 WARN("%d %s failed\n", category
, locale
);
2038 if(locale
[0] != 'C' || locale
[1] != '\0')
2039 initial_locale
= FALSE
;
2042 free_locinfo(locinfo
);
2043 *plocinfo
= newlocinfo
;
2046 if(newlocinfo
== MSVCRT_locale
->locinfo
) {
2049 MSVCRT___lc_codepage
= newlocinfo
->lc_codepage
;
2050 MSVCRT___lc_collate_cp
= newlocinfo
->lc_collate_cp
;
2051 MSVCRT___mb_cur_max
= newlocinfo
->mb_cur_max
;
2052 MSVCRT__pctype
= newlocinfo
->pctype
;
2053 for(i
=MSVCRT_LC_MIN
; i
<=MSVCRT_LC_MAX
; i
++)
2054 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2057 if(category
== MSVCRT_LC_ALL
)
2058 return construct_lc_all(newlocinfo
);
2060 return newlocinfo
->lc_category
[category
].locale
;
2063 /*********************************************************************
2064 * _wsetlocale (MSVCRT.@)
2066 MSVCRT_wchar_t
* CDECL
MSVCRT__wsetlocale(int category
, const MSVCRT_wchar_t
* wlocale
)
2068 static MSVCRT_wchar_t current_lc_all
[MAX_LOCALE_LENGTH
];
2070 char *locale
= NULL
;
2075 len
= MSVCRT_wcstombs(NULL
, wlocale
, 0);
2079 locale
= MSVCRT_malloc(++len
);
2083 MSVCRT_wcstombs(locale
, wlocale
, len
);
2087 ret
= MSVCRT_setlocale(category
, locale
);
2088 MSVCRT_free(locale
);
2090 if(ret
&& MSVCRT_mbstowcs(current_lc_all
, ret
, MAX_LOCALE_LENGTH
)==-1)
2094 return ret
? current_lc_all
: NULL
;
2097 #if _MSVCR_VER >= 80
2098 /*********************************************************************
2099 * _configthreadlocale (MSVCR80.@)
2101 int CDECL
_configthreadlocale(int type
)
2103 thread_data_t
*data
= msvcrt_get_thread_data();
2104 MSVCRT__locale_t locale
;
2110 ret
= (data
->have_locale
? MSVCRT__ENABLE_PER_THREAD_LOCALE
: MSVCRT__DISABLE_PER_THREAD_LOCALE
);
2112 if(type
== MSVCRT__ENABLE_PER_THREAD_LOCALE
) {
2113 if(!data
->have_locale
) {
2114 /* Copy current global locale */
2115 locale
= MSVCRT__create_locale(MSVCRT_LC_ALL
, MSVCRT_setlocale(MSVCRT_LC_ALL
, NULL
));
2119 data
->locinfo
= locale
->locinfo
;
2120 data
->mbcinfo
= locale
->mbcinfo
;
2121 data
->have_locale
= TRUE
;
2122 MSVCRT_free(locale
);
2128 if(type
== MSVCRT__DISABLE_PER_THREAD_LOCALE
) {
2129 if(data
->have_locale
) {
2130 free_locinfo(data
->locinfo
);
2131 free_mbcinfo(data
->mbcinfo
);
2132 data
->locinfo
= MSVCRT_locale
->locinfo
;
2133 data
->mbcinfo
= MSVCRT_locale
->mbcinfo
;
2134 data
->have_locale
= FALSE
;
2147 BOOL
msvcrt_init_locale(void)
2152 MSVCRT_locale
= MSVCRT__create_locale(0, "C");
2157 MSVCRT___lc_codepage
= MSVCRT_locale
->locinfo
->lc_codepage
;
2158 MSVCRT___lc_collate_cp
= MSVCRT_locale
->locinfo
->lc_collate_cp
;
2159 MSVCRT___mb_cur_max
= MSVCRT_locale
->locinfo
->mb_cur_max
;
2160 MSVCRT__pctype
= MSVCRT_locale
->locinfo
->pctype
;
2161 for(i
=MSVCRT_LC_MIN
; i
<=MSVCRT_LC_MAX
; i
++)
2162 MSVCRT___lc_handle
[i
] = MSVCRT_locale
->locinfo
->lc_handle
[i
];
2163 _setmbcp(_MB_CP_ANSI
);
2167 #if _MSVCR_VER >= 120
2168 /*********************************************************************
2169 * wctrans (MSVCR120.@)
2171 wctrans_t CDECL
MSVCR120_wctrans(const char *property
)
2173 static const char str_tolower
[] = "tolower";
2174 static const char str_toupper
[] = "toupper";
2176 if(!strcmp(property
, str_tolower
))
2178 if(!strcmp(property
, str_toupper
))
2183 /*********************************************************************
2184 * towctrans (MSVCR120.@)
2186 MSVCRT_wint_t CDECL
MSVCR120_towctrans(MSVCRT_wint_t c
, wctrans_t category
)
2189 return MSVCRT__towupper_l(c
, NULL
);
2190 return MSVCRT__towlower_l(c
, NULL
);