- Update many parts of CRT, and misc cleanup.
[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 #define MAX_LOCALE_LENGTH 256
30
31 unsigned char MSVCRT_mbctype[257];
32 static int g_mbcp_is_multibyte = 0;
33
34 /* It seems that the data about valid trail bytes is not available from kernel32
35 * so we have to store is here. The format is the same as for lead bytes in CPINFO */
36 struct cp_extra_info_t
37 {
38 int cp;
39 BYTE TrailBytes[MAX_LEADBYTES];
40 };
41
42 static struct cp_extra_info_t g_cpextrainfo[] =
43 {
44 {932, {0x40, 0x7e, 0x80, 0xfc, 0, 0}},
45 {936, {0x40, 0xfe, 0, 0}},
46 {949, {0x41, 0xfe, 0, 0}},
47 {950, {0x40, 0x7e, 0xa1, 0xfe, 0, 0}},
48 {20932, {1, 255, 0, 0}}, /* seems to give different results on different systems */
49 {0, {1, 255, 0, 0}} /* match all with FIXME */
50 };
51
52
53 char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH];
54 LCID MSVCRT_current_lc_all_lcid;
55 int MSVCRT___lc_codepage;
56 int MSVCRT___lc_collate_cp;
57 HANDLE MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1];
58
59 /* MT */
60 #define LOCK_LOCALE _mlock(_SETLOCALE_LOCK);
61 #define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK);
62
63 #define MSVCRT_LEADBYTE 0x8000
64
65 typedef struct {
66 char search_language[MAX_ELEM_LEN];
67 char search_country[MAX_ELEM_LEN];
68 char search_codepage[MAX_ELEM_LEN];
69 char found_language[MAX_ELEM_LEN];
70 char found_country[MAX_ELEM_LEN];
71 char found_codepage[MAX_ELEM_LEN];
72 unsigned int match_flags;
73 LANGID found_lang_id;
74 } locale_search_t;
75
76 unsigned int __setlc_active;
77 unsigned int __unguarded_readlc_active;
78 int _current_category; /* used by setlocale */
79 const char *_current_locale;
80
81
82 int parse_locale(const char *locale, char *lang, char *country, char *code_page);
83
84 #define _C_ _CONTROL
85 #define _S_ _SPACE
86 #define _P_ _PUNCT
87 #define _D_ _DIGIT
88 #define _H_ _HEX
89 #define _U_ _UPPER
90 #define _L_ _LOWER
91
92 WORD MSVCRT__ctype [257] = {
93 0, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_C_, _S_|_C_,
94 _S_|_C_, _S_|_C_, _S_|_C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_,
95 _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_BLANK,
96 _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_,
97 _P_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_,
98 _D_|_H_, _D_|_H_, _D_|_H_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _U_|_H_,
99 _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_, _U_, _U_, _U_, _U_,
100 _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_,
101 _U_, _P_, _P_, _P_, _P_, _P_, _P_, _L_|_H_, _L_|_H_, _L_|_H_, _L_|_H_,
102 _L_|_H_, _L_|_H_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_,
103 _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _P_, _P_, _P_, _P_,
104 _C_, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
110 };
111
112 /* Internal: Current ctype table for locale */
113 WORD MSVCRT_current_ctype[257];
114
115 /* pctype is used by macros in the Win32 headers. It must point
116 * To a table of flags exactly like ctype. To allow locale
117 * changes to affect ctypes (i.e. isleadbyte), we use a second table
118 * and update its flags whenever the current locale changes.
119 */
120 WORD* MSVCRT__pctype = MSVCRT_current_ctype + 1;
121
122 /* Friendly country strings & iso codes for synonym support.
123 * Based on MS documentation for setlocale().
124 */
125 static const char * const _country_synonyms[] =
126 {
127 "Hong Kong","HK",
128 "Hong-Kong","HK",
129 "New Zealand","NZ",
130 "New-Zealand","NZ",
131 "PR China","CN",
132 "PR-China","CN",
133 "United Kingdom","GB",
134 "United-Kingdom","GB",
135 "Britain","GB",
136 "England","GB",
137 "Great Britain","GB",
138 "United States","US",
139 "United-States","US",
140 "America","US"
141 };
142
143 /* Note: Flags are weighted in order of matching importance */
144 #define FOUND_LANGUAGE 0x4
145 #define FOUND_COUNTRY 0x2
146 #define FOUND_CODEPAGE 0x1
147
148 /* INTERNAL: Map a synonym to an ISO code */
149 static void remap_synonym(char *name)
150 {
151 size_t i;
152 for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
153 {
154 if (!strcasecmp(_country_synonyms[i],name))
155 {
156 TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
157 name[0] = _country_synonyms[i+1][0];
158 name[1] = _country_synonyms[i+1][1];
159 name[2] = '\0';
160 return;
161 }
162 }
163 }
164
165 #define CONTINUE_LOOKING TRUE
166 #define STOP_LOOKING FALSE
167
168 /* INTERNAL: Get and compare locale info with a given string */
169 static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp)
170 {
171 buff[0] = 0;
172 GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN);
173 if (!buff[0] || !cmp[0])
174 return 0;
175 /* Partial matches are allowed, e.g. "Germ" matches "Germany" */
176 return !strncasecmp(cmp, buff, strlen(cmp));
177 }
178
179
180 static BOOL CALLBACK
181 find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam)
182 {
183 locale_search_t *res = (locale_search_t *)lParam;
184 const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
185 char buff[MAX_ELEM_LEN];
186 unsigned int flags = 0;
187
188 if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
189 return CONTINUE_LOOKING;
190
191 /* Check Language */
192 if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language) ||
193 compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language) ||
194 compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language))
195 {
196 TRACE(":Found language: %s->%s\n", res->search_language, buff);
197 flags |= FOUND_LANGUAGE;
198 memcpy(res->found_language,res->search_language,MAX_ELEM_LEN);
199 }
200 else if (res->match_flags & FOUND_LANGUAGE)
201 {
202 return CONTINUE_LOOKING;
203 }
204
205 /* Check Country */
206 if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country) ||
207 compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country) ||
208 compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country))
209 {
210 TRACE("Found country:%s->%s\n", res->search_country, buff);
211 flags |= FOUND_COUNTRY;
212 memcpy(res->found_country,res->search_country,MAX_ELEM_LEN);
213 }
214 else if (res->match_flags & FOUND_COUNTRY)
215 {
216 return CONTINUE_LOOKING;
217 }
218
219 /* Check codepage */
220 if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) ||
221 (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage)))
222 {
223 TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
224 flags |= FOUND_CODEPAGE;
225 memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
226 }
227 else if (res->match_flags & FOUND_CODEPAGE)
228 {
229 return CONTINUE_LOOKING;
230 }
231
232 if (flags > res->match_flags)
233 {
234 /* Found a better match than previously */
235 res->match_flags = flags;
236 res->found_lang_id = LangID;
237 }
238 if (flags & (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 (strcasecmp(locale->search_codepage,"OCP"))
276 {
277 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
278 locale->found_codepage, MAX_ELEM_LEN);
279 }
280 if (strcasecmp(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 = (unsigned char *)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(MSVCRT_mbctype, 0, sizeof(MSVCRT_mbctype));
676
677 bytes = cpi.LeadByte;
678 while (bytes[0] || bytes[1])
679 {
680 for (i = bytes[0]; i <= bytes[1]; i++)
681 MSVCRT_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 MSVCRT_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 (!(MSVCRT_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 (!(MSVCRT_mbctype[i + 1] & _M1))
729 {
730 if ((*curr_type) & C1_UPPER)
731 MSVCRT_mbctype[i + 1] |= _SBUP;
732 if ((*curr_type) & C1_LOWER)
733 MSVCRT_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 MSVCRT_mbctype[i + 1] |= _MP;
746 for (i = 166; i <= 223; i++)
747 MSVCRT_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 FIXME(" stub\n");
807 }
808
809
810 /*********************************************************************
811 * _Strftime (MSVCRT.@)
812 */
813 const char* _Strftime(char *out, unsigned int len, const char *fmt,
814 const void *tm, void *foo)
815 {
816 /* FIXME: */
817 FIXME("(%p %d %s %p %p) stub\n", out, len, fmt, tm, foo);
818 return "";
819 }
820
821
822 /*********************************************************************
823 * _Getdays (MSVCRT.@)
824 */
825 const char* _Getdays(void)
826 {
827 static const char *MSVCRT_days = ":Sun:Sunday:Mon:Monday:Tue:Tuesday:Wed:"
828 "Wednesday:Thu:Thursday:Fri:Friday:Sat:Saturday";
829 /* FIXME: Use locale */
830 FIXME("(void) semi-stub\n");
831 return MSVCRT_days;
832 }
833
834 /*********************************************************************
835 * _Getmonths (MSVCRT.@)
836 */
837 const char* _Getmonths(void)
838 {
839 static const char *MSVCRT_months = ":Jan:January:Feb:February:Mar:March:Apr:"
840 "April:May:May:Jun:June:Jul:July:Aug:August:Sep:September:Oct:"
841 "October:Nov:November:Dec:December";
842 /* FIXME: Use locale */
843 FIXME("(void) semi-stub\n");
844 return MSVCRT_months;
845 }
846
847 /*********************************************************************
848 * __crtLCMapStringA (MSVCRT.@)
849 */
850 int __crtLCMapStringA(
851 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
852 int dstlen, unsigned int codepage, int xflag
853 ) {
854 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
855 lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
856 /* FIXME: A bit incorrect. But msvcrt itself just converts its
857 * arguments to wide strings and then calls LCMapStringW
858 */
859 return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
860 }
861
862 /*********************************************************************
863 * __crtLCMapStringW (MSVCRT.@)
864 */
865 int __crtLCMapStringW(
866 LCID lcid, DWORD mapflags, LPCWSTR src, int srclen, LPWSTR dst,
867 int dstlen, unsigned int codepage, int xflag
868 ) {
869 TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
870 lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
871
872 return LCMapStringW(lcid,mapflags,src,srclen,dst,dstlen);
873 }
874
875 int CDECL _getmbcp(void)
876 {
877 return MSVCRT___lc_codepage;
878 }