Merge from amd64 branch:
[reactos.git] / reactos / lib / sdk / crt / locale / locale.c
1 /*
2 * Some stuff takem from wine msvcrt\locale.c
3 *
4 * Copyright 2000 Jon Griffiths
5 */
6
7 #include <precomp.h>
8 #include <locale.h>
9
10 #include "mbctype.h"
11
12 // mtdll.h
13 #define _SETLOCALE_LOCK 19
14
15 // msvcrt.h
16 #define MSVCRT_LC_ALL 0
17 #define MSVCRT_LC_COLLATE 1
18 #define MSVCRT_LC_CTYPE 2
19 #define MSVCRT_LC_MONETARY 3
20 #define MSVCRT_LC_NUMERIC 4
21 #define MSVCRT_LC_TIME 5
22 #define MSVCRT_LC_MIN MSVCRT_LC_ALL
23 #define MSVCRT_LC_MAX MSVCRT_LC_TIME
24
25 /* FIXME: Need to hold locale for each LC_* type and aggregate
26 * string to produce lc_all.
27 */
28 #define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
29
30 unsigned char _mbctype[257] = { 0 };
31 int g_mbcp_is_multibyte = 0;
32
33 /* It seems that the data about valid trail bytes is not available from kernel32
34 * so we have to store is here. The format is the same as for lead bytes in CPINFO */
35 struct cp_extra_info_t
36 {
37 int cp;
38 BYTE TrailBytes[MAX_LEADBYTES];
39 };
40
41 static struct cp_extra_info_t g_cpextrainfo[] =
42 {
43 {932, {0x40, 0x7e, 0x80, 0xfc, 0, 0}},
44 {936, {0x40, 0xfe, 0, 0}},
45 {949, {0x41, 0xfe, 0, 0}},
46 {950, {0x40, 0x7e, 0xa1, 0xfe, 0, 0}},
47 {20932, {1, 255, 0, 0}}, /* seems to give different results on different systems */
48 {0, {1, 255, 0, 0}} /* match all with FIXME */
49 };
50
51
52 char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH] = { 0 };
53 LCID MSVCRT_current_lc_all_lcid = 0;
54 int MSVCRT___lc_codepage = 0;
55 int MSVCRT___lc_collate_cp = 0;
56 HANDLE MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1] = { 0 };
57
58 /* MT */
59 #define LOCK_LOCALE _mlock(_SETLOCALE_LOCK);
60 #define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK);
61
62 #define MSVCRT_LEADBYTE 0x8000
63
64 typedef struct {
65 char search_language[MAX_ELEM_LEN];
66 char search_country[MAX_ELEM_LEN];
67 char search_codepage[MAX_ELEM_LEN];
68 char found_language[MAX_ELEM_LEN];
69 char found_country[MAX_ELEM_LEN];
70 char found_codepage[MAX_ELEM_LEN];
71 unsigned int match_flags;
72 LANGID found_lang_id;
73 } locale_search_t;
74
75 unsigned int __setlc_active;
76 unsigned int __unguarded_readlc_active;
77 int _current_category; /* used by setlocale */
78 const char *_current_locale;
79
80
81 int parse_locale(const char *locale, char *lang, char *country, char *code_page);
82
83 #define _C_ _CONTROL
84 #define _S_ _SPACE
85 #define _P_ _PUNCT
86 #define _D_ _DIGIT
87 #define _H_ _HEX
88 #define _U_ _UPPER
89 #define _L_ _LOWER
90
91 WORD MSVCRT__ctype [257] = {
92 0, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_C_, _S_|_C_,
93 _S_|_C_, _S_|_C_, _S_|_C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_,
94 _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_BLANK,
95 _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_,
96 _P_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_,
97 _D_|_H_, _D_|_H_, _D_|_H_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _U_|_H_,
98 _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_, _U_, _U_, _U_, _U_,
99 _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_,
100 _U_, _P_, _P_, _P_, _P_, _P_, _P_, _L_|_H_, _L_|_H_, _L_|_H_, _L_|_H_,
101 _L_|_H_, _L_|_H_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_,
102 _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _P_, _P_, _P_, _P_,
103 _C_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
109 };
110
111 /* Internal: Current ctype table for locale */
112 WORD MSVCRT_current_ctype[257];
113
114 /* pctype is used by macros in the Win32 headers. It must point
115 * To a table of flags exactly like ctype. To allow locale
116 * changes to affect ctypes (i.e. isleadbyte), we use a second table
117 * and update its flags whenever the current locale changes.
118 */
119 WORD* MSVCRT__pctype = MSVCRT_current_ctype + 1;
120
121 /* Friendly country strings & iso codes for synonym support.
122 * Based on MS documentation for setlocale().
123 */
124 static const char * const _country_synonyms[] =
125 {
126 "Hong Kong","HK",
127 "Hong-Kong","HK",
128 "New Zealand","NZ",
129 "New-Zealand","NZ",
130 "PR China","CN",
131 "PR-China","CN",
132 "United Kingdom","GB",
133 "United-Kingdom","GB",
134 "Britain","GB",
135 "England","GB",
136 "Great Britain","GB",
137 "United States","US",
138 "United-States","US",
139 "America","US"
140 };
141
142 /* Note: Flags are weighted in order of matching importance */
143 #define FOUND_LANGUAGE 0x4
144 #define FOUND_COUNTRY 0x2
145 #define FOUND_CODEPAGE 0x1
146
147 /* INTERNAL: Map a synonym to an ISO code */
148 static void remap_synonym(char *name)
149 {
150 size_t i;
151 for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
152 {
153 if (!_stricmp(_country_synonyms[i],name))
154 {
155 TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
156 name[0] = _country_synonyms[i+1][0];
157 name[1] = _country_synonyms[i+1][1];
158 name[2] = '\0';
159 return;
160 }
161 }
162 }
163
164 #define CONTINUE_LOOKING TRUE
165 #define STOP_LOOKING FALSE
166
167 /* INTERNAL: Get and compare locale info with a given string */
168 static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp)
169 {
170 buff[0] = 0;
171 GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN);
172 if (!buff[0] || !cmp[0])
173 return 0;
174 /* Partial matches are allowed, e.g. "Germ" matches "Germany" */
175 return !_strnicmp(cmp, buff, strlen(cmp));
176 }
177
178
179 static BOOL CALLBACK
180 find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam)
181 {
182 locale_search_t *res = (locale_search_t *)lParam;
183 const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
184 char buff[MAX_ELEM_LEN];
185 unsigned int flags = 0;
186
187 if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
188 return CONTINUE_LOOKING;
189
190 /* Check Language */
191 if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language) ||
192 compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language) ||
193 compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language))
194 {
195 TRACE(":Found language: %s->%s\n", res->search_language, buff);
196 flags |= FOUND_LANGUAGE;
197 memcpy(res->found_language,res->search_language,MAX_ELEM_LEN);
198 }
199 else if (res->match_flags & FOUND_LANGUAGE)
200 {
201 return CONTINUE_LOOKING;
202 }
203
204 /* Check Country */
205 if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country) ||
206 compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country) ||
207 compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country))
208 {
209 TRACE("Found country:%s->%s\n", res->search_country, buff);
210 flags |= FOUND_COUNTRY;
211 memcpy(res->found_country,res->search_country,MAX_ELEM_LEN);
212 }
213 else if (res->match_flags & FOUND_COUNTRY)
214 {
215 return CONTINUE_LOOKING;
216 }
217
218 /* Check codepage */
219 if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) ||
220 (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage)))
221 {
222 TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
223 flags |= FOUND_CODEPAGE;
224 memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
225 }
226 else if (res->match_flags & FOUND_CODEPAGE)
227 {
228 return CONTINUE_LOOKING;
229 }
230
231 if (flags > res->match_flags)
232 {
233 /* Found a better match than previously */
234 res->match_flags = flags;
235 res->found_lang_id = LangID;
236 }
237 if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE)) ==
238 (FOUND_LANGUAGE | FOUND_COUNTRY | FOUND_CODEPAGE))
239 {
240 TRACE(":found exact locale match\n");
241 return STOP_LOOKING;
242 }
243 return CONTINUE_LOOKING;
244 }
245
246 /* Internal: Find the LCID for a locale specification */
247 static LCID MSVCRT_locale_to_LCID(locale_search_t* locale)
248 {
249 LCID lcid;
250 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
251 (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
252 (LONG_PTR)locale);
253
254 if (!locale->match_flags)
255 return 0;
256
257 /* If we were given something that didn't match, fail */
258 if (locale->search_country[0] && !(locale->match_flags & FOUND_COUNTRY))
259 return 0;
260
261 lcid = MAKELCID(locale->found_lang_id, SORT_DEFAULT);
262
263 /* Populate partial locale, translating LCID to locale string elements */
264 if (!locale->found_codepage[0])
265 {
266 /* Even if a codepage is not enumerated for a locale
267 * it can be set if valid */
268 if (locale->search_codepage[0])
269 {
270 if (IsValidCodePage(atoi(locale->search_codepage)))
271 memcpy(locale->found_codepage,locale->search_codepage,MAX_ELEM_LEN);
272 else
273 {
274 /* Special codepage values: OEM & ANSI */
275 if (_stricmp(locale->search_codepage,"OCP"))
276 {
277 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
278 locale->found_codepage, MAX_ELEM_LEN);
279 }
280 if (_stricmp(locale->search_codepage,"ACP"))
281 {
282 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
283 locale->found_codepage, MAX_ELEM_LEN);
284 }
285 else
286 return 0;
287
288 if (!atoi(locale->found_codepage))
289 return 0;
290 }
291 }
292 else
293 {
294 /* Prefer ANSI codepages if present */
295 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
296 locale->found_codepage, MAX_ELEM_LEN);
297 if (!locale->found_codepage[0] || !atoi(locale->found_codepage))
298 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
299 locale->found_codepage, MAX_ELEM_LEN);
300 }
301 }
302 GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE,
303 locale->found_language, MAX_ELEM_LEN);
304 GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY|LOCALE_NOUSEROVERRIDE,
305 locale->found_country, MAX_ELEM_LEN);
306 return lcid;
307 }
308
309 /* INTERNAL: Set ctype behaviour for a codepage */
310 static void msvcrt_set_ctype(unsigned int codepage, LCID lcid)
311 {
312 CPINFO cp;
313
314 memset(&cp, 0, sizeof(CPINFO));
315
316 if (GetCPInfo(codepage, &cp))
317 {
318 int i;
319 char str[3];
320 unsigned char *traverse = cp.LeadByte;
321
322 memset(MSVCRT_current_ctype, 0, sizeof(MSVCRT__ctype));
323 MSVCRT___lc_codepage = codepage;
324 MSVCRT___lc_collate_cp = codepage;
325
326 /* Switch ctype macros to MBCS if needed */
327 __mb_cur_max = cp.MaxCharSize;
328
329 /* Set remaining ctype flags: FIXME: faster way to do this? */
330 str[1] = str[2] = 0;
331 for (i = 0; i < 256; i++)
332 {
333 if (!(MSVCRT__pctype[i] & MSVCRT_LEADBYTE))
334 {
335 str[0] = i;
336 GetStringTypeA(lcid, CT_CTYPE1, str, 1, MSVCRT__pctype + i);
337 }
338 }
339
340 /* Set leadbyte flags */
341 while (traverse[0] || traverse[1])
342 {
343 for( i = traverse[0]; i <= traverse[1]; i++ )
344 MSVCRT_current_ctype[i+1] |= MSVCRT_LEADBYTE;
345 traverse += 2;
346 };
347 }
348 }
349
350
351 /*
352 * @implemented
353 */
354 char *setlocale(int category, const char *locale)
355 {
356 LCID lcid = 0;
357 locale_search_t lc;
358 int haveLang, haveCountry, haveCP;
359 char* next;
360 int lc_all = 0;
361
362 TRACE("(%d %s)\n",category,locale);
363
364 if (category < MSVCRT_LC_MIN || category > MSVCRT_LC_MAX)
365 return NULL;
366
367 if (locale == NULL)
368 {
369 /* Report the current Locale */
370 return MSVCRT_current_lc_all;
371 }
372
373 LOCK_LOCALE;
374
375 if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_')
376 {
377 WARN(":restore previous locale not implemented!\n");
378 /* FIXME: Easiest way to do this is parse the string and
379 * call this function recursively with its elements,
380 * Where they differ for each lc_ type.
381 */
382 UNLOCK_LOCALE;
383 return MSVCRT_current_lc_all;
384 }
385
386 /* Default Locale: Special case handling */
387 if (!strlen(locale) || ((toupper(locale[0]) == 'C') && !locale[1]))
388 {
389 MSVCRT_current_lc_all[0] = 'C';
390 MSVCRT_current_lc_all[1] = '\0';
391 MSVCRT___lc_codepage = GetACP();
392 MSVCRT___lc_collate_cp = GetACP();
393
394 switch (category) {
395 case MSVCRT_LC_ALL:
396 lc_all = 1; /* Fall through all cases ... */
397 case MSVCRT_LC_COLLATE:
398 if (!lc_all) break;
399 case MSVCRT_LC_CTYPE:
400 /* Restore C locale ctype info */
401 __mb_cur_max = 1;
402 memcpy(MSVCRT_current_ctype, MSVCRT__ctype, sizeof(MSVCRT__ctype));
403 if (!lc_all) break;
404 case MSVCRT_LC_MONETARY:
405 if (!lc_all) break;
406 case MSVCRT_LC_NUMERIC:
407 if (!lc_all) break;
408 case MSVCRT_LC_TIME:
409 break;
410 }
411 UNLOCK_LOCALE;
412 return MSVCRT_current_lc_all;
413 }
414
415 /* Get locale elements */
416 haveLang = haveCountry = haveCP = 0;
417 memset(&lc,0,sizeof(lc));
418
419 next = strchr(locale,'_');
420 if (next && next != locale)
421 {
422 haveLang = 1;
423 memcpy(lc.search_language,locale,next-locale);
424 locale += next-locale+1;
425 }
426
427 next = strchr(locale,'.');
428 if (next)
429 {
430 haveCP = 1;
431 if (next == locale)
432 {
433 locale++;
434 lstrcpynA(lc.search_codepage, locale, MAX_ELEM_LEN);
435 }
436 else
437 {
438 if (haveLang)
439 {
440 haveCountry = 1;
441 memcpy(lc.search_country,locale,next-locale);
442 locale += next-locale+1;
443 }
444 else
445 {
446 haveLang = 1;
447 memcpy(lc.search_language,locale,next-locale);
448 locale += next-locale+1;
449 }
450 lstrcpynA(lc.search_codepage, locale, MAX_ELEM_LEN);
451 }
452 }
453 else
454 {
455 if (haveLang)
456 {
457 haveCountry = 1;
458 lstrcpynA(lc.search_country, locale, MAX_ELEM_LEN);
459 }
460 else
461 {
462 haveLang = 1;
463 lstrcpynA(lc.search_language, locale, MAX_ELEM_LEN);
464 }
465 }
466
467 if (haveCountry)
468 remap_synonym(lc.search_country);
469
470 if (haveCP && !haveCountry && !haveLang)
471 {
472 ERR(":Codepage only locale not implemented\n");
473 /* FIXME: Use default lang/country and skip locale_to_LCID()
474 * call below...
475 */
476 UNLOCK_LOCALE;
477 return NULL;
478 }
479
480 lcid = MSVCRT_locale_to_LCID(&lc);
481
482 TRACE(":found LCID %d\n",lcid);
483
484 if (lcid == 0)
485 {
486 UNLOCK_LOCALE;
487 return NULL;
488 }
489
490 MSVCRT_current_lc_all_lcid = lcid;
491
492 _snprintf(MSVCRT_current_lc_all,MAX_LOCALE_LENGTH,"%s_%s.%s",
493 lc.found_language,lc.found_country,lc.found_codepage);
494
495 switch (category) {
496 case MSVCRT_LC_ALL:
497 lc_all = 1; /* Fall through all cases ... */
498 case MSVCRT_LC_COLLATE:
499 if (!lc_all) break;
500 case MSVCRT_LC_CTYPE:
501 msvcrt_set_ctype(atoi(lc.found_codepage),lcid);
502 if (!lc_all) break;
503 case MSVCRT_LC_MONETARY:
504 if (!lc_all) break;
505 case MSVCRT_LC_NUMERIC:
506 if (!lc_all) break;
507 case MSVCRT_LC_TIME:
508 break;
509 }
510 UNLOCK_LOCALE;
511 return MSVCRT_current_lc_all;
512 }
513
514 /*
515 * @unimplemented
516 */
517 wchar_t* _wsetlocale(int category, const wchar_t* locale)
518 {
519 static wchar_t fake[] = {
520 'E','n','g','l','i','s','h','_','U','n','i','t','e','d',' ',
521 'S','t','a','t','e','s','.','1','2','5','2',0 };
522
523 TRACE("%d %S\n", category, locale);
524
525 return fake;
526 }
527
528 /*
529
530 locale "lang[_country[.code_page]]"
531 | ".code_page"
532 | ""
533 | NULL
534
535 */
536 int parse_locale(const char *locale, char *lang, char *country, char *code_page)
537 {
538 while ( *locale != 0 && *locale != '.' && *locale != '_' )
539 {
540 *lang = *locale;
541 lang++;
542 locale++;
543 }
544 *lang = 0;
545 if ( *locale == '_' ) {
546 locale++;
547 while ( *locale != 0 && *locale != '.' )
548 {
549 *country = *locale;
550 country++;
551 locale++;
552 }
553 }
554 *country = 0;
555
556
557 if ( *locale == '.' ) {
558 locale++;
559 while ( *locale != 0 && *locale != '.' )
560 {
561 *code_page = *locale;
562 code_page++;
563 locale++;
564 }
565 }
566
567 *code_page = 0;
568 return 0;
569 }
570
571 const struct map_lcid2str {
572 short langid;
573 const char *langname;
574 const char *country;
575 } languages[]={
576 {0x0409,"English", "United States"},
577 {0x0809,"English", "United Kingdom"},
578 {0x0000,"Unknown", "Unknown"}
579
580 };
581
582 const struct map_cntr {
583 const char *abrev;
584 const char *country;
585 } abrev[] = {
586 {"britain", "united kingdom"},
587 {"england", "united kingdom"},
588 {"gbr", "united kingdom"},
589 {"great britain", "united kingdom"},
590 {"uk", "united kingdom"},
591 {"united kingdom", "united kingdom"},
592 {"united-kingdom", "united kingdom"},
593 {"america", "united states" },
594 {"united states", "united states"},
595 {"united-states", "united states"},
596 {"us", "united states"},
597 {"usa", "united states"}
598 };
599
600
601 struct lconv _lconv = {
602 ".", // decimal_point
603 ",", // thousands_sep
604 "", // grouping;
605 "DOL", // int_curr_symbol
606 "$", // currency_symbol
607 ".", // mon_decimal_point
608 ",", // mon_thousands_sep
609 "", // mon_grouping;
610 "+", // positive_sign
611 "-", // negative_sign
612 127, // int_frac_digits
613 127, // frac_digits
614 127, // p_cs_precedes
615 127, // p_sep_by_space
616 127, // n_cs_precedes
617 127, // n_sep_by_space
618 127, // p_sign_posn;
619 127 // n_sign_posn;
620 };
621
622 /*
623 * @implemented
624 */
625 struct lconv *localeconv(void)
626 {
627 return (struct lconv *) &_lconv;
628 }
629
630 /*********************************************************************
631 * _setmbcp (MSVCRT.@)
632 * @implemented
633 */
634 int CDECL _setmbcp(int cp)
635 {
636 int newcp;
637 CPINFO cpi;
638 BYTE *bytes;
639 WORD chartypes[256];
640 WORD *curr_type;
641 char bufA[256];
642 WCHAR bufW[256];
643 int charcount;
644 int ret;
645 int i;
646
647 TRACE("_setmbcp %d\n",cp);
648 switch (cp)
649 {
650 case _MB_CP_ANSI:
651 newcp = GetACP();
652 break;
653 case _MB_CP_OEM:
654 newcp = GetOEMCP();
655 break;
656 case _MB_CP_LOCALE:
657 newcp = MSVCRT___lc_codepage;
658 break;
659 case _MB_CP_SBCS:
660 newcp = 20127; /* ASCII */
661 break;
662 default:
663 newcp = cp;
664 break;
665 }
666
667 if (!GetCPInfo(newcp, &cpi))
668 {
669 ERR("Codepage %d not found\n", newcp);
670 __set_errno(EINVAL);
671 return -1;
672 }
673
674 /* setup the _mbctype */
675 memset(_mbctype, 0, sizeof(_mbctype));
676
677 bytes = cpi.LeadByte;
678 while (bytes[0] || bytes[1])
679 {
680 for (i = bytes[0]; i <= bytes[1]; i++)
681 _mbctype[i + 1] |= _M1;
682 bytes += 2;
683 }
684
685 if (cpi.MaxCharSize > 1)
686 {
687 /* trail bytes not available through kernel32 but stored in a structure in msvcrt */
688 struct cp_extra_info_t *cpextra = g_cpextrainfo;
689
690 g_mbcp_is_multibyte = 1;
691 while (TRUE)
692 {
693 if (cpextra->cp == 0 || cpextra->cp == newcp)
694 {
695 if (cpextra->cp == 0)
696 ERR("trail bytes data not available for DBCS codepage %d - assuming all bytes\n", newcp);
697
698 bytes = cpextra->TrailBytes;
699 while (bytes[0] || bytes[1])
700 {
701 for (i = bytes[0]; i <= bytes[1]; i++)
702 _mbctype[i + 1] |= _M2;
703 bytes += 2;
704 }
705 break;
706 }
707 cpextra++;
708 }
709 }
710 else
711 g_mbcp_is_multibyte = 0;
712
713 /* we can't use GetStringTypeA directly because we don't have a locale - only a code page
714 */
715 charcount = 0;
716 for (i = 0; i < 256; i++)
717 if (!(_mbctype[i + 1] & _M1))
718 bufA[charcount++] = i;
719
720 ret = MultiByteToWideChar(newcp, 0, bufA, charcount, bufW, charcount);
721 if (ret != charcount)
722 ERR("MultiByteToWideChar of chars failed for cp %d, ret=%d (exp %d), error=%d\n", newcp, ret, charcount, GetLastError());
723
724 GetStringTypeW(CT_CTYPE1, bufW, charcount, chartypes);
725
726 curr_type = chartypes;
727 for (i = 0; i < 256; i++)
728 if (!(_mbctype[i + 1] & _M1))
729 {
730 if ((*curr_type) & C1_UPPER)
731 _mbctype[i + 1] |= _SBUP;
732 if ((*curr_type) & C1_LOWER)
733 _mbctype[i + 1] |= _SBLOW;
734 curr_type++;
735 }
736
737 if (newcp == 932) /* CP932 only - set _MP and _MS */
738 {
739 /* On Windows it's possible to calculate the _MP and _MS from CT_CTYPE1
740 * and CT_CTYPE3. But as of Wine 0.9.43 we return wrong values what makes
741 * it hard. As this is set only for codepage 932 we hardcode it what gives
742 * also faster execution.
743 */
744 for (i = 161; i <= 165; i++)
745 _mbctype[i + 1] |= _MP;
746 for (i = 166; i <= 223; i++)
747 _mbctype[i + 1] |= _MS;
748 }
749
750 MSVCRT___lc_collate_cp = MSVCRT___lc_codepage = newcp;
751 TRACE("(%d) -> %d\n", cp, MSVCRT___lc_codepage);
752 return 0;
753 }
754
755
756 /*********************************************************************
757 * __lc_collate_cp (MSVCRT.@)
758 *
759 * @unimplemented
760 */
761 void __lc_collate_cp(int cp)
762 {
763 FIXME("__lc_collate_cp - stub\n");
764 return;
765 }
766
767
768 /*********************************************************************
769 * __lc_handle (MSVCRT.@)
770 *
771 * @unimplemented
772 */
773 void __lc_handle(void)
774 {
775 FIXME("__lc_handle - stub\n");
776 return;
777 }
778
779
780 /*********************************************************************
781 * __lc_codepage (MSVCRT.@)
782 *
783 * @unimplemented
784 */
785 void __lc_codepage(void)
786 {
787 FIXME("__lc_codepage - stub\n");
788 return;
789 }
790
791
792 /*********************************************************************
793 * _Gettnames (MSVCRT.@)
794 */
795 void *_Gettnames(void)
796 {
797 FIXME("(void), stub!\n");
798 return NULL;
799 }
800
801 /*********************************************************************
802 * __lconv_init (MSVCRT.@)
803 */
804 void __lconv_init(void)
805 {
806 char Char = (char) UCHAR_MAX;
807
808 TRACE("__lconv_init()\n");
809
810 _lconv.int_frac_digits = Char;
811 _lconv.frac_digits = Char;
812 _lconv.p_sep_by_space = _lconv.n_sep_by_space = Char;
813 _lconv.p_cs_precedes = _lconv.n_cs_precedes = Char;
814 _lconv.p_sign_posn = _lconv.n_sign_posn = Char;
815 }
816
817
818 /*********************************************************************
819 * _Strftime (MSVCRT.@)
820 */
821 const char* _Strftime(char *out, unsigned int len, const char *fmt,
822 const void *tm, void *foo)
823 {
824 /* FIXME: */
825 FIXME("(%p %d %s %p %p) stub\n", out, len, fmt, tm, foo);
826 return "";
827 }
828
829
830 /*********************************************************************
831 * _Getdays (MSVCRT.@)
832 */
833 const char* _Getdays(void)
834 {
835 static const char *MSVCRT_days = ":Sun:Sunday:Mon:Monday:Tue:Tuesday:Wed:"
836 "Wednesday:Thu:Thursday:Fri:Friday:Sat:Saturday";
837 /* FIXME: Use locale */
838 FIXME("(void) semi-stub\n");
839 return MSVCRT_days;
840 }
841
842 /*********************************************************************
843 * _Getmonths (MSVCRT.@)
844 */
845 const char* _Getmonths(void)
846 {
847 static const char *MSVCRT_months = ":Jan:January:Feb:February:Mar:March:Apr:"
848 "April:May:May:Jun:June:Jul:July:Aug:August:Sep:September:Oct:"
849 "October:Nov:November:Dec:December";
850 /* FIXME: Use locale */
851 FIXME("(void) semi-stub\n");
852 return MSVCRT_months;
853 }
854
855 /*********************************************************************
856 * __crtLCMapStringA (MSVCRT.@)
857 */
858 int __crtLCMapStringA(
859 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
860 int dstlen, unsigned int codepage, int xflag
861 ) {
862 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
863 lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
864 /* FIXME: A bit incorrect. But msvcrt itself just converts its
865 * arguments to wide strings and then calls LCMapStringW
866 */
867 return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
868 }
869
870 /*********************************************************************
871 * __crtLCMapStringW (MSVCRT.@)
872 */
873 int __crtLCMapStringW(
874 LCID lcid, DWORD mapflags, LPCWSTR src, int srclen, LPWSTR dst,
875 int dstlen, unsigned int codepage, int xflag
876 ) {
877 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
878 lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
879
880 return LCMapStringW(lcid,mapflags,src,srclen,dst,dstlen);
881 }
882
883 int CDECL _getmbcp(void)
884 {
885 return MSVCRT___lc_codepage;
886 }
887
888 /*********************************************************************
889 * ___unguarded_readlc_active_add_func (MSVCRT.@)
890 */
891 unsigned int * CDECL ___unguarded_readlc_active_add_func(void)
892 {
893 return &__unguarded_readlc_active;
894 }
895
896 /*********************************************************************
897 * ___setlc_active_func (MSVCRT.@)
898 */
899 unsigned int CDECL ___setlc_active_func(void)
900 {
901 return __setlc_active;
902 }