Don't return from overlapped ReadFile on EAGAIN and other non-fatal
[wine/gsoc_dplay.git] / dlls / msvcrt / locale.c
blob8d025008424cd94000496942421ba9d58810d8d5
1 /*
2 * msvcrt.dll locale functions
4 * Copyright 2000 Jon Griffiths
5 */
6 #include "winnt.h"
7 #include "winbase.h"
8 #include "winuser.h"
10 #include "msvcrt.h"
11 #include "msvcrt/locale.h"
13 DEFAULT_DEBUG_CHANNEL(msvcrt);
15 /* FIXME: Need to hold locale for each LC_* type and aggregate
16 * string to produce lc_all.
18 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
19 #define MAX_LOCALE_LENGTH 256
20 char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH];
21 LCID MSVCRT_current_lc_all_lcid;
22 int MSVCRT_current_lc_all_cp;
24 /* MT */
25 extern CRITICAL_SECTION MSVCRT_locale_cs;
26 #define LOCK_LOCALE EnterCriticalSection(&MSVCRT_locale_cs)
27 #define UNLOCK_LOCALE LeaveCriticalSection(&MSVCRT_locale_cs)
29 /* ctype data modified when the locale changes */
30 extern WORD MSVCRT__ctype [257];
31 extern WORD MSVCRT_current_ctype[257];
32 extern WORD* MSVCRT__pctype;
34 /* mbctype data modified when the locale changes */
35 extern int MSVCRT___mb_cur_max;
36 extern unsigned char MSVCRT_mbctype[257];
38 #define MSVCRT_LEADBYTE 0x8000
40 /* Friendly country strings & iso codes for synonym support.
41 * Based on MS documentation for setlocale().
43 static const char* _country_synonyms[] =
45 "Hong Kong","HK",
46 "Hong-Kong","HK",
47 "New Zealand","NZ",
48 "New-Zealand","NZ",
49 "PR China","CN",
50 "PR-China","CN",
51 "United Kingdom","GB",
52 "United-Kingdom","GB",
53 "Britain","GB",
54 "England","GB",
55 "Great Britain","GB",
56 "United States","US",
57 "United-States","US",
58 "America","US"
61 /* INTERNAL: Map a synonym to an ISO code */
62 static void remap_synonym(char *name)
64 size_t i;
65 for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
67 if (!strcasecmp(_country_synonyms[i],name))
69 TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
70 name[0] = _country_synonyms[i+1][0];
71 name[1] = _country_synonyms[i+1][1];
72 name[2] = '\0';
73 return;
78 /* Note: Flags are weighted in order of matching importance */
79 #define FOUND_LANGUAGE 0x4
80 #define FOUND_COUNTRY 0x2
81 #define FOUND_CODEPAGE 0x1
83 typedef struct {
84 char search_language[MAX_ELEM_LEN];
85 char search_country[MAX_ELEM_LEN];
86 char search_codepage[MAX_ELEM_LEN];
87 char found_language[MAX_ELEM_LEN];
88 char found_country[MAX_ELEM_LEN];
89 char found_codepage[MAX_ELEM_LEN];
90 unsigned int match_flags;
91 LANGID found_lang_id;
92 } locale_search_t;
94 #define CONTINUE_LOOKING TRUE
95 #define STOP_LOOKING FALSE
97 /* INTERNAL: Get and compare locale info with a given string */
98 static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp)
100 buff[0] = 0;
101 GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN);
102 if (!buff[0] || !cmp[0])
103 return 0;
104 /* Partial matches are allowed, e.g. "Germ" matches "Germany" */
105 return !strncasecmp(cmp, buff, strlen(cmp));
108 static BOOL CALLBACK
109 find_best_locale_proc(HMODULE hModule WINE_UNUSED, LPCSTR type WINE_UNUSED,
110 LPCSTR name WINE_UNUSED, WORD LangID, LONG lParam)
112 locale_search_t *res = (locale_search_t *)lParam;
113 const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
114 char buff[MAX_ELEM_LEN];
115 unsigned int flags = 0;
117 if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
118 return CONTINUE_LOOKING;
120 /* Check Language */
121 if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language) ||
122 compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language) ||
123 compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language))
125 TRACE(":Found language: %s->%s\n", res->search_language, buff);
126 flags |= FOUND_LANGUAGE;
127 memcpy(res->found_language,res->search_language,MAX_ELEM_LEN);
129 else if (res->match_flags & FOUND_LANGUAGE)
131 return CONTINUE_LOOKING;
134 /* Check Country */
135 if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country) ||
136 compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country) ||
137 compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country))
139 TRACE("Found country:%s->%s\n", res->search_country, buff);
140 flags |= FOUND_COUNTRY;
141 memcpy(res->found_country,res->search_country,MAX_ELEM_LEN);
143 else if (res->match_flags & FOUND_COUNTRY)
145 return CONTINUE_LOOKING;
148 /* Check codepage */
149 if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) ||
150 (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage)))
152 TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
153 flags |= FOUND_CODEPAGE;
154 memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
156 else if (res->match_flags & FOUND_CODEPAGE)
158 return CONTINUE_LOOKING;
161 if (flags > res->match_flags)
163 /* Found a better match than previously */
164 res->match_flags = flags;
165 res->found_lang_id = LangID;
167 if (flags & (FOUND_LANGUAGE & FOUND_COUNTRY & FOUND_CODEPAGE))
169 TRACE(":found exact locale match\n");
170 return STOP_LOOKING;
172 return CONTINUE_LOOKING;
175 extern int atoi(const char *);
177 /* Internal: Find the LCID for a locale specification */
178 static LCID MSVCRT_locale_to_LCID(locale_search_t* locale)
180 LCID lcid;
181 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), RT_STRINGA,
182 (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
183 (LONG)locale);
185 if (!locale->match_flags)
186 return 0;
188 /* If we were given something that didn't match, fail */
189 if (locale->search_country[0] && !(locale->match_flags & FOUND_COUNTRY))
190 return 0;
192 lcid = MAKELCID(locale->found_lang_id, SORT_DEFAULT);
194 /* Populate partial locale, translating LCID to locale string elements */
195 if (!locale->found_codepage[0])
197 /* Even if a codepage is not enumerated for a locale
198 * it can be set if valid */
199 if (locale->search_codepage[0])
201 if (IsValidCodePage(atoi(locale->search_codepage)))
202 memcpy(locale->found_codepage,locale->search_codepage,MAX_ELEM_LEN);
203 else
205 /* Special codepage values: OEM & ANSI */
206 if (strcasecmp(locale->search_codepage,"OCP"))
208 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
209 locale->found_codepage, MAX_ELEM_LEN);
211 if (strcasecmp(locale->search_codepage,"ACP"))
213 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
214 locale->found_codepage, MAX_ELEM_LEN);
216 else
217 return 0;
219 if (!atoi(locale->found_codepage))
220 return 0;
223 else
225 /* Prefer ANSI codepages if present */
226 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
227 locale->found_codepage, MAX_ELEM_LEN);
228 if (!locale->found_codepage[0] || !atoi(locale->found_codepage))
229 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
230 locale->found_codepage, MAX_ELEM_LEN);
233 GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE,
234 locale->found_language, MAX_ELEM_LEN);
235 GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY|LOCALE_NOUSEROVERRIDE,
236 locale->found_country, MAX_ELEM_LEN);
237 return lcid;
240 extern int snprintf(char *, int, const char *, ...);
242 /* INTERNAL: Set ctype behaviour for a codepage */
243 static void msvcrt_set_ctype(unsigned int codepage, LCID lcid)
245 CPINFO cp;
247 memset(&cp, 0, sizeof(CPINFO));
249 if (GetCPInfo(codepage, &cp))
251 int i;
252 char str[3];
253 unsigned char *traverse = (unsigned char *)cp.LeadByte;
255 memset(MSVCRT_current_ctype, 0, sizeof(MSVCRT__ctype));
256 MSVCRT_current_lc_all_cp = codepage;
258 /* Switch ctype macros to MBCS if needed */
259 MSVCRT___mb_cur_max = cp.MaxCharSize;
261 /* Set remaining ctype flags: FIXME: faster way to do this? */
262 str[1] = str[2] = 0;
263 for (i = 0; i < 256; i++)
265 if (!(MSVCRT__pctype[i] & MSVCRT_LEADBYTE))
267 str[0] = i;
268 GetStringTypeA(lcid, CT_CTYPE1, str, 1, MSVCRT__pctype + i);
272 /* Set leadbyte flags */
273 while (traverse[0] || traverse[1])
275 for( i = traverse[0]; i <= traverse[1]; i++ )
276 MSVCRT_current_ctype[i+1] |= MSVCRT_LEADBYTE;
277 traverse += 2;
283 /*********************************************************************
284 * setlocale (MSVCRT.@)
286 char* MSVCRT_setlocale(int category, const char* locale)
288 LCID lcid = 0;
289 locale_search_t lc;
290 int haveLang, haveCountry, haveCP;
291 char* next;
292 int lc_all = 0;
294 TRACE("(%d %s)\n",category,locale);
296 if (category < MSVCRT_LC_MIN || category > MSVCRT_LC_MAX)
297 return NULL;
299 if (locale == NULL)
301 /* Report the current Locale */
302 return MSVCRT_current_lc_all;
305 LOCK_LOCALE;
307 if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_')
309 FIXME(":restore previous locale not implemented!\n");
310 /* FIXME: Easiest way to do this is parse the string and
311 * call this function recursively with its elements,
312 * Where they differ for each lc_ type.
314 UNLOCK_LOCALE;
315 return MSVCRT_current_lc_all;
318 /* Default Locale: Special case handling */
319 if (!strlen(locale) || ((toupper(locale[0]) == 'C') && !locale[1]))
321 MSVCRT_current_lc_all[0] = 'C';
322 MSVCRT_current_lc_all[1] = '\0';
323 MSVCRT_current_lc_all_cp = GetACP();
325 switch (category) {
326 case MSVCRT_LC_ALL:
327 lc_all = 1; /* Fall through all cases ... */
328 case MSVCRT_LC_COLLATE:
329 if (!lc_all) break;
330 case MSVCRT_LC_CTYPE:
331 /* Restore C locale ctype info */
332 MSVCRT___mb_cur_max = 1;
333 memcpy(MSVCRT_current_ctype, MSVCRT__ctype, sizeof(MSVCRT__ctype));
334 memset(MSVCRT_mbctype, 0, sizeof(MSVCRT_mbctype));
335 if (!lc_all) break;
336 case MSVCRT_LC_MONETARY:
337 if (!lc_all) break;
338 case MSVCRT_LC_NUMERIC:
339 if (!lc_all) break;
340 case MSVCRT_LC_TIME:
341 break;
343 UNLOCK_LOCALE;
344 return MSVCRT_current_lc_all;
347 /* Get locale elements */
348 haveLang = haveCountry = haveCP = 0;
349 memset(&lc,0,sizeof(lc));
351 next = strchr(locale,'_');
352 if (next && next != locale)
354 haveLang = 1;
355 strncpy(lc.search_language,locale,next-locale);
356 locale += next-locale+1;
359 next = strchr(locale,'.');
360 if (next)
362 haveCP = 1;
363 if (next == locale)
365 locale++;
366 strncpy(lc.search_codepage, locale, MAX_ELEM_LEN);
368 else
370 if (haveLang)
372 haveCountry = 1;
373 strncpy(lc.search_country,locale,next-locale);
374 locale += next-locale+1;
376 else
378 haveLang = 1;
379 strncpy(lc.search_language,locale,next-locale);
380 locale += next-locale+1;
382 strncpy(lc.search_codepage, locale, MAX_ELEM_LEN);
385 else
387 if (haveLang)
389 haveCountry = 1;
390 strncpy(lc.search_country, locale, MAX_ELEM_LEN);
392 else
394 haveLang = 1;
395 strncpy(lc.search_language, locale, MAX_ELEM_LEN);
399 if (haveCountry)
400 remap_synonym(lc.search_country);
402 if (haveCP && !haveCountry && !haveLang)
404 FIXME(":Codepage only locale not implemented\n");
405 /* FIXME: Use default lang/country and skip locale_to_LCID()
406 * call below...
408 UNLOCK_LOCALE;
409 return NULL;
412 lcid = MSVCRT_locale_to_LCID(&lc);
414 TRACE(":found LCID %ld\n",lcid);
416 if (lcid == 0)
418 UNLOCK_LOCALE;
419 return NULL;
422 MSVCRT_current_lc_all_lcid = lcid;
424 snprintf(MSVCRT_current_lc_all,MAX_LOCALE_LENGTH,"%s_%s.%s",
425 lc.found_language,lc.found_country,lc.found_codepage);
427 switch (category) {
428 case MSVCRT_LC_ALL:
429 lc_all = 1; /* Fall through all cases ... */
430 case MSVCRT_LC_COLLATE:
431 if (!lc_all) break;
432 case MSVCRT_LC_CTYPE:
433 msvcrt_set_ctype(atoi(lc.found_codepage),lcid);
434 if (!lc_all) break;
435 case MSVCRT_LC_MONETARY:
436 if (!lc_all) break;
437 case MSVCRT_LC_NUMERIC:
438 if (!lc_all) break;
439 case MSVCRT_LC_TIME:
440 break;
442 UNLOCK_LOCALE;
443 return MSVCRT_current_lc_all;
447 /*********************************************************************
448 * _Getdays (MSVCRT.@)
450 const char* _Getdays(void)
452 static const char *MSVCRT_days = ":Sun:Sunday:Mon:Monday:Tue:Tuesday:Wed:"
453 "Wednesday:Thu:Thursday:Fri:Friday:Sat:Saturday";
454 /* FIXME: Use locale */
455 TRACE("(void) semi-stub\n");
456 return MSVCRT_days;
459 /*********************************************************************
460 * _Getmonths (MSVCRT.@)
462 const char* _Getmonths(void)
464 static const char *MSVCRT_months = ":Jan:January:Feb:February:Mar:March:Apr:"
465 "April:May:May:Jun:June:Jul:July:Aug:August:Sep:September:Oct:"
466 "October:Nov:November:Dec:December";
467 /* FIXME: Use locale */
468 TRACE("(void) semi-stub\n");
469 return MSVCRT_months;
472 /*********************************************************************
473 * _Getnames (MSVCRT.@)
475 const char* _Getnames(void)
477 /* FIXME: */
478 TRACE("(void) stub\n");
479 return "";
482 /*********************************************************************
483 * _Strftime (MSVCRT.@)
485 const char* _Strftime(char *out, unsigned int len, const char *fmt,
486 const void *tm, void *foo)
488 /* FIXME: */
489 TRACE("(%p %d %s %p %p) stub\n", out, len, fmt, tm, foo);
490 return "";
493 /* FIXME: MBCP probably belongs in mbcs.c */
495 /*********************************************************************
496 * _setmbcp (MSVCRT.@)
498 void _setmbcp(int cp)
500 LOCK_LOCALE;
501 if (MSVCRT_current_lc_all_cp != cp)
503 /* FIXME: set ctype behaviour for this cp */
504 MSVCRT_current_lc_all_cp = cp;
506 UNLOCK_LOCALE;
509 /*********************************************************************
510 * _getmbcp (MSVCRT.@)
512 int _getmbcp(void)
514 return MSVCRT_current_lc_all_cp;