msvcrt: Move towctrans to wcs.c file.
[wine.git] / dlls / msvcrt / locale.c
blob52fbd9cf9c7537a03f518db7d71f45d325946ceb
1 /*
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
21 #include <limits.h>
22 #include <locale.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <mbctype.h>
27 #include <wctype.h>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "winnls.h"
34 #include "msvcrt.h"
35 #include "mtdll.h"
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};
97 #if _MSVCR_VER >= 110
98 static const MSVCRT_wchar_t en_us[] = {'e','n','-','U','S',0};
99 #endif
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"}},
109 #if _MSVCR_VER < 110
110 MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
111 #endif
112 1, 0,
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
120 en_us,
121 #endif
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};
201 #endif
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
208 #endif
211 /* Friendly country strings & language names abbreviations. */
212 static const char * const _country_synonyms[] =
214 "american", "enu",
215 "american english", "enu",
216 "american-english", "enu",
217 "english-american", "enu",
218 "english-us", "enu",
219 "english-usa", "enu",
220 "us", "enu",
221 "usa", "enu",
222 "australian", "ena",
223 "english-aus", "ena",
224 "belgian", "nlb",
225 "french-belgian", "frb",
226 "canadian", "enc",
227 "english-can", "enc",
228 "french-canadian", "frc",
229 "chinese", "chs",
230 "chinese-simplified", "chs",
231 "chinese-traditional", "cht",
232 "dutch-belgian", "nlb",
233 "english-nz", "enz",
234 "uk", "eng",
235 "english-uk", "eng",
236 "french-swiss", "frs",
237 "swiss", "des",
238 "german-swiss", "des",
239 "italian-swiss", "its",
240 "german-austrian", "dea",
241 "portuguese", "ptb",
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)
252 unsigned int i;
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]);
259 return;
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
269 typedef struct {
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;
275 BOOL allow_sname;
276 } locale_search_t;
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)
284 int len;
286 if(!cmp[0])
287 return 0;
289 buff[0] = 0;
290 GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN);
291 if (!buff[0])
292 return 0;
294 /* Partial matches are only allowed on language/country names */
295 len = strlen(cmp);
296 if(exact || len<=3)
297 return !MSVCRT__stricmp(cmp, buff);
298 else
299 return !MSVCRT__strnicmp(cmp, buff, len);
302 static BOOL CALLBACK
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;
319 return STOP_LOOKING;
321 #endif
323 /* Check Language */
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;
336 /* Check Country */
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");
359 return STOP_LOOKING;
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;
370 DWORD locale_cp;
371 LCID lcid;
373 if (!strcmp(locale, data->cached_locale)) {
374 if (codepage)
375 *codepage = data->cached_cp;
376 if (sname)
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();
386 } else {
387 locale_search_t search;
389 memset(&search, 0, sizeof(locale_search_t));
390 lstrcpynA(search.search_language, locale, MAX_ELEM_LEN);
391 if(region) {
392 lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN);
393 if(region-locale < MAX_ELEM_LEN)
394 search.search_language[region-locale] = '\0';
395 } else
396 search.search_country[0] = '\0';
398 if(cp) {
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';
405 if(!cp && !region)
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,
416 (LONG_PTR)&search);
418 if (!search.match_flags)
419 return -1;
421 /* If we were given something that didn't match, fail */
422 if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE)))
423 return -1;
424 if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
425 return -1;
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));
435 if (!locale_cp)
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)) {
443 locale_cp = CP_UTF8;
444 #endif
445 } else {
446 locale_cp = MSVCRT_atoi(cp + 1);
448 if (!IsValidCodePage(locale_cp))
449 return -1;
451 if (!locale_cp)
452 return -1;
454 if (codepage)
455 *codepage = locale_cp;
456 if (sname)
457 *sname = is_sname;
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;
466 return lcid;
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);
484 #endif
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;
498 return FALSE;
501 memcpy(locinfo->lc_category[category].locale, name, len);
502 locinfo->lc_category[category].locale[len] = 0;
503 *locinfo->lc_category[category].refcount = 1;
504 return TRUE;
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];
511 WCHAR buf[100];
512 int len;
514 locinfo->lc_category[cat].wrefcount = MSVCRT_malloc(sizeof(int));
515 if(!locinfo->lc_category[cat].wrefcount)
516 return FALSE;
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))))
527 return FALSE;
529 memcpy(locinfo->lc_name[cat], buf, len*sizeof(MSVCRT_wchar_t));
530 return TRUE;
532 #else
533 static inline BOOL set_lc_locale_name(MSVCRT_pthreadlocinfo locinfo, int cat)
535 return TRUE;
537 #endif
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)
543 char buf[256], *p;
545 if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) {
546 p = buf;
548 locinfo->lc_id[category].wLanguage = 0;
549 while(*p) {
550 locinfo->lc_id[category].wLanguage *= 16;
552 if(*p <= '9')
553 locinfo->lc_id[category].wLanguage += *p-'0';
554 else
555 locinfo->lc_id[category].wLanguage += *p-'a'+10;
557 p++;
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) {
571 int len = 0;
573 len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE
574 |LOCALE_NOUSEROVERRIDE, buf, 256);
575 buf[len-1] = '_';
576 len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY
577 |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len);
578 buf[len-1] = '.';
579 MSVCRT_sprintf(buf+len, "%d", cp);
580 len += strlen(buf+len);
582 return init_category_name(buf, len, locinfo, category);
585 return TRUE;
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)
606 int i;
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)))
630 return;
632 if(data->locale_flags & LOCALE_FREE)
634 free_locinfo(data->locinfo);
635 free_mbcinfo(data->mbcinfo);
638 _lock_locales();
639 data->locinfo = MSVCRT_locale->locinfo;
640 grab_locinfo(data->locinfo);
641 _unlock_locales();
643 _lock(_MB_CP_LOCK);
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];
669 int i;
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))
674 break;
677 if(i==MSVCRT_LC_MAX)
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;
699 char *out;
701 TRACE("\n");
703 for(i=0; i<7; i++) {
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);
708 if(!out)
709 return NULL;
711 size = 0;
712 for(i=0; i<7; i++) {
713 out[size++] = ':';
714 len = strlen(cur->str.names.short_wday[i]);
715 memcpy(&out[size], cur->str.names.short_wday[i], len);
716 size += len;
718 out[size++] = ':';
719 len = strlen(cur->str.names.wday[i]);
720 memcpy(&out[size], cur->str.names.wday[i], len);
721 size += len;
723 out[size] = '\0';
725 return out;
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;
735 MSVCRT_wchar_t *out;
736 int i, len, size = 0;
738 TRACE("\n");
740 for(i=0; i<7; i++) {
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));
745 if(!out)
746 return NULL;
748 size = 0;
749 for(i=0; i<7; i++) {
750 out[size++] = ':';
751 len = MSVCRT_wcslen(cur->wstr.names.short_wday[i]);
752 memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
753 size += len;
755 out[size++] = ':';
756 len = MSVCRT_wcslen(cur->wstr.names.wday[i]);
757 memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
758 size += len;
760 out[size] = '\0';
762 return out;
764 #endif
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;
773 char *out;
775 TRACE("\n");
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);
782 if(!out)
783 return NULL;
785 size = 0;
786 for(i=0; i<12; i++) {
787 out[size++] = ':';
788 len = strlen(cur->str.names.short_mon[i]);
789 memcpy(&out[size], cur->str.names.short_mon[i], len);
790 size += len;
792 out[size++] = ':';
793 len = strlen(cur->str.names.mon[i]);
794 memcpy(&out[size], cur->str.names.mon[i], len);
795 size += len;
797 out[size] = '\0';
799 return out;
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;
809 MSVCRT_wchar_t *out;
810 int i, len, size = 0;
812 TRACE("\n");
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));
819 if(!out)
820 return NULL;
822 size = 0;
823 for(i=0; i<12; i++) {
824 out[size++] = ':';
825 len = MSVCRT_wcslen(cur->wstr.names.short_mon[i]);
826 memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
827 size += len;
829 out[size++] = ':';
830 len = MSVCRT_wcslen(cur->wstr.names.mon[i]);
831 memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
832 size += len;
834 out[size] = '\0';
836 return out;
838 #endif
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);
848 TRACE("\n");
850 for(i=0; i<ARRAY_SIZE(cur->str.str); i++)
851 size += strlen(cur->str.str[i])+1;
853 ret = MSVCRT_malloc(size);
854 if(!ret)
855 return NULL;
856 memcpy(ret, cur, sizeof(*ret));
858 size = 0;
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];
863 size += len;
866 return ret;
869 #if _MSVCR_VER >= 110
870 /*********************************************************************
871 * _W_Gettnames (MSVCR110.@)
873 void* CDECL _W_Gettnames(void)
875 return _Gettnames();
877 #endif
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));
898 if (!in) return 0;
901 r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len);
902 if (!r) goto done;
904 if (mapflags & LCMAP_SORTKEY)
906 r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen);
907 goto done;
910 r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0);
911 if (!r) goto done;
912 out_len = r;
913 if (r > ARRAY_SIZE(buf_out))
915 out = MSVCRT_malloc(r * sizeof(WCHAR));
916 if (!out)
918 r = 0;
919 goto done;
923 r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len);
924 if (!r) goto done;
926 r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL);
928 done:
929 if (in != buf_in) MSVCRT_free(in);
930 if (out != buf_out) MSVCRT_free(out);
931 return r;
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);
989 #endif
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;
1028 return 0;
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;
1047 #endif
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)
1068 int i;
1070 if(!locinfo)
1071 return;
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]);
1083 #endif
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);
1096 #endif
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);
1115 #endif
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))
1137 return;
1139 MSVCRT_free(locinfo);
1142 /* INTERNAL: frees MSVCRT_pthreadmbcinfo struct */
1143 void free_mbcinfo(MSVCRT_pthreadmbcinfo mbcinfo)
1145 if(!mbcinfo)
1146 return;
1148 if(InterlockedDecrement(&mbcinfo->refcount))
1149 return;
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);
1164 return locale;
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));
1179 if(!loc)
1180 return NULL;
1182 return get_current_locale_noalloc(loc);
1185 /*********************************************************************
1186 * _free_locale (MSVCRT.@)
1188 void CDECL MSVCRT__free_locale(MSVCRT__locale_t locale)
1190 if (!locale)
1191 return;
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,
1221 LOCALE_STIMEFORMAT
1224 MSVCRT___lc_time_data *cur;
1225 int i, ret, size;
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);
1230 if(!ret)
1231 return NULL;
1232 size += ret;
1234 ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0);
1235 if(!ret)
1236 return NULL;
1237 size += ret*sizeof(MSVCRT_wchar_t);
1239 #if _MSVCR_VER >= 110
1240 size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(MSVCRT_wchar_t);
1241 #endif
1243 cur = MSVCRT_malloc(size);
1244 if(!cur)
1245 return NULL;
1247 ret = 0;
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);
1260 #else
1261 cur->lcid = lcid;
1262 #endif
1263 cur->unk = 1;
1264 cur->refcount = 1;
1266 return cur;
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 };
1283 char buf[256];
1284 BOOL sname;
1285 #if _MSVCR_VER >= 100
1286 MSVCRT_wchar_t wbuf[256];
1287 #endif
1288 int i;
1290 TRACE("(%d %s)\n", category, locale);
1292 if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
1293 return NULL;
1295 if(locale[0]=='C' && !locale[1]) {
1296 lcid[0] = 0;
1297 cp[0] = CP_ACP;
1298 } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
1299 const char *p;
1301 while(1) {
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)) {
1316 i = MSVCRT_LC_TIME;
1317 locale += sizeof(time)-1;
1318 } else
1319 return NULL;
1321 p = strchr(locale, ';');
1322 if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
1323 lcid[i] = 0;
1324 cp[i] = CP_ACP;
1325 } else if(p) {
1326 memcpy(buf, locale, p-locale);
1327 buf[p-locale] = '\0';
1328 lcid[i] = MSVCRT_locale_to_LCID(buf, &cp[i], &sname);
1329 if(sname) {
1330 locale_name[i] = locale;
1331 locale_len[i] = p-locale;
1333 } else {
1334 lcid[i] = MSVCRT_locale_to_LCID(locale, &cp[i], &sname);
1335 if(sname) {
1336 locale_name[i] = locale;
1337 locale_len[i] = strlen(locale);
1341 if(lcid[i] == -1)
1342 return NULL;
1344 if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
1345 break;
1347 locale = p+1;
1349 } else {
1350 lcid[0] = MSVCRT_locale_to_LCID(locale, &cp[0], &sname);
1351 if(lcid[0] == -1)
1352 return NULL;
1353 if(sname) {
1354 locale_name[0] = locale;
1355 locale_len[0] = strlen(locale);
1358 for(i=1; i<6; i++) {
1359 lcid[i] = lcid[0];
1360 cp[i] = cp[0];
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) {
1368 if(old_locinfo) {
1369 lcid[i] = old_locinfo->lc_handle[i];
1370 cp[i] = old_locinfo->lc_id[i].wCodePage;
1371 } else {
1372 lcid[i] = 0;
1373 cp[i] = 0;
1378 locinfo = MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo));
1379 if(!locinfo)
1380 return NULL;
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);
1389 return NULL;
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);
1400 return NULL;
1403 locinfo->lc_collate_cp = locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage;
1404 } else {
1405 if(!init_category_name("C", 1, locinfo, MSVCRT_LC_COLLATE)) {
1406 free_locinfo(locinfo);
1407 return NULL;
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);
1415 return NULL;
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]) {
1432 CPINFO cp_info;
1433 int j;
1435 if(!update_threadlocinfo_category(lcid[MSVCRT_LC_CTYPE],
1436 cp[MSVCRT_LC_CTYPE], locinfo, MSVCRT_LC_CTYPE)) {
1437 free_locinfo(locinfo);
1438 return NULL;
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);
1445 return NULL;
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);
1452 return NULL;
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);
1461 return NULL;
1464 locinfo->ctype1[0] = 0;
1465 locinfo->pctype = locinfo->ctype1+1;
1467 buf[1] = buf[2] = '\0';
1468 for(i=1; i<257; i++) {
1469 buf[0] = i-1;
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)
1484 buf[i] = ' ';
1485 else
1486 buf[i] = i;
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);
1493 } else {
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);
1501 return NULL;
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);
1522 return NULL;
1524 memset(locinfo->lconv, 0, sizeof(struct MSVCRT_lconv));
1525 *locinfo->lconv_intl_refcount = 1;
1526 } else {
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);
1534 return NULL;
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;
1566 #endif
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);
1572 return NULL;
1575 locinfo->lconv_mon_refcount = MSVCRT_malloc(sizeof(int));
1576 if(!locinfo->lconv_mon_refcount) {
1577 free_locinfo(locinfo);
1578 return NULL;
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);
1587 else {
1588 free_locinfo(locinfo);
1589 return NULL;
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);
1596 else {
1597 free_locinfo(locinfo);
1598 return NULL;
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);
1605 else {
1606 free_locinfo(locinfo);
1607 return NULL;
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);
1614 else {
1615 free_locinfo(locinfo);
1616 return NULL;
1619 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONGROUPING
1620 |LOCALE_NOUSEROVERRIDE, buf, 256);
1621 if(i>1)
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';
1627 if(buf[i] != '0')
1628 locinfo->lconv->mon_grouping[i/2+1] = 127;
1629 } else {
1630 free_locinfo(locinfo);
1631 return NULL;
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);
1638 else {
1639 free_locinfo(locinfo);
1640 return NULL;
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);
1647 else {
1648 free_locinfo(locinfo);
1649 return NULL;
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;
1655 else {
1656 free_locinfo(locinfo);
1657 return NULL;
1660 if(GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_ICURRDIGITS
1661 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1662 locinfo->lconv->frac_digits = val;
1663 else {
1664 free_locinfo(locinfo);
1665 return NULL;
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;
1671 else {
1672 free_locinfo(locinfo);
1673 return NULL;
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;
1679 else {
1680 free_locinfo(locinfo);
1681 return NULL;
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;
1687 else {
1688 free_locinfo(locinfo);
1689 return NULL;
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;
1695 else {
1696 free_locinfo(locinfo);
1697 return NULL;
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;
1703 else {
1704 free_locinfo(locinfo);
1705 return NULL;
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;
1711 else {
1712 free_locinfo(locinfo);
1713 return NULL;
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));
1721 else {
1722 free_locinfo(locinfo);
1723 return NULL;
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));
1730 else {
1731 free_locinfo(locinfo);
1732 return NULL;
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));
1739 else {
1740 free_locinfo(locinfo);
1741 return NULL;
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));
1748 else {
1749 free_locinfo(locinfo);
1750 return NULL;
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));
1757 else {
1758 free_locinfo(locinfo);
1759 return NULL;
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));
1766 else {
1767 free_locinfo(locinfo);
1768 return NULL;
1770 #endif
1771 } else {
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;
1796 #endif
1799 if(!init_category_name("C", 1, locinfo, MSVCRT_LC_MONETARY)) {
1800 free_locinfo(locinfo);
1801 return NULL;
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);
1809 return NULL;
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;
1825 #endif
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);
1831 return NULL;
1834 locinfo->lconv_num_refcount = MSVCRT_malloc(sizeof(int));
1835 if(!locinfo->lconv_num_refcount) {
1836 free_locinfo(locinfo);
1837 return NULL;
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);
1846 else {
1847 free_locinfo(locinfo);
1848 return NULL;
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);
1855 else {
1856 free_locinfo(locinfo);
1857 return NULL;
1860 i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SGROUPING
1861 |LOCALE_NOUSEROVERRIDE, buf, 256);
1862 if(i>1)
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';
1868 if(buf[i] != '0')
1869 locinfo->lconv->grouping[i/2+1] = 127;
1870 } else {
1871 free_locinfo(locinfo);
1872 return NULL;
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));
1880 else {
1881 free_locinfo(locinfo);
1882 return NULL;
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));
1889 else {
1890 free_locinfo(locinfo);
1891 return NULL;
1893 #endif
1894 } else {
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;
1903 #endif
1906 if (!init_category_name("C", 1, locinfo, MSVCRT_LC_NUMERIC)) {
1907 free_locinfo(locinfo);
1908 return NULL;
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);
1916 return NULL;
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);
1928 return NULL;
1931 locinfo->lc_time_curr = create_time_data(lcid[MSVCRT_LC_TIME]);
1932 if(!locinfo->lc_time_curr) {
1933 free_locinfo(locinfo);
1934 return NULL;
1936 } else {
1937 if(!init_category_name("C", 1, locinfo, MSVCRT_LC_TIME)) {
1938 free_locinfo(locinfo);
1939 return NULL;
1941 locinfo->lc_time_curr = &cloc_time_data;
1942 InterlockedIncrement(&locinfo->lc_time_curr->refcount);
1945 return locinfo;
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));
1956 if(!loc)
1957 return NULL;
1959 loc->locinfo = create_locinfo(category, locale, NULL);
1960 if(!loc->locinfo) {
1961 MSVCRT_free(loc);
1962 return NULL;
1965 loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage,
1966 loc->locinfo->lc_handle[MSVCRT_LC_CTYPE], NULL);
1967 if(!loc->mbcinfo) {
1968 free_locinfo(loc->locinfo);
1969 MSVCRT_free(loc);
1970 return NULL;
1972 return loc;
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;
1982 MSVCRT_size_t len;
1983 char *str;
1985 if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
1986 return NULL;
1988 len = MSVCRT_wcstombs(NULL, locale, 0);
1989 if(len == -1)
1990 return NULL;
1991 if(!(str = MSVCRT_malloc(++len)))
1992 return NULL;
1993 MSVCRT_wcstombs(str, locale, len);
1995 loc = MSVCRT__create_locale(category, str);
1997 MSVCRT_free(str);
1998 return loc;
2000 #endif
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)
2011 return NULL;
2013 if(!locale) {
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);
2021 if(!newlocinfo) {
2022 WARN("%d %s failed\n", category, locale);
2023 return NULL;
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;
2035 else
2037 int i;
2039 _lock_locales();
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];
2049 _unlock_locales();
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;
2067 const char *ret;
2068 MSVCRT_size_t len;
2070 if(wlocale) {
2071 len = MSVCRT_wcstombs(NULL, wlocale, 0);
2072 if(len == -1)
2073 return NULL;
2075 locale = MSVCRT_malloc(++len);
2076 if(!locale)
2077 return NULL;
2079 MSVCRT_wcstombs(locale, wlocale, len);
2082 _lock_locales();
2083 ret = MSVCRT_setlocale(category, locale);
2084 MSVCRT_free(locale);
2086 if(ret && MSVCRT_mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
2087 ret = NULL;
2089 _unlock_locales();
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();
2100 int ret;
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;
2109 else if(type)
2110 ret = -1;
2112 return ret;
2114 #endif
2116 BOOL msvcrt_init_locale(void)
2118 int i;
2120 _lock_locales();
2121 MSVCRT_locale = MSVCRT__create_locale(0, "C");
2122 _unlock_locales();
2123 if(!MSVCRT_locale)
2124 return FALSE;
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);
2133 return TRUE;
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))
2146 return 2;
2147 if(!strcmp(property, str_toupper))
2148 return 1;
2149 return 0;
2151 #endif