msvcrt: Update lc_category.wrefcount in __get_current_locale.
[wine.git] / dlls / msvcrt / locale.c
blob5971882229c9a5234f12db9fe968f869fb2b2fcd
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 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];
620 int i;
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))
625 break;
628 if(i==MSVCRT_LC_MAX)
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;
650 char *out;
652 TRACE("\n");
654 for(i=0; i<7; i++) {
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);
659 if(!out)
660 return NULL;
662 size = 0;
663 for(i=0; i<7; i++) {
664 out[size++] = ':';
665 len = strlen(cur->str.names.short_wday[i]);
666 memcpy(&out[size], cur->str.names.short_wday[i], len);
667 size += len;
669 out[size++] = ':';
670 len = strlen(cur->str.names.wday[i]);
671 memcpy(&out[size], cur->str.names.wday[i], len);
672 size += len;
674 out[size] = '\0';
676 return out;
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;
686 MSVCRT_wchar_t *out;
687 int i, len, size = 0;
689 TRACE("\n");
691 for(i=0; i<7; i++) {
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));
696 if(!out)
697 return NULL;
699 size = 0;
700 for(i=0; i<7; i++) {
701 out[size++] = ':';
702 len = MSVCRT_wcslen(cur->wstr.names.short_wday[i]);
703 memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out));
704 size += len;
706 out[size++] = ':';
707 len = MSVCRT_wcslen(cur->wstr.names.wday[i]);
708 memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out));
709 size += len;
711 out[size] = '\0';
713 return out;
715 #endif
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;
724 char *out;
726 TRACE("\n");
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);
733 if(!out)
734 return NULL;
736 size = 0;
737 for(i=0; i<12; i++) {
738 out[size++] = ':';
739 len = strlen(cur->str.names.short_mon[i]);
740 memcpy(&out[size], cur->str.names.short_mon[i], len);
741 size += len;
743 out[size++] = ':';
744 len = strlen(cur->str.names.mon[i]);
745 memcpy(&out[size], cur->str.names.mon[i], len);
746 size += len;
748 out[size] = '\0';
750 return out;
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;
760 MSVCRT_wchar_t *out;
761 int i, len, size = 0;
763 TRACE("\n");
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));
770 if(!out)
771 return NULL;
773 size = 0;
774 for(i=0; i<12; i++) {
775 out[size++] = ':';
776 len = MSVCRT_wcslen(cur->wstr.names.short_mon[i]);
777 memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out));
778 size += len;
780 out[size++] = ':';
781 len = MSVCRT_wcslen(cur->wstr.names.mon[i]);
782 memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out));
783 size += len;
785 out[size] = '\0';
787 return out;
789 #endif
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);
799 TRACE("\n");
801 for(i=0; i<ARRAY_SIZE(cur->str.str); i++)
802 size += strlen(cur->str.str[i])+1;
804 ret = MSVCRT_malloc(size);
805 if(!ret)
806 return NULL;
807 memcpy(ret, cur, sizeof(*ret));
809 size = 0;
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];
814 size += len;
817 return ret;
820 #if _MSVCR_VER >= 110
821 /*********************************************************************
822 * _W_Gettnames (MSVCR110.@)
824 void* CDECL _W_Gettnames(void)
826 return _Gettnames();
828 #endif
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));
849 if (!in) return 0;
852 r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len);
853 if (!r) goto done;
855 if (mapflags & LCMAP_SORTKEY)
857 r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen);
858 goto done;
861 r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0);
862 if (!r) goto done;
863 out_len = r;
864 if (r > ARRAY_SIZE(buf_out))
866 out = MSVCRT_malloc(r * sizeof(WCHAR));
867 if (!out)
869 r = 0;
870 goto done;
874 r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len);
875 if (!r) goto done;
877 r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL);
879 done:
880 if (in != buf_in) MSVCRT_free(in);
881 if (out != buf_out) MSVCRT_free(out);
882 return r;
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);
940 #endif
942 /*********************************************************************
943 * btowc(MSVCRT.@)
945 MSVCRT_wint_t CDECL MSVCRT_btowc(int c)
947 unsigned char letter = c;
948 MSVCRT_wchar_t ret;
950 if(c == MSVCRT_EOF)
951 return MSVCRT_WEOF;
952 if(!get_locinfo()->lc_codepage)
953 return c & 255;
954 if(!MultiByteToWideChar(get_locinfo()->lc_codepage,
955 MB_ERR_INVALID_CHARS, (LPCSTR)&letter, 1, &ret, 1))
956 return MSVCRT_WEOF;
958 return ret;
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;
998 return 0;
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;
1017 #endif
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)
1038 int i;
1040 if(!locinfo)
1041 return;
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]);
1053 #endif
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);
1066 #endif
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);
1085 #endif
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))
1107 return;
1109 MSVCRT_free(locinfo);
1112 /* INTERNAL: frees MSVCRT_pthreadmbcinfo struct */
1113 void free_mbcinfo(MSVCRT_pthreadmbcinfo mbcinfo)
1115 if(!mbcinfo)
1116 return;
1118 if(InterlockedDecrement(&mbcinfo->refcount))
1119 return;
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();
1143 int i;
1145 if(!data || !data->have_locale)
1147 _lock_locales();
1148 *locale = *MSVCRT_locale;
1150 else
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)
1173 _unlock_locales();
1175 InterlockedIncrement(&locale->mbcinfo->refcount);
1176 return locale;
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));
1191 if(!loc)
1192 return NULL;
1194 return get_current_locale_noalloc(loc);
1197 /*********************************************************************
1198 * _free_locale (MSVCRT.@)
1200 void CDECL MSVCRT__free_locale(MSVCRT__locale_t locale)
1202 if (!locale)
1203 return;
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,
1233 LOCALE_STIMEFORMAT
1236 MSVCRT___lc_time_data *cur;
1237 int i, ret, size;
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);
1242 if(!ret)
1243 return NULL;
1244 size += ret;
1246 ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0);
1247 if(!ret)
1248 return NULL;
1249 size += ret*sizeof(MSVCRT_wchar_t);
1251 #if _MSVCR_VER >= 110
1252 size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(MSVCRT_wchar_t);
1253 #endif
1255 cur = MSVCRT_malloc(size);
1256 if(!cur)
1257 return NULL;
1259 ret = 0;
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);
1272 #else
1273 cur->lcid = lcid;
1274 #endif
1275 cur->unk = 1;
1276 cur->refcount = 1;
1278 return cur;
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 };
1295 char buf[256];
1296 BOOL sname;
1297 #if _MSVCR_VER >= 100
1298 MSVCRT_wchar_t wbuf[256];
1299 #endif
1300 int i;
1302 TRACE("(%d %s)\n", category, locale);
1304 if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
1305 return NULL;
1307 if(locale[0]=='C' && !locale[1]) {
1308 lcid[0] = 0;
1309 cp[0] = CP_ACP;
1310 } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') {
1311 const char *p;
1313 while(1) {
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)) {
1328 i = MSVCRT_LC_TIME;
1329 locale += sizeof(time)-1;
1330 } else
1331 return NULL;
1333 p = strchr(locale, ';');
1334 if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) {
1335 lcid[i] = 0;
1336 cp[i] = CP_ACP;
1337 } else if(p) {
1338 memcpy(buf, locale, p-locale);
1339 buf[p-locale] = '\0';
1340 lcid[i] = MSVCRT_locale_to_LCID(buf, &cp[i], &sname);
1341 if(sname) {
1342 locale_name[i] = locale;
1343 locale_len[i] = p-locale;
1345 } else {
1346 lcid[i] = MSVCRT_locale_to_LCID(locale, &cp[i], &sname);
1347 if(sname) {
1348 locale_name[i] = locale;
1349 locale_len[i] = strlen(locale);
1353 if(lcid[i] == -1)
1354 return NULL;
1356 if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_')
1357 break;
1359 locale = p+1;
1361 } else {
1362 lcid[0] = MSVCRT_locale_to_LCID(locale, &cp[0], &sname);
1363 if(lcid[0] == -1)
1364 return NULL;
1365 if(sname) {
1366 locale_name[0] = locale;
1367 locale_len[0] = strlen(locale);
1370 for(i=1; i<6; i++) {
1371 lcid[i] = lcid[0];
1372 cp[i] = cp[0];
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) {
1380 if(old_locinfo) {
1381 lcid[i] = old_locinfo->lc_handle[i];
1382 cp[i] = old_locinfo->lc_id[i].wCodePage;
1383 } else {
1384 lcid[i] = 0;
1385 cp[i] = 0;
1390 locinfo = MSVCRT_malloc(sizeof(MSVCRT_threadlocinfo));
1391 if(!locinfo)
1392 return NULL;
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);
1401 return NULL;
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);
1412 return NULL;
1415 locinfo->lc_collate_cp = locinfo->lc_id[MSVCRT_LC_COLLATE].wCodePage;
1416 } else {
1417 if(!init_category_name("C", 1, locinfo, MSVCRT_LC_COLLATE)) {
1418 free_locinfo(locinfo);
1419 return NULL;
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);
1427 return NULL;
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]) {
1444 CPINFO cp_info;
1445 int j;
1447 if(!update_threadlocinfo_category(lcid[MSVCRT_LC_CTYPE],
1448 cp[MSVCRT_LC_CTYPE], locinfo, MSVCRT_LC_CTYPE)) {
1449 free_locinfo(locinfo);
1450 return NULL;
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);
1457 return NULL;
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);
1464 return NULL;
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);
1473 return NULL;
1476 locinfo->ctype1[0] = 0;
1477 locinfo->pctype = locinfo->ctype1+1;
1479 buf[1] = buf[2] = '\0';
1480 for(i=1; i<257; i++) {
1481 buf[0] = i-1;
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)
1496 buf[i] = ' ';
1497 else
1498 buf[i] = i;
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);
1505 } else {
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);
1513 return NULL;
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);
1534 return NULL;
1536 memset(locinfo->lconv, 0, sizeof(struct MSVCRT_lconv));
1537 *locinfo->lconv_intl_refcount = 1;
1538 } else {
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);
1546 return NULL;
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;
1578 #endif
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);
1584 return NULL;
1587 locinfo->lconv_mon_refcount = MSVCRT_malloc(sizeof(int));
1588 if(!locinfo->lconv_mon_refcount) {
1589 free_locinfo(locinfo);
1590 return NULL;
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);
1599 else {
1600 free_locinfo(locinfo);
1601 return NULL;
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);
1608 else {
1609 free_locinfo(locinfo);
1610 return NULL;
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);
1617 else {
1618 free_locinfo(locinfo);
1619 return NULL;
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);
1626 else {
1627 free_locinfo(locinfo);
1628 return NULL;
1631 i = GetLocaleInfoA(lcid[MSVCRT_LC_MONETARY], LOCALE_SMONGROUPING
1632 |LOCALE_NOUSEROVERRIDE, buf, 256);
1633 if(i>1)
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';
1639 if(buf[i] != '0')
1640 locinfo->lconv->mon_grouping[i/2+1] = 127;
1641 } else {
1642 free_locinfo(locinfo);
1643 return NULL;
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);
1650 else {
1651 free_locinfo(locinfo);
1652 return NULL;
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);
1659 else {
1660 free_locinfo(locinfo);
1661 return NULL;
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;
1667 else {
1668 free_locinfo(locinfo);
1669 return NULL;
1672 if(GetLocaleInfoW(lcid[MSVCRT_LC_MONETARY], LOCALE_ICURRDIGITS
1673 |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2))
1674 locinfo->lconv->frac_digits = val;
1675 else {
1676 free_locinfo(locinfo);
1677 return NULL;
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;
1683 else {
1684 free_locinfo(locinfo);
1685 return NULL;
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;
1691 else {
1692 free_locinfo(locinfo);
1693 return NULL;
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;
1699 else {
1700 free_locinfo(locinfo);
1701 return NULL;
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;
1707 else {
1708 free_locinfo(locinfo);
1709 return NULL;
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;
1715 else {
1716 free_locinfo(locinfo);
1717 return NULL;
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;
1723 else {
1724 free_locinfo(locinfo);
1725 return NULL;
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));
1733 else {
1734 free_locinfo(locinfo);
1735 return NULL;
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));
1742 else {
1743 free_locinfo(locinfo);
1744 return NULL;
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));
1751 else {
1752 free_locinfo(locinfo);
1753 return NULL;
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));
1760 else {
1761 free_locinfo(locinfo);
1762 return NULL;
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));
1769 else {
1770 free_locinfo(locinfo);
1771 return NULL;
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));
1778 else {
1779 free_locinfo(locinfo);
1780 return NULL;
1782 #endif
1783 } else {
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;
1808 #endif
1811 if(!init_category_name("C", 1, locinfo, MSVCRT_LC_MONETARY)) {
1812 free_locinfo(locinfo);
1813 return NULL;
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);
1821 return NULL;
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;
1837 #endif
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);
1843 return NULL;
1846 locinfo->lconv_num_refcount = MSVCRT_malloc(sizeof(int));
1847 if(!locinfo->lconv_num_refcount) {
1848 free_locinfo(locinfo);
1849 return NULL;
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);
1858 else {
1859 free_locinfo(locinfo);
1860 return NULL;
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);
1867 else {
1868 free_locinfo(locinfo);
1869 return NULL;
1872 i = GetLocaleInfoA(lcid[MSVCRT_LC_NUMERIC], LOCALE_SGROUPING
1873 |LOCALE_NOUSEROVERRIDE, buf, 256);
1874 if(i>1)
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';
1880 if(buf[i] != '0')
1881 locinfo->lconv->grouping[i/2+1] = 127;
1882 } else {
1883 free_locinfo(locinfo);
1884 return NULL;
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));
1892 else {
1893 free_locinfo(locinfo);
1894 return NULL;
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));
1901 else {
1902 free_locinfo(locinfo);
1903 return NULL;
1905 #endif
1906 } else {
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;
1915 #endif
1918 if (!init_category_name("C", 1, locinfo, MSVCRT_LC_NUMERIC)) {
1919 free_locinfo(locinfo);
1920 return NULL;
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);
1928 return NULL;
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);
1941 return NULL;
1944 locinfo->lc_time_curr = create_time_data(lcid[MSVCRT_LC_TIME]);
1945 if(!locinfo->lc_time_curr) {
1946 free_locinfo(locinfo);
1947 return NULL;
1949 } else {
1950 if(!init_category_name("C", 1, locinfo, MSVCRT_LC_TIME)) {
1951 free_locinfo(locinfo);
1952 return NULL;
1954 locinfo->lc_time_curr = &cloc_time_data;
1957 return locinfo;
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));
1968 if(!loc)
1969 return NULL;
1971 loc->locinfo = create_locinfo(category, locale, NULL);
1972 if(!loc->locinfo) {
1973 MSVCRT_free(loc);
1974 return NULL;
1977 loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[MSVCRT_LC_CTYPE].wCodePage,
1978 loc->locinfo->lc_handle[MSVCRT_LC_CTYPE], NULL);
1979 if(!loc->mbcinfo) {
1980 free_locinfo(loc->locinfo);
1981 MSVCRT_free(loc);
1982 return NULL;
1984 return loc;
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;
1994 MSVCRT_size_t len;
1995 char *str;
1997 if(category<MSVCRT_LC_MIN || category>MSVCRT_LC_MAX || !locale)
1998 return NULL;
2000 len = MSVCRT_wcstombs(NULL, locale, 0);
2001 if(len == -1)
2002 return NULL;
2003 if(!(str = MSVCRT_malloc(++len)))
2004 return NULL;
2005 MSVCRT_wcstombs(str, locale, len);
2007 loc = MSVCRT__create_locale(category, str);
2009 MSVCRT_free(str);
2010 return loc;
2012 #endif
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)
2023 return NULL;
2025 if(!locale) {
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);
2033 if(!newlocinfo) {
2034 WARN("%d %s failed\n", category, locale);
2035 return NULL;
2038 if(locale[0] != 'C' || locale[1] != '\0')
2039 initial_locale = FALSE;
2041 _lock_locales();
2042 free_locinfo(locinfo);
2043 *plocinfo = newlocinfo;
2044 _unlock_locales();
2046 if(newlocinfo == MSVCRT_locale->locinfo) {
2047 int i;
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;
2071 const char *ret;
2072 MSVCRT_size_t len;
2074 if(wlocale) {
2075 len = MSVCRT_wcstombs(NULL, wlocale, 0);
2076 if(len == -1)
2077 return NULL;
2079 locale = MSVCRT_malloc(++len);
2080 if(!locale)
2081 return NULL;
2083 MSVCRT_wcstombs(locale, wlocale, len);
2086 _lock_locales();
2087 ret = MSVCRT_setlocale(category, locale);
2088 MSVCRT_free(locale);
2090 if(ret && MSVCRT_mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1)
2091 ret = NULL;
2093 _unlock_locales();
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;
2105 int ret;
2107 if(!data)
2108 return -1;
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));
2116 if(!locale)
2117 return -1;
2119 data->locinfo = locale->locinfo;
2120 data->mbcinfo = locale->mbcinfo;
2121 data->have_locale = TRUE;
2122 MSVCRT_free(locale);
2125 return ret;
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;
2137 return ret;
2140 if(!type)
2141 return ret;
2143 return -1;
2145 #endif
2147 BOOL msvcrt_init_locale(void)
2149 int i;
2151 _lock_locales();
2152 MSVCRT_locale = MSVCRT__create_locale(0, "C");
2153 _unlock_locales();
2154 if(!MSVCRT_locale)
2155 return FALSE;
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);
2164 return TRUE;
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))
2177 return 2;
2178 if(!strcmp(property, str_toupper))
2179 return 1;
2180 return 0;
2183 /*********************************************************************
2184 * towctrans (MSVCR120.@)
2186 MSVCRT_wint_t CDECL MSVCR120_towctrans(MSVCRT_wint_t c, wctrans_t category)
2188 if(category == 1)
2189 return MSVCRT__towupper_l(c, NULL);
2190 return MSVCRT__towlower_l(c, NULL);
2192 #endif