ReactOS 0.4.16-dev-340-g0540c21
locale.c
Go to the documentation of this file.
1/*
2 * Locale support
3 *
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2002 Alexandre Julliard for CodeWeavers
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <k32.h>
25
26#define NDEBUG
27#include <debug.h>
29
30#include "lcformat_private.h"
31#ifdef __REACTOS__
32 #include "japanese.h"
33 #define strcasecmp _stricmp
34#endif
35
38
39#undef WINVER
40#define WINVER DLL_EXPORT_VERSION
41
42/* From winnls.h */
43#define LOCALE_NAME_USER_DEFAULT NULL
44
45#define REG_SZ 1
46extern int wine_fold_string(int flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen);
47extern int wine_get_sortkey(int flags, const WCHAR *src, int srclen, char *dst, int dstlen);
48extern int wine_compare_string(int flags, const WCHAR *str1, int len1, const WCHAR *str2, int len2);
49#ifdef __REACTOS__
51#else
53#endif
54#define NLSRC_OFFSET 5000 /* FIXME */
55
57
58#define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
59 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
60#define MB_FLAGSMASK (MB_PRECOMPOSED|MB_COMPOSITE|MB_USEGLYPHCHARS|MB_ERR_INVALID_CHARS)
61#define WC_FLAGSMASK (WC_DISCARDNS|WC_SEPCHARS|WC_DEFAULTCHAR|WC_ERR_INVALID_CHARS|\
62 WC_COMPOSITECHECK|WC_NO_BEST_FIT_CHARS)
63
64/* current code pages */
65static const union cptable *ansi_cptable;
66static const union cptable *oem_cptable;
67static const union cptable *mac_cptable;
68static const union cptable *unix_cptable; /* NULL if UTF8 */
69
70static const WCHAR szLocaleKeyName[] = {
71 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
72 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
73 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
74};
75
76static const WCHAR szLangGroupsKeyName[] = {
77 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
78 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
79 'C','o','n','t','r','o','l','\\','N','l','s','\\',
80 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
81};
82
83#if (WINVER >= 0x0600)
84/* Charset to codepage map, sorted by name. */
85static const struct charset_entry
86{
87 const char *charset_name;
89} charset_names[] =
90{
91 { "BIG5", 950 },
92 { "CP1250", 1250 },
93 { "CP1251", 1251 },
94 { "CP1252", 1252 },
95 { "CP1253", 1253 },
96 { "CP1254", 1254 },
97 { "CP1255", 1255 },
98 { "CP1256", 1256 },
99 { "CP1257", 1257 },
100 { "CP1258", 1258 },
101 { "CP932", 932 },
102 { "CP936", 936 },
103 { "CP949", 949 },
104 { "CP950", 950 },
105 { "EUCJP", 20932 },
106 { "GB2312", 936 },
107 { "IBM037", 37 },
108 { "IBM1026", 1026 },
109 { "IBM424", 424 },
110 { "IBM437", 437 },
111 { "IBM500", 500 },
112 { "IBM850", 850 },
113 { "IBM852", 852 },
114 { "IBM855", 855 },
115 { "IBM857", 857 },
116 { "IBM860", 860 },
117 { "IBM861", 861 },
118 { "IBM862", 862 },
119 { "IBM863", 863 },
120 { "IBM864", 864 },
121 { "IBM865", 865 },
122 { "IBM866", 866 },
123 { "IBM869", 869 },
124 { "IBM874", 874 },
125 { "IBM875", 875 },
126 { "ISO88591", 28591 },
127 { "ISO885910", 28600 },
128 { "ISO885913", 28603 },
129 { "ISO885914", 28604 },
130 { "ISO885915", 28605 },
131 { "ISO885916", 28606 },
132 { "ISO88592", 28592 },
133 { "ISO88593", 28593 },
134 { "ISO88594", 28594 },
135 { "ISO88595", 28595 },
136 { "ISO88596", 28596 },
137 { "ISO88597", 28597 },
138 { "ISO88598", 28598 },
139 { "ISO88599", 28599 },
140 { "KOI8R", 20866 },
141 { "KOI8U", 21866 },
142 { "UTF8", CP_UTF8 }
143};
144#endif
145
146
148{
149 WCHAR win_name[128]; /* Windows name ("en-US") */
150 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
151 WCHAR *country; /* country ("US") */
152 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
153 WCHAR *script; /* script ("Latn") for Windows format only */
154 WCHAR *modifier; /* modifier or sort order */
155 LCID lcid; /* corresponding LCID */
156 int matches; /* number of elements matching LCID (0..4) */
157 UINT codepage; /* codepage corresponding to charset */
158};
159
160/* locale ids corresponding to the various Unix locale parameters */
170
171static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
172static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
173static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
174static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
175static const WCHAR iDateW[] = {'i','D','a','t','e',0};
176static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
177static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
178static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
179static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
180static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
181static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
182static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
183static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
184static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
185static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
186static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
187static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
188static const WCHAR s1159W[] = {'s','1','1','5','9',0};
189static const WCHAR s2359W[] = {'s','2','3','5','9',0};
190static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
191static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
192static const WCHAR sDateW[] = {'s','D','a','t','e',0};
193static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
194static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
195static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
196static const WCHAR sListW[] = {'s','L','i','s','t',0};
197static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
198static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
199static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
200static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
201static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
202static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
203static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
204static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
205static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
206static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
207static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
208static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
209static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
210
211static struct registry_value
212{
214 const WCHAR *name;
216} registry_values[] =
217{
228 { LOCALE_IPAPERSIZE, iPaperSizeW },
229 { LOCALE_ITIME, iTimeW },
230 { LOCALE_S1159, s1159W },
231 { LOCALE_S2359, s2359W },
233 { LOCALE_SDATE, sDateW },
236 { LOCALE_SLIST, sListW },
245 { LOCALE_STIME, sTimeW },
247 { LOCALE_SYEARMONTH, sYearMonthW },
248 /* The following are not listed under MSDN as supported,
249 * but seem to be used and also stored in the registry.
250 */
252 { LOCALE_IDATE, iDateW },
257 /* The following are used in XP and later */
258 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
262
263static RTL_CRITICAL_SECTION cache_section = { NULL, -1, 0, 0, 0, 0 };
264
265#ifndef __REACTOS__
266/* Copy Ascii string to Unicode without using codepages */
267static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
268{
269 while (n > 1 && *src)
270 {
271 *dst++ = (unsigned char)*src++;
272 n--;
273 }
274 if (n) *dst = 0;
275}
276
277static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
278{
279 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
280}
281#endif // !__REACTOS__
282
283/***********************************************************************
284 * get_lcid_codepage
285 *
286 * Retrieve the ANSI codepage for a given locale.
287 */
288static inline UINT get_lcid_codepage( LCID lcid )
289{
290 UINT ret;
291 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
292 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
293 return ret;
294}
295
296#ifndef __REACTOS__
297/***********************************************************************
298 * get_codepage_table
299 *
300 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
301 */
302static const union cptable *get_codepage_table( unsigned int codepage )
303{
304 const union cptable *ret = NULL;
305
306 assert( ansi_cptable ); /* init must have been done already */
307
308 switch(codepage)
309 {
310 case CP_ACP:
311 return ansi_cptable;
312 case CP_OEMCP:
313 return oem_cptable;
314 case CP_MACCP:
315 return mac_cptable;
316 case CP_UTF7:
317 case CP_UTF8:
318 break;
319 case CP_THREAD_ACP:
320 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
321 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
322 if (!codepage) return ansi_cptable;
323 /* fall through */
324 default:
325 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
326 if (codepage == oem_cptable->info.codepage) return oem_cptable;
327 if (codepage == mac_cptable->info.codepage) return mac_cptable;
329 break;
330 }
331 return ret;
332}
333#endif // !__REACTOS__
334
335#if (WINVER >= 0x0600)
336/***********************************************************************
337 * charset_cmp (internal)
338 */
339static int charset_cmp( const void *name, const void *entry )
340{
341 const struct charset_entry *charset = entry;
342 return strcasecmp( name, charset->charset_name );
343}
344
345/***********************************************************************
346 * find_charset
347 */
348static UINT find_charset( const WCHAR *name )
349{
350 const struct charset_entry *entry;
351 char charset_name[16];
352 size_t i, j;
353
354 /* remove punctuation characters from charset name */
355 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
356 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
357 charset_name[j] = 0;
358
359 entry = bsearch( charset_name, charset_names,
360 sizeof(charset_names)/sizeof(charset_names[0]),
361 sizeof(charset_names[0]), charset_cmp );
362 if (entry) return entry->codepage;
363 return 0;
364}
365#endif // (WINVER >= 0x0600)
366
368{
369 switch (lang)
370 {
380 }
382 return lang;
383}
384
385#if (WINVER >= 0x0600)
386/***********************************************************************
387 * find_locale_id_callback
388 */
391{
392 struct locale_name *data = (struct locale_name *)lParam;
393 WCHAR buffer[128];
394 int matches = 0;
395 LCID lcid = MAKELCID( lang, SORT_DEFAULT ); /* FIXME: handle sort order */
396
397 if (PRIMARYLANGID(lang) == LANG_NEUTRAL) return TRUE; /* continue search */
398
399 /* first check exact name */
400 if (data->win_name[0] &&
402 buffer, sizeof(buffer)/sizeof(WCHAR) ))
403 {
404 if (!strcmpiW( data->win_name, buffer ))
405 {
406 matches = 4; /* everything matches */
407 goto done;
408 }
409 }
410
412 buffer, sizeof(buffer)/sizeof(WCHAR) ))
413 return TRUE;
414 if (strcmpiW( buffer, data->lang )) return TRUE;
415 matches++; /* language name matched */
416
417 if (data->script)
418 {
419 if (GetLocaleInfoW( lcid, LOCALE_SSCRIPTS | LOCALE_NOUSEROVERRIDE,
420 buffer, sizeof(buffer)/sizeof(WCHAR) ))
421 {
422 const WCHAR *p = buffer;
423 unsigned int len = strlenW( data->script );
424 while (*p)
425 {
426 if (!strncmpiW( p, data->script, len ) && (!p[len] || p[len] == ';')) break;
427 if (!(p = strchrW( p, ';'))) goto done;
428 p++;
429 }
430 if (!*p) goto done;
431 matches++; /* script matched */
432 }
433 }
434
435 if (data->country)
436 {
438 buffer, sizeof(buffer)/sizeof(WCHAR) ))
439 {
440 if (strcmpiW( buffer, data->country )) goto done;
441 matches++; /* country name matched */
442 }
443 }
444 else /* match default language */
445 {
446 LANGID def_lang = data->script ? lang : MAKELANGID( PRIMARYLANGID(lang), LANG_NEUTRAL );
447 if (lang == get_default_sublang( def_lang )) matches++;
448 }
449
450 if (data->codepage)
451 {
452 UINT unix_cp;
453 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
454 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
455 {
456 if (unix_cp == data->codepage) matches++;
457 }
458 }
459
460 /* FIXME: check sort order */
461
462done:
463 if (matches > data->matches)
464 {
465 data->lcid = lcid;
466 data->matches = matches;
467 }
468 return (data->matches < 4); /* no need to continue for perfect match */
469}
470
471
472/***********************************************************************
473 * parse_locale_name
474 *
475 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
476 * Unix format is: lang[_country][.charset][@modifier]
477 * Windows format is: lang[-script][-country][_modifier]
478 */
479static void parse_locale_name( const WCHAR *str, struct locale_name *name )
480{
481 static const WCHAR sepW[] = {'-','_','.','@',0};
482 static const WCHAR winsepW[] = {'-','_',0};
483 static const WCHAR posixW[] = {'P','O','S','I','X',0};
484 static const WCHAR cW[] = {'C',0};
485 static const WCHAR latinW[] = {'l','a','t','i','n',0};
486 static const WCHAR latnW[] = {'-','L','a','t','n',0};
487 WCHAR *p;
488
489 TRACE("%s\n", debugstr_w(str));
490
491 name->country = name->charset = name->script = name->modifier = NULL;
493 name->matches = 0;
494 name->codepage = 0;
495 name->win_name[0] = 0;
496 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
497
498 if (!*name->lang)
499 {
500 name->lcid = LOCALE_INVARIANT;
501 name->matches = 4;
502 return;
503 }
504
505 if (!(p = strpbrkW( name->lang, sepW )))
506 {
507 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
508 {
509 name->matches = 4; /* perfect match for default English lcid */
510 return;
511 }
512 strcpyW( name->win_name, name->lang );
513 }
514 else if (*p == '-') /* Windows format */
515 {
516 strcpyW( name->win_name, name->lang );
517 *p++ = 0;
518 name->country = p;
519 if ((p = strpbrkW( p, winsepW )) && *p == '-')
520 {
521 *p++ = 0;
522 name->script = name->country;
523 name->country = p;
524 p = strpbrkW( p, winsepW );
525 }
526 if (p)
527 {
528 *p++ = 0;
529 name->modifier = p;
530 }
531 /* second value can be script or country, check length to resolve the ambiguity */
532 if (!name->script && strlenW( name->country ) == 4)
533 {
534 name->script = name->country;
535 name->country = NULL;
536 }
537 }
538 else /* Unix format */
539 {
540 if (*p == '_')
541 {
542 *p++ = 0;
543 name->country = p;
544 p = strpbrkW( p, sepW + 2 );
545 }
546 if (p && *p == '.')
547 {
548 *p++ = 0;
549 name->charset = p;
550 p = strchrW( p, '@' );
551 }
552 if (p)
553 {
554 *p++ = 0;
555 name->modifier = p;
556 }
557
558 if (name->charset)
559 name->codepage = find_charset( name->charset );
560
561 /* rebuild a Windows name if possible */
562
563 if (name->charset) goto done; /* can't specify charset in Windows format */
564 if (name->modifier && strcmpW( name->modifier, latinW ))
565 goto done; /* only Latn script supported for now */
566 strcpyW( name->win_name, name->lang );
567 if (name->modifier) strcatW( name->win_name, latnW );
568 if (name->country)
569 {
570 p = name->win_name + strlenW(name->win_name);
571 *p++ = '-';
572 strcpyW( p, name->country );
573 }
574 }
575done:
578}
579#endif
580
581
582/***********************************************************************
583 * convert_default_lcid
584 *
585 * Get the default LCID to use for a given lctype in GetLocaleInfo.
586 */
588{
592 {
593 LCID default_id = 0;
594
595 switch(lctype & 0xffff)
596 {
597 case LOCALE_SSORTNAME:
598 default_id = lcid_LC_COLLATE;
599 break;
600
604 case LOCALE_IDEFAULTEBCDICCODEPAGE:
607 default_id = lcid_LC_CTYPE;
608 break;
609
611 case LOCALE_ICURRENCY:
613 case LOCALE_INEGCURR:
620 case LOCALE_SCURRENCY:
625 case LOCALE_SNATIVECURRNAME:
626 default_id = lcid_LC_MONETARY;
627 break;
628
629 case LOCALE_IDIGITS:
630 case LOCALE_IDIGITSUBSTITUTION:
631 case LOCALE_ILZERO:
633 case LOCALE_SDECIMAL:
634 case LOCALE_SGROUPING:
635 //case LOCALE_SNAN:
638 //case LOCALE_SNEGINFINITY:
639 //case LOCALE_SPOSINFINITY:
641 case LOCALE_STHOUSAND:
642 default_id = lcid_LC_NUMERIC;
643 break;
644
646 case LOCALE_ICENTURY:
647 case LOCALE_IDATE:
648 case LOCALE_IDAYLZERO:
651 case LOCALE_ILDATE:
652 case LOCALE_IMONLZERO:
654 case LOCALE_ITIME:
656 case LOCALE_ITLZERO:
657 case LOCALE_S1159:
658 case LOCALE_S2359:
679 case LOCALE_SDATE:
680 case LOCALE_SDAYNAME1:
681 case LOCALE_SDAYNAME2:
682 case LOCALE_SDAYNAME3:
683 case LOCALE_SDAYNAME4:
684 case LOCALE_SDAYNAME5:
685 case LOCALE_SDAYNAME6:
686 case LOCALE_SDAYNAME7:
687 //case LOCALE_SDURATION:
688 case LOCALE_SLONGDATE:
703 //case LOCALE_SSHORTESTDAYNAME1:
704 //case LOCALE_SSHORTESTDAYNAME2:
705 //case LOCALE_SSHORTESTDAYNAME3:
706 //case LOCALE_SSHORTESTDAYNAME4:
707 //case LOCALE_SSHORTESTDAYNAME5:
708 //case LOCALE_SSHORTESTDAYNAME6:
709 //case LOCALE_SSHORTESTDAYNAME7:
710 case LOCALE_STIME:
712 case LOCALE_SYEARMONTH:
713 default_id = lcid_LC_TIME;
714 break;
715
716 case LOCALE_IPAPERSIZE:
717 default_id = lcid_LC_PAPER;
718 break;
719
720 case LOCALE_IMEASURE:
721 default_id = lcid_LC_MEASUREMENT;
722 break;
723
724 case LOCALE_ICOUNTRY:
725 default_id = lcid_LC_TELEPHONE;
726 break;
727 }
728 if (default_id) lcid = default_id;
729 }
730 return ConvertDefaultLocale( lcid );
731}
732
733/***********************************************************************
734 * is_genitive_name_supported
735 *
736 * Determine could LCTYPE basically support genitive name form or not.
737 */
739{
740 switch(lctype & 0xffff)
741 {
755 return TRUE;
756 default:
757 return FALSE;
758 }
759}
760
761/***********************************************************************
762 * create_registry_key
763 *
764 * Create the Control Panel\\International registry key.
765 */
766static inline HANDLE create_registry_key(void)
767{
768 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
769 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
772 HANDLE cpl_key, hkey = 0;
773
774 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
775
776 attr.Length = sizeof(attr);
777 attr.RootDirectory = hkey;
778 attr.ObjectName = &nameW;
779 attr.Attributes = 0;
780 attr.SecurityDescriptor = NULL;
781 attr.SecurityQualityOfService = NULL;
782 RtlInitUnicodeString( &nameW, cplW );
783
784 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
785 {
786 NtClose( attr.RootDirectory );
787 attr.RootDirectory = cpl_key;
788 RtlInitUnicodeString( &nameW, intlW );
789 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
790 }
791 NtClose( attr.RootDirectory );
792 return hkey;
793}
794
795
796#ifndef __REACTOS__
797/* update the registry settings for a given locale parameter */
798/* return TRUE if an update was needed */
800 const LCTYPE *values, UINT nb_values )
801{
802 static const WCHAR formatW[] = { '%','0','8','x',0 };
803 WCHAR bufferW[40];
805 DWORD count, i;
806
808 count = sizeof(bufferW);
810 {
812 LPCWSTR text = (LPCWSTR)info->Data;
813
814 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
815 TRACE( "updating registry, locale %s changed %s -> %08x\n",
817 }
818 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
819 sprintfW( bufferW, formatW, lcid );
820 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
821
822 for (i = 0; i < nb_values; i++)
823 {
825 sizeof(bufferW)/sizeof(WCHAR) );
826 SetLocaleInfoW( lcid, values[i], bufferW );
827 }
828 return TRUE;
829}
830
831
832/***********************************************************************
833 * LOCALE_InitRegistry
834 *
835 * Update registry contents on startup if the user locale has changed.
836 * This simulates the action of the Windows control panel.
837 */
839{
840 static const WCHAR acpW[] = {'A','C','P',0};
841 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
842 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
843 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
844 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
845 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
846 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
847 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
848 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
849 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
850 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
851 static const struct
852 {
855 } update_cp_values[] = {
857 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
859 };
860 static const LCTYPE lc_messages_values[] = {
863 LOCALE_SLIST };
864 static const LCTYPE lc_monetary_values[] = {
873 static const LCTYPE lc_numeric_values[] = {
877 LOCALE_IDIGITSUBSTITUTION,
883 static const LCTYPE lc_time_values[] = {
897 LOCALE_SYEARMONTH,
898 LOCALE_IDATE };
899 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
900 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
901 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
902
904 WCHAR bufferW[80];
905 DWORD count, i;
906 HANDLE hkey;
908
909 if (!(hkey = create_registry_key()))
910 return; /* don't do anything if we can't create the registry key */
911
912 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
913 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
914 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
915 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
916 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
917 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
918 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
919 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
920 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
921 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
922 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
923 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
924 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
925 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
926
927 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
928 {
929 static const WCHAR codepageW[] =
930 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
931 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
932 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
933
935 HANDLE nls_key;
936 DWORD len = 14;
937
938 RtlInitUnicodeString( &nameW, codepageW );
940 while (codepageW[len])
941 {
942 nameW.Length = len * sizeof(WCHAR);
943 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
944 NtClose( nls_key );
945 len++;
946 while (codepageW[len] && codepageW[len] != '\\') len++;
947 }
948 nameW.Length = len * sizeof(WCHAR);
949 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
950 {
951 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
952 {
953 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
954 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
955 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
956 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
957 }
958 NtClose( nls_key );
959 }
960 }
961
962 NtClose( hkey );
963}
964
965
966#ifdef __APPLE__
967/***********************************************************************
968 * get_mac_locale
969 *
970 * Return a locale identifier string reflecting the Mac locale, in a form
971 * that parse_locale_name() will understand. So, strip out unusual
972 * things like script, variant, etc. Or, rather, just construct it as
973 * <lang>[_<country>].UTF-8.
974 */
975static const char* get_mac_locale(void)
976{
977 static char mac_locale[50];
978
979 if (!mac_locale[0])
980 {
981 CFLocaleRef locale = CFLocaleCopyCurrent();
982 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
983 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
984 CFStringRef locale_string;
985
986 if (country)
987 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
988 else
989 locale_string = CFStringCreateCopy(NULL, lang);
990
991 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
992 strcat(mac_locale, ".UTF-8");
993
994 CFRelease(locale);
995 CFRelease(locale_string);
996 }
997
998 return mac_locale;
999}
1000
1001
1002/***********************************************************************
1003 * has_env
1004 */
1005static BOOL has_env(const char* name)
1006{
1007 const char* value = getenv( name );
1008 return value && value[0];
1009}
1010#endif
1011
1012
1013/***********************************************************************
1014 * get_locale
1015 *
1016 * Get the locale identifier for a given category. On most platforms,
1017 * this is just a thin wrapper around setlocale(). On OS X, though, it
1018 * is common for the Mac locale settings to not be supported by the C
1019 * library. So, we sometimes override the result with the Mac locale.
1020 */
1021static const char* get_locale(int category, const char* category_name)
1022{
1023 const char* ret = setlocale(category, NULL);
1024
1025#ifdef __ANDROID__
1026 if (!strcmp(ret, "C"))
1027 {
1028 ret = getenv( category_name );
1029 if (!ret || !ret[0]) ret = getenv( "LC_ALL" );
1030 if (!ret || !ret[0]) ret = "C";
1031 }
1032#endif
1033
1034#ifdef __APPLE__
1035 /* If LC_ALL is set, respect it as a user override.
1036 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
1037 and equal to UTF-8. That's because, when the Mac locale isn't supported
1038 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
1039 parse_locale_name() doesn't handle that properly, so we override that
1040 with the Mac locale (which uses UTF-8 for the charset, anyway).
1041 Otherwise:
1042 For LC_MESSAGES, we override the C library because the user language
1043 setting is separate from the locale setting on which LANG was based.
1044 If the C library didn't get anything better from LANG than C or POSIX,
1045 override that. That probably means the Mac locale isn't supported by
1046 the C library. */
1047 if (!has_env( "LC_ALL" ) &&
1048 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
1049 (!has_env( category_name ) &&
1050 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
1051 {
1052 const char* override = get_mac_locale();
1053
1054 if (category == LC_MESSAGES)
1055 {
1056 /* Retrieve the preferred language as chosen in System Preferences. */
1057 static char messages_locale[50];
1058
1059 if (!messages_locale[0])
1060 {
1061 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1062 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1063 {
1064 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1065 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1066 if (components)
1067 {
1068 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1069 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1070 CFLocaleRef locale = NULL;
1071 CFStringRef locale_string;
1072
1073 if (!country)
1074 {
1075 locale = CFLocaleCopyCurrent();
1076 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1077 }
1078
1079 if (country)
1080 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1081 else
1082 locale_string = CFStringCreateCopy( NULL, lang );
1083 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1084 strcat( messages_locale, ".UTF-8" );
1085
1086 CFRelease( locale_string );
1087 if (locale) CFRelease( locale );
1088 CFRelease( components );
1089 }
1090 }
1091 if (preferred_langs)
1092 CFRelease( preferred_langs );
1093 }
1094
1095 if (messages_locale[0])
1096 override = messages_locale;
1097 }
1098
1099 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1100 ret = override;
1101 }
1102#endif
1103
1104 return ret;
1105}
1106
1107
1108/***********************************************************************
1109 * setup_unix_locales
1110 */
1112{
1113 struct locale_name locale_name;
1114 WCHAR buffer[128], ctype_buff[128];
1115 const char *locale;
1116 UINT unix_cp = 0;
1117
1118 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1119 {
1120 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
1121 parse_locale_name( ctype_buff, &locale_name );
1123 unix_cp = locale_name.codepage;
1124 }
1125 if (!lcid_LC_CTYPE) /* this one needs a default value */
1127
1128 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1130
1131#define GET_UNIX_LOCALE(cat) do \
1132 if ((locale = get_locale( cat, #cat ))) \
1133 { \
1134 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
1135 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1136 else { \
1137 parse_locale_name( buffer, &locale_name ); \
1138 lcid_##cat = locale_name.lcid; \
1139 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1140 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1141 } \
1142 } while (0)
1143
1145 GET_UNIX_LOCALE( LC_MESSAGES );
1149#ifdef LC_PAPER
1150 GET_UNIX_LOCALE( LC_PAPER );
1151#endif
1152#ifdef LC_MEASUREMENT
1153 GET_UNIX_LOCALE( LC_MEASUREMENT );
1154#endif
1155#ifdef LC_TELEPHONE
1156 GET_UNIX_LOCALE( LC_TELEPHONE );
1157#endif
1158
1159#undef GET_UNIX_LOCALE
1160
1161 return unix_cp;
1162}
1163#endif // !__REACTOS__
1164
1165
1166/***********************************************************************
1167 * GetUserDefaultLangID (KERNEL32.@)
1168 *
1169 * Get the default language Id for the current user.
1170 *
1171 * PARAMS
1172 * None.
1173 *
1174 * RETURNS
1175 * The current LANGID of the default language for the current user.
1176 */
1178{
1180}
1181
1182
1183/***********************************************************************
1184 * GetSystemDefaultLangID (KERNEL32.@)
1185 *
1186 * Get the default language Id for the system.
1187 *
1188 * PARAMS
1189 * None.
1190 *
1191 * RETURNS
1192 * The current LANGID of the default language for the system.
1193 */
1195{
1197}
1198
1199
1200/***********************************************************************
1201 * GetUserDefaultLCID (KERNEL32.@)
1202 *
1203 * Get the default locale Id for the current user.
1204 *
1205 * PARAMS
1206 * None.
1207 *
1208 * RETURNS
1209 * The current LCID of the default locale for the current user.
1210 */
1212{
1213 LCID lcid;
1215 return lcid;
1216}
1217
1218
1219/***********************************************************************
1220 * GetSystemDefaultLCID (KERNEL32.@)
1221 *
1222 * Get the default locale Id for the system.
1223 *
1224 * PARAMS
1225 * None.
1226 *
1227 * RETURNS
1228 * The current LCID of the default locale for the system.
1229 */
1231{
1232 LCID lcid;
1234 return lcid;
1235}
1236
1237#ifndef __REACTOS__
1238/***********************************************************************
1239 * GetSystemDefaultLocaleName (KERNEL32.@)
1240 */
1242{
1244 return LCIDToLocaleName(lcid, localename, len, 0);
1245}
1246
1248{
1249 LCTYPE type;
1250 int lsize;
1251
1252 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1253
1254 if (flags & MUI_LANGUAGE_ID)
1256 else
1258
1260 if (!lsize)
1261 {
1262 /* keep last error from callee */
1263 return FALSE;
1264 }
1265 lsize++;
1266 if (!*size)
1267 {
1268 *size = lsize;
1269 *count = 1;
1270 return TRUE;
1271 }
1272
1273 if (lsize > *size)
1274 {
1276 return FALSE;
1277 }
1278
1280 {
1281 /* keep last error from callee */
1282 return FALSE;
1283 }
1284
1285 buffer[lsize-1] = 0;
1286 *size = lsize;
1287 *count = 1;
1288 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1289 return TRUE;
1290
1291}
1292
1293/***********************************************************************
1294 * GetSystemPreferredUILanguages (KERNEL32.@)
1295 */
1297{
1298 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1299 {
1301 return FALSE;
1302 }
1303 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1304 {
1306 return FALSE;
1307 }
1308 if (*size && !buffer)
1309 {
1311 return FALSE;
1312 }
1313
1315}
1316
1317/***********************************************************************
1318 * SetThreadPreferredUILanguages (KERNEL32.@)
1319 */
1321{
1322 FIXME( "%u, %p, %p\n", flags, buffer, count );
1323 return TRUE;
1324}
1325
1326/***********************************************************************
1327 * GetThreadPreferredUILanguages (KERNEL32.@)
1328 */
1330{
1331 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1333}
1334
1335#if (WINVER >= 0x0600)
1336/******************************************************************************
1337 * GetUserPreferredUILanguages (KERNEL32.@)
1338 */
1340{
1341 TRACE( "%u %p %p %p\n", flags, count, buffer, size );
1342
1343 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID))
1344 {
1346 return FALSE;
1347 }
1348 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1349 {
1351 return FALSE;
1352 }
1353 if (*size && !buffer)
1354 {
1356 return FALSE;
1357 }
1358
1360}
1361#endif // (WINVER >= 0x0600)
1362#endif // !__REACTOS__
1363
1364/***********************************************************************
1365 * GetUserDefaultUILanguage (KERNEL32.@)
1366 *
1367 * Get the default user interface language Id for the current user.
1368 *
1369 * PARAMS
1370 * None.
1371 *
1372 * RETURNS
1373 * The current LANGID of the default UI language for the current user.
1374 */
1376{
1377 LANGID lang;
1379 return lang;
1380}
1381
1382
1383/***********************************************************************
1384 * GetSystemDefaultUILanguage (KERNEL32.@)
1385 *
1386 * Get the default user interface language Id for the system.
1387 *
1388 * PARAMS
1389 * None.
1390 *
1391 * RETURNS
1392 * The current LANGID of the default UI language for the system. This is
1393 * typically the same language used during the installation process.
1394 */
1396{
1397 LANGID lang;
1399 return lang;
1400}
1401
1402#if (WINVER >= 0x0600)
1403/***********************************************************************
1404 * LocaleNameToLCID (KERNEL32.@)
1405 */
1406LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1407{
1408 struct locale_name locale_name;
1409
1410 if (flags) FIXME( "unsupported flags %x\n", flags );
1411
1413 return GetUserDefaultLCID();
1414
1415 /* string parsing */
1416 parse_locale_name( name, &locale_name );
1417
1418 TRACE( "found lcid %x for %s, matches %d\n",
1420
1421 if (!locale_name.matches)
1422 {
1424 return 0;
1425 }
1426
1427 if (locale_name.matches == 1)
1428 WARN( "locale %s not recognized, defaulting to %s\n",
1430
1431 return locale_name.lcid;
1432}
1433
1434
1435/***********************************************************************
1436 * LCIDToLocaleName (KERNEL32.@)
1437 */
1439{
1440 if (flags) FIXME( "unsupported flags %x\n", flags );
1441
1443}
1444#endif
1445
1446
1447/******************************************************************************
1448 * get_locale_registry_value
1449 *
1450 * Gets the registry value name and cache for a given lctype.
1451 */
1453{
1454 int i;
1455 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1457 return &registry_values[i];
1458 return NULL;
1459}
1460
1461
1462/******************************************************************************
1463 * get_registry_locale_info
1464 *
1465 * Retrieve user-modified locale info from the registry.
1466 * Return length, 0 on error, -1 if not found.
1467 */
1469{
1470 DWORD size;
1471 INT ret;
1472 HANDLE hkey;
1476 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1477
1479
1481 {
1482 if (!(hkey = create_registry_key()))
1483 {
1485 return -1;
1486 }
1487
1489 size = info_size + len * sizeof(WCHAR);
1490
1491 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1492 {
1493 NtClose( hkey );
1496 return 0;
1497 }
1498
1500
1501 /* try again with a bigger buffer when we have to return the correct size */
1502 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1503 {
1505 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1506 {
1507 info = new_info;
1509 }
1510 }
1511
1512 NtClose( hkey );
1513
1514 if (!status)
1515 {
1516 INT length = (size - info_size) / sizeof(WCHAR);
1518
1519 if (!length || ((WCHAR *)&info->Data)[length-1])
1520 length++;
1521
1522 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1523
1524 if (!cached_value)
1525 {
1526 HeapFree( GetProcessHeap(), 0, info );
1529 return 0;
1530 }
1531
1532 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1533 cached_value[length-1] = 0;
1534 HeapFree( GetProcessHeap(), 0, info );
1536 }
1537 else
1538 {
1540 {
1541 ret = (size - info_size) / sizeof(WCHAR);
1542 }
1544 {
1545 ret = -1;
1546 }
1547 else
1548 {
1550 ret = 0;
1551 }
1552 HeapFree( GetProcessHeap(), 0, info );
1554 return ret;
1555 }
1556 }
1557
1559
1560 if (buffer)
1561 {
1562 if (ret > len)
1563 {
1565 ret = 0;
1566 }
1567 else
1568 {
1570 }
1571 }
1572
1574
1575 return ret;
1576}
1577
1578
1579/******************************************************************************
1580 * GetLocaleInfoA (KERNEL32.@)
1581 *
1582 * Get information about an aspect of a locale.
1583 *
1584 * PARAMS
1585 * lcid [I] LCID of the locale
1586 * lctype [I] LCTYPE_ flags from "winnls.h"
1587 * buffer [O] Destination for the information
1588 * len [I] Length of buffer in characters
1589 *
1590 * RETURNS
1591 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1592 * with the information.
1593 * Failure: 0. Use GetLastError() to determine the cause.
1594 *
1595 * NOTES
1596 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1597 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1598 * which is a bit string.
1599 */
1601{
1602 WCHAR *bufferW;
1603 INT lenW, ret;
1604
1605 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1606
1607 if (len < 0 || (len && !buffer))
1608 {
1610 return 0;
1611 }
1614 {
1616 return 0;
1617 }
1618
1619 if (!len) buffer = NULL;
1620
1621 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1622
1623 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1624 {
1626 return 0;
1627 }
1628 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1629 {
1630 if ((lctype & LOCALE_RETURN_NUMBER) ||
1632 {
1633 /* it's not an ASCII string, just bytes */
1634 ret *= sizeof(WCHAR);
1635 if (buffer)
1636 {
1637 if (ret <= len) memcpy( buffer, bufferW, ret );
1638 else
1639 {
1641 ret = 0;
1642 }
1643 }
1644 }
1645 else
1646 {
1649 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1650 }
1651 }
1652 HeapFree( GetProcessHeap(), 0, bufferW );
1653 return ret;
1654}
1655
1657{
1658 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1659}
1660
1661/******************************************************************************
1662 * GetLocaleInfoW (KERNEL32.@)
1663 *
1664 * See GetLocaleInfoA.
1665 */
1667{
1668 LANGID lang_id;
1669 HRSRC hrsrc;
1670 HGLOBAL hmem;
1671 INT ret;
1672 UINT lcflags;
1673 const WCHAR *p;
1674 unsigned int i;
1675
1676 if (len < 0 || (len && !buffer))
1677 {
1679 return 0;
1680 }
1683 {
1685 return 0;
1686 }
1687
1688 if (!len) buffer = NULL;
1689
1690 lcid = convert_default_lcid( lcid, lctype );
1691
1693 lctype &= 0xffff;
1694
1695 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1696
1697 /* first check for overrides in the registry */
1698
1699 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1701 {
1703
1704 if (value)
1705 {
1706 if (lcflags & LOCALE_RETURN_NUMBER)
1707 {
1708 WCHAR tmp[16];
1709 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1710 if (ret > 0)
1711 {
1712 WCHAR *end;
1714 if (*end) /* invalid number */
1715 {
1717 return 0;
1718 }
1719 ret = sizeof(UINT)/sizeof(WCHAR);
1720 if (!buffer) return ret;
1721 if (ret > len)
1722 {
1724 return 0;
1725 }
1726 memcpy( buffer, &number, sizeof(number) );
1727 }
1728 }
1730
1731 if (ret != -1) return ret;
1732 }
1733 }
1734
1735 /* now load it from kernel resources */
1736
1737 lang_id = LANGIDFROMLCID( lcid );
1738
1739 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1740 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL) lang_id = get_default_sublang( lang_id );
1741
1743 ULongToPtr((lctype >> 4) + 1), lang_id )))
1744 {
1745 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1746 return 0;
1747 }
1748 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1749 return 0;
1750
1751 p = LockResource( hmem );
1752 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1753
1754 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1755 else if (is_genitive_name_supported( lctype ) && *p)
1756 {
1757 /* genitive form's stored after a null separator from a nominative */
1758 for (i = 1; i <= *p; i++) if (!p[i]) break;
1759
1760 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1761 {
1762 ret = *p - i + 1;
1763 p += i;
1764 }
1765 else ret = i;
1766 }
1767 else
1768 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1769
1770 if (!buffer) return ret;
1771
1772 if (ret > len)
1773 {
1775 return 0;
1776 }
1777
1778 if (lcflags & LOCALE_RETURN_NUMBER)
1779 {
1780 UINT number;
1781 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1782 if (!tmp) return 0;
1783 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1784 tmp[*p] = 0;
1786 if (!*end)
1787 memcpy( buffer, &number, sizeof(number) );
1788 else /* invalid number */
1789 {
1791 ret = 0;
1792 }
1793 HeapFree( GetProcessHeap(), 0, tmp );
1794
1795 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1796 lcid, lctype, buffer, len, number );
1797 }
1798 else
1799 {
1800 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1801 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1802
1803 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1804 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1805 }
1806 return ret;
1807}
1808
1809#if (WINVER >= 0x0600)
1810/******************************************************************************
1811 * GetLocaleInfoEx (KERNEL32.@)
1812 */
1813INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1814{
1815 LCID lcid = LocaleNameToLCID(locale, 0);
1816
1817 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1818
1819 if (!lcid) return 0;
1820
1821 /* special handling for neutral locale names */
1822 if (locale && strlenW(locale) == 2)
1823 {
1824 switch (info)
1825 {
1826 case LOCALE_SNAME:
1827 if (len && len < 3)
1828 {
1830 return 0;
1831 }
1832 if (len) strcpyW(buffer, locale);
1833 return 3;
1834 case LOCALE_SPARENT:
1835 if (len) buffer[0] = 0;
1836 return 1;
1837 }
1838 }
1839
1840 return GetLocaleInfoW(lcid, info, buffer, len);
1841}
1842
1843BOOL
1844WINAPI
1846 LPCWSTR lpLocaleName
1847)
1848{
1849 TRACE( "IsValidLocaleName not implemented (lpLocaleName=%s)\n", debugstr_w(lpLocaleName));
1850 return TRUE;
1851}
1852
1853INT
1854WINAPI
1856 LPWSTR lpLocaleName,
1857 INT cchLocaleName
1858)
1859{
1860 TRACE( "GetUserDefaultLocaleName not implemented (lpLocaleName=%s, cchLocaleName=%d)\n", debugstr_w(lpLocaleName), cchLocaleName);
1861 return 0;
1862}
1863#endif
1864
1865/******************************************************************************
1866 * SetLocaleInfoA [KERNEL32.@]
1867 *
1868 * Set information about an aspect of a locale.
1869 *
1870 * PARAMS
1871 * lcid [I] LCID of the locale
1872 * lctype [I] LCTYPE_ flags from "winnls.h"
1873 * data [I] Information to set
1874 *
1875 * RETURNS
1876 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1877 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1878 * Failure: FALSE. Use GetLastError() to determine the cause.
1879 *
1880 * NOTES
1881 * - Values are only be set for the current user locale; the system locale
1882 * settings cannot be changed.
1883 * - Any settings changed by this call are lost when the locale is changed by
1884 * the control panel (in Wine, this happens every time you change LANG).
1885 * - The native implementation of this function does not check that lcid matches
1886 * the current user locale, and simply sets the new values. Wine warns you in
1887 * this case, but behaves the same.
1888 */
1890{
1892 WCHAR *strW;
1893 DWORD len;
1894 BOOL ret;
1895
1897
1898 if (!data)
1899 {
1901 return FALSE;
1902 }
1903 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1904 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1905 {
1907 return FALSE;
1908 }
1910 ret = SetLocaleInfoW( lcid, lctype, strW );
1911 HeapFree( GetProcessHeap(), 0, strW );
1912 return ret;
1913}
1914
1915
1916/******************************************************************************
1917 * SetLocaleInfoW (KERNEL32.@)
1918 *
1919 * See SetLocaleInfoA.
1920 */
1922{
1923 struct registry_value *value;
1924 static const WCHAR intlW[] = {'i','n','t','l',0 };
1927 HANDLE hkey;
1928
1929 lctype &= 0xffff;
1931
1932 if (!data || !value)
1933 {
1935 return FALSE;
1936 }
1937
1939 {
1941 return FALSE;
1942 }
1943
1944 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1945
1946 /* FIXME: should check that data to set is sane */
1947
1948 /* FIXME: profile functions should map to registry */
1949 WriteProfileStringW( intlW, value->name, data );
1950
1951 if (!(hkey = create_registry_key())) return FALSE;
1953 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, (PVOID)data, (strlenW(data)+1)*sizeof(WCHAR) );
1954
1956 HeapFree( GetProcessHeap(), 0, value->cached_value );
1957 value->cached_value = NULL;
1959
1961 {
1962 /* Set I-value from S value */
1963 WCHAR *lpD, *lpM, *lpY;
1964 WCHAR szBuff[2];
1965
1966 lpD = strrchrW(data, 'd');
1967 lpM = strrchrW(data, 'M');
1968 lpY = strrchrW(data, 'y');
1969
1970 if (lpD <= lpM)
1971 {
1972 szBuff[0] = '1'; /* D-M-Y */
1973 }
1974 else
1975 {
1976 if (lpY <= lpM)
1977 szBuff[0] = '2'; /* Y-M-D */
1978 else
1979 szBuff[0] = '0'; /* M-D-Y */
1980 }
1981
1982 szBuff[1] = '\0';
1983
1986 else
1988
1990
1991 WriteProfileStringW( intlW, value->name, szBuff );
1992
1994 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1995
1997 HeapFree( GetProcessHeap(), 0, value->cached_value );
1998 value->cached_value = NULL;
2000 }
2001
2002 NtClose( hkey );
2003
2005 return !status;
2006}
2007
2008
2009#ifndef __REACTOS__
2010/******************************************************************************
2011 * GetACP (KERNEL32.@)
2012 *
2013 * Get the current Ansi code page Id for the system.
2014 *
2015 * PARAMS
2016 * None.
2017 *
2018 * RETURNS
2019 * The current Ansi code page identifier for the system.
2020 */
2022{
2024 return ansi_cptable->info.codepage;
2025}
2026
2027
2028/******************************************************************************
2029 * SetCPGlobal (KERNEL32.@)
2030 *
2031 * Set the current Ansi code page Id for the system.
2032 *
2033 * PARAMS
2034 * acp [I] code page ID to be the new ACP.
2035 *
2036 * RETURNS
2037 * The previous ACP.
2038 */
2040{
2041 UINT ret = GetACP();
2042 const union cptable *new_cptable = wine_cp_get_table( acp );
2043
2044 if (new_cptable) ansi_cptable = new_cptable;
2045 return ret;
2046}
2047
2048
2049/***********************************************************************
2050 * GetOEMCP (KERNEL32.@)
2051 *
2052 * Get the current OEM code page Id for the system.
2053 *
2054 * PARAMS
2055 * None.
2056 *
2057 * RETURNS
2058 * The current OEM code page identifier for the system.
2059 */
2061{
2063 return oem_cptable->info.codepage;
2064}
2065
2066
2067/***********************************************************************
2068 * IsValidCodePage (KERNEL32.@)
2069 *
2070 * Determine if a given code page identifier is valid.
2071 *
2072 * PARAMS
2073 * codepage [I] Code page Id to verify.
2074 *
2075 * RETURNS
2076 * TRUE, If codepage is valid and available on the system,
2077 * FALSE otherwise.
2078 */
2080{
2081 switch(codepage) {
2082 case CP_UTF7:
2083 case CP_UTF8:
2084 return TRUE;
2085 default:
2086 return wine_cp_get_table( codepage ) != NULL;
2087 }
2088}
2089
2090
2091/***********************************************************************
2092 * IsDBCSLeadByteEx (KERNEL32.@)
2093 *
2094 * Determine if a character is a lead byte in a given code page.
2095 *
2096 * PARAMS
2097 * codepage [I] Code page for the test.
2098 * testchar [I] Character to test
2099 *
2100 * RETURNS
2101 * TRUE, if testchar is a lead byte in codepage,
2102 * FALSE otherwise.
2103 */
2105{
2106 const union cptable *table = get_codepage_table( codepage );
2107 return table && wine_is_dbcs_leadbyte( table, testchar );
2108}
2109
2110
2111/***********************************************************************
2112 * IsDBCSLeadByte (KERNEL32.@)
2113 * IsDBCSLeadByte (KERNEL.207)
2114 *
2115 * Determine if a character is a lead byte.
2116 *
2117 * PARAMS
2118 * testchar [I] Character to test
2119 *
2120 * RETURNS
2121 * TRUE, if testchar is a lead byte in the ANSI code page,
2122 * FALSE otherwise.
2123 */
2125{
2126 if (!ansi_cptable) return FALSE;
2127 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2128}
2129
2130
2131/***********************************************************************
2132 * GetCPInfo (KERNEL32.@)
2133 *
2134 * Get information about a code page.
2135 *
2136 * PARAMS
2137 * codepage [I] Code page number
2138 * cpinfo [O] Destination for code page information
2139 *
2140 * RETURNS
2141 * Success: TRUE. cpinfo is updated with the information about codepage.
2142 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2143 */
2145{
2146 const union cptable *table;
2147
2148 if (!cpinfo)
2149 {
2151 return FALSE;
2152 }
2153
2154 if (!(table = get_codepage_table( codepage )))
2155 {
2156 switch(codepage)
2157 {
2158 case CP_UTF7:
2159 case CP_UTF8:
2160 cpinfo->DefaultChar[0] = 0x3f;
2161 cpinfo->DefaultChar[1] = 0;
2162 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2163 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2164 return TRUE;
2165 }
2166
2168 return FALSE;
2169 }
2170 if (table->info.def_char & 0xff00)
2171 {
2172 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2173 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2174 }
2175 else
2176 {
2177 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2178 cpinfo->DefaultChar[1] = 0;
2179 }
2180 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2181 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2182 else
2183 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2184
2185 return TRUE;
2186}
2187
2188/***********************************************************************
2189 * GetCPInfoExA (KERNEL32.@)
2190 *
2191 * Get extended information about a code page.
2192 *
2193 * PARAMS
2194 * codepage [I] Code page number
2195 * dwFlags [I] Reserved, must to 0.
2196 * cpinfo [O] Destination for code page information
2197 *
2198 * RETURNS
2199 * Success: TRUE. cpinfo is updated with the information about codepage.
2200 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2201 */
2203{
2204 CPINFOEXW cpinfoW;
2205
2206 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2207 return FALSE;
2208
2209 /* the layout is the same except for CodePageName */
2210 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2211 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2212 return TRUE;
2213}
2214
2215/***********************************************************************
2216 * GetCPInfoExW (KERNEL32.@)
2217 *
2218 * Unicode version of GetCPInfoExA.
2219 */
2221{
2222 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2223 return FALSE;
2224
2225 switch(codepage)
2226 {
2227 case CP_UTF7:
2228 {
2229 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2230
2231 cpinfo->CodePage = CP_UTF7;
2232 cpinfo->UnicodeDefaultChar = 0x3f;
2233 strcpyW(cpinfo->CodePageName, utf7);
2234 break;
2235 }
2236
2237 case CP_UTF8:
2238 {
2239 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2240
2241 cpinfo->CodePage = CP_UTF8;
2242 cpinfo->UnicodeDefaultChar = 0x3f;
2243 strcpyW(cpinfo->CodePageName, utf8);
2244 break;
2245 }
2246
2247 default:
2248 {
2249 const union cptable *table = get_codepage_table( codepage );
2250
2251 cpinfo->CodePage = table->info.codepage;
2252 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2253 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2254 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2255 break;
2256 }
2257 }
2258 return TRUE;
2259}
2260
2261/***********************************************************************
2262 * EnumSystemCodePagesA (KERNEL32.@)
2263 *
2264 * Call a user defined function for every code page installed on the system.
2265 *
2266 * PARAMS
2267 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2268 * flags [I] Reserved, set to 0.
2269 *
2270 * RETURNS
2271 * TRUE, If all code pages have been enumerated, or
2272 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2273 */
2275{
2276 const union cptable *table;
2277 char buffer[10];
2278 int index = 0;
2279
2280 for (;;)
2281 {
2282 if (!(table = wine_cp_enum_table( index++ ))) break;
2283 sprintf( buffer, "%d", table->info.codepage );
2284 if (!lpfnCodePageEnum( buffer )) break;
2285 }
2286 return TRUE;
2287}
2288
2289
2290/***********************************************************************
2291 * EnumSystemCodePagesW (KERNEL32.@)
2292 *
2293 * See EnumSystemCodePagesA.
2294 */
2296{
2297 const union cptable *table;
2298 WCHAR buffer[10], *p;
2299 int page, index = 0;
2300
2301 for (;;)
2302 {
2303 if (!(table = wine_cp_enum_table( index++ ))) break;
2304 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2305 *--p = 0;
2306 page = table->info.codepage;
2307 do
2308 {
2309 *--p = '0' + (page % 10);
2310 page /= 10;
2311 } while( page );
2312 if (!lpfnCodePageEnum( p )) break;
2313 }
2314 return TRUE;
2315}
2316
2317
2318/***********************************************************************
2319 * utf7_write_w
2320 *
2321 * Helper for utf7_mbstowcs
2322 *
2323 * RETURNS
2324 * TRUE on success, FALSE on error
2325 */
2326static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2327{
2328 if (dstlen > 0)
2329 {
2330 if (*index >= dstlen)
2331 return FALSE;
2332
2333 dst[*index] = character;
2334 }
2335
2336 (*index)++;
2337
2338 return TRUE;
2339}
2340
2341/***********************************************************************
2342 * utf7_mbstowcs
2343 *
2344 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2345 *
2346 * RETURNS
2347 * On success, the number of characters written
2348 * On dst buffer overflow, -1
2349 */
2350static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2351{
2352 static const signed char base64_decoding_table[] =
2353 {
2354 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2355 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2356 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2357 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2358 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2359 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2360 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2361 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2362 };
2363
2364 const char *source_end = src + srclen;
2365 int dest_index = 0;
2366
2367 DWORD byte_pair = 0;
2368 short offset = 0;
2369
2370 while (src < source_end)
2371 {
2372 if (*src == '+')
2373 {
2374 src++;
2375 if (src >= source_end)
2376 break;
2377
2378 if (*src == '-')
2379 {
2380 /* just a plus sign escaped as +- */
2381 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2382 return -1;
2383 src++;
2384 continue;
2385 }
2386
2387 do
2388 {
2389 signed char sextet = *src;
2390 if (sextet == '-')
2391 {
2392 /* skip over the dash and end base64 decoding
2393 * the current, unfinished byte pair is discarded */
2394 src++;
2395 offset = 0;
2396 break;
2397 }
2398 if (sextet < 0)
2399 {
2400 /* the next character of src is < 0 and therefore not part of a base64 sequence
2401 * the current, unfinished byte pair is NOT discarded in this case
2402 * this is probably a bug in Windows */
2403 break;
2404 }
2405
2406 sextet = base64_decoding_table[sextet];
2407 if (sextet == -1)
2408 {
2409 /* -1 means that the next character of src is not part of a base64 sequence
2410 * in other words, all sextets in this base64 sequence have been processed
2411 * the current, unfinished byte pair is discarded */
2412 offset = 0;
2413 break;
2414 }
2415
2416 byte_pair = (byte_pair << 6) | sextet;
2417 offset += 6;
2418
2419 if (offset >= 16)
2420 {
2421 /* this byte pair is done */
2422 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2423 return -1;
2424 offset -= 16;
2425 }
2426
2427 src++;
2428 }
2429 while (src < source_end);
2430 }
2431 else
2432 {
2433 /* we have to convert to unsigned char in case *src < 0 */
2434 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2435 return -1;
2436 src++;
2437 }
2438 }
2439
2440 return dest_index;
2441}
2442
2443/***********************************************************************
2444 * MultiByteToWideChar (KERNEL32.@)
2445 *
2446 * Convert a multibyte character string into a Unicode string.
2447 *
2448 * PARAMS
2449 * page [I] Codepage character set to convert from
2450 * flags [I] Character mapping flags
2451 * src [I] Source string buffer
2452 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2453 * dst [O] Destination buffer
2454 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2455 *
2456 * RETURNS
2457 * Success: If dstlen > 0, the number of characters written to dst.
2458 * If dstlen == 0, the number of characters needed to perform the
2459 * conversion. In both cases the count includes the terminating NUL.
2460 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2461 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2462 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2463 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2464 * possible for src.
2465 */
2467 LPWSTR dst, INT dstlen )
2468{
2469 const union cptable *table;
2470 int ret;
2471
2472 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2473 {
2475 return 0;
2476 }
2477
2478 if (srclen < 0) srclen = strlen(src) + 1;
2479
2480 switch(page)
2481 {
2482 case CP_SYMBOL:
2483 if (flags)
2484 {
2486 return 0;
2487 }
2489 break;
2490 case CP_UTF7:
2491 if (flags)
2492 {
2494 return 0;
2495 }
2497 break;
2498 case CP_UNIXCP:
2499 if (unix_cptable)
2500 {
2502 break;
2503 }
2504#ifdef __APPLE__
2505 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2506#endif
2507 /* fall through */
2508 case CP_UTF8:
2509 if (flags & ~MB_FLAGSMASK)
2510 {
2512 return 0;
2513 }
2515 break;
2516 default:
2517 if (!(table = get_codepage_table( page )))
2518 {
2520 return 0;
2521 }
2522 if (flags & ~MB_FLAGSMASK)
2523 {
2525 return 0;
2526 }
2528 break;
2529 }
2530
2531 if (ret < 0)
2532 {
2533 switch(ret)
2534 {
2535 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2536 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2537 }
2538 ret = 0;
2539 }
2540 TRACE("cp %d %s -> %s, ret = %d\n",
2542 return ret;
2543}
2544
2545
2546/***********************************************************************
2547 * utf7_can_directly_encode
2548 *
2549 * Helper for utf7_wcstombs
2550 */
2551static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2552{
2553 static const BOOL directly_encodable_table[] =
2554 {
2555 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2556 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2557 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2558 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2559 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2560 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2561 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2562 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2563 };
2564
2565 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2566}
2567
2568/***********************************************************************
2569 * utf7_write_c
2570 *
2571 * Helper for utf7_wcstombs
2572 *
2573 * RETURNS
2574 * TRUE on success, FALSE on error
2575 */
2576static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2577{
2578 if (dstlen > 0)
2579 {
2580 if (*index >= dstlen)
2581 return FALSE;
2582
2583 dst[*index] = character;
2584 }
2585
2586 (*index)++;
2587
2588 return TRUE;
2589}
2590
2591/***********************************************************************
2592 * utf7_wcstombs
2593 *
2594 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2595 *
2596 * RETURNS
2597 * On success, the number of characters written
2598 * On dst buffer overflow, -1
2599 */
2600static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2601{
2602 static const char base64_encoding_table[] =
2603 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2604
2605 const WCHAR *source_end = src + srclen;
2606 int dest_index = 0;
2607
2608 while (src < source_end)
2609 {
2610 if (*src == '+')
2611 {
2612 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2613 return -1;
2614 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2615 return -1;
2616 src++;
2617 }
2618 else if (utf7_can_directly_encode(*src))
2619 {
2620 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2621 return -1;
2622 src++;
2623 }
2624 else
2625 {
2626 unsigned int offset = 0;
2627 DWORD byte_pair = 0;
2628
2629 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2630 return -1;
2631
2632 while (src < source_end && !utf7_can_directly_encode(*src))
2633 {
2634 byte_pair = (byte_pair << 16) | *src;
2635 offset += 16;
2636 while (offset >= 6)
2637 {
2638 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2639 return -1;
2640 offset -= 6;
2641 }
2642 src++;
2643 }
2644
2645 if (offset)
2646 {
2647 /* Windows won't create a padded base64 character if there's no room for the - sign
2648 * as well ; this is probably a bug in Windows */
2649 if (dstlen > 0 && dest_index + 1 >= dstlen)
2650 return -1;
2651
2652 byte_pair <<= (6 - offset);
2653 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2654 return -1;
2655 }
2656
2657 /* Windows always explicitly terminates the base64 sequence
2658 even though RFC 2152 (page 3, rule 2) does not require this */
2659 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2660 return -1;
2661 }
2662 }
2663
2664 return dest_index;
2665}
2666
2667/***********************************************************************
2668 * WideCharToMultiByte (KERNEL32.@)
2669 *
2670 * Convert a Unicode character string into a multibyte string.
2671 *
2672 * PARAMS
2673 * page [I] Code page character set to convert to
2674 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2675 * src [I] Source string buffer
2676 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2677 * dst [O] Destination buffer
2678 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2679 * defchar [I] Default character to use for conversion if no exact
2680 * conversion can be made
2681 * used [O] Set if default character was used in the conversion
2682 *
2683 * RETURNS
2684 * Success: If dstlen > 0, the number of characters written to dst.
2685 * If dstlen == 0, number of characters needed to perform the
2686 * conversion. In both cases the count includes the terminating NUL.
2687 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2688 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2689 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2690 * parameter was given.
2691 */
2693 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2694{
2695 const union cptable *table;
2696 int ret, used_tmp;
2697
2698 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2699 {
2701 return 0;
2702 }
2703
2704 if (srclen < 0) srclen = strlenW(src) + 1;
2705
2706 switch(page)
2707 {
2708 case CP_SYMBOL:
2709 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2710 if (flags)
2711 {
2713 return 0;
2714 }
2715 if (defchar || used)
2716 {
2718 return 0;
2719 }
2721 break;
2722 case CP_UTF7:
2723 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2724 if (defchar || used)
2725 {
2727 return 0;
2728 }
2729 if (flags)
2730 {
2732 return 0;
2733 }
2735 break;
2736 case CP_UNIXCP:
2737 if (unix_cptable)
2738 {
2740 defchar, used ? &used_tmp : NULL );
2741 break;
2742 }
2743 /* fall through */
2744 case CP_UTF8:
2745 if (defchar || used)
2746 {
2748 return 0;
2749 }
2750 if (flags & ~WC_FLAGSMASK)
2751 {
2753 return 0;
2754 }
2756 break;
2757 default:
2758 if (!(table = get_codepage_table( page )))
2759 {
2761 return 0;
2762 }
2763 if (flags & ~WC_FLAGSMASK)
2764 {
2766 return 0;
2767 }
2769 defchar, used ? &used_tmp : NULL );
2770 if (used) *used = used_tmp;
2771 break;
2772 }
2773
2774 if (ret < 0)
2775 {
2776 switch(ret)
2777 {
2778 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2779 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2780 }
2781 ret = 0;
2782 }
2783 TRACE("cp %d %s -> %s, ret = %d\n",
2785 return ret;
2786}
2787#endif // !__REACTOS__
2788
2789
2790/***********************************************************************
2791 * GetThreadLocale (KERNEL32.@)
2792 *
2793 * Get the current threads locale.
2794 *
2795 * PARAMS
2796 * None.
2797 *
2798 * RETURNS
2799 * The LCID currently associated with the calling thread.
2800 */
2802{
2803 LCID ret = NtCurrentTeb()->CurrentLocale;
2804 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2805 return ret;
2806}
2807
2808/**********************************************************************
2809 * SetThreadLocale (KERNEL32.@)
2810 *
2811 * Set the current threads locale.
2812 *
2813 * PARAMS
2814 * lcid [I] LCID of the locale to set
2815 *
2816 * RETURNS
2817 * Success: TRUE. The threads locale is set to lcid.
2818 * Failure: FALSE. Use GetLastError() to determine the cause.
2819 */
2821{
2822 TRACE("(0x%04X)\n", lcid);
2823
2824 lcid = ConvertDefaultLocale(lcid);
2825
2826 if (lcid != GetThreadLocale())
2827 {
2828 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2829 {
2831 return FALSE;
2832 }
2833
2834 NtCurrentTeb()->CurrentLocale = lcid;
2835 }
2836 return TRUE;
2837}
2838
2839#ifndef __REACTOS__
2840/**********************************************************************
2841 * SetThreadUILanguage (KERNEL32.@)
2842 *
2843 * Set the current threads UI language.
2844 *
2845 * PARAMS
2846 * langid [I] LANGID of the language to set, or 0 to use
2847 * the available language which is best supported
2848 * for console applications
2849 *
2850 * RETURNS
2851 * Success: The return value is the same as the input value.
2852 * Failure: The return value differs from the input value.
2853 * Use GetLastError() to determine the cause.
2854 */
2856{
2857 TRACE("(0x%04x) stub - returning success\n", langid);
2858 return langid;
2859}
2860#endif // !__REACTOS__
2861
2862/******************************************************************************
2863 * ConvertDefaultLocale (KERNEL32.@)
2864 *
2865 * Convert a default locale identifier into a real identifier.
2866 *
2867 * PARAMS
2868 * lcid [I] LCID identifier of the locale to convert
2869 *
2870 * RETURNS
2871 * lcid unchanged, if not a default locale or its sublanguage is
2872 * not SUBLANG_NEUTRAL.
2873 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2874 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2875 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2876 */
2878{
2879 LANGID langid;
2880
2881 switch (lcid)
2882 {
2883 case LOCALE_INVARIANT:
2884 /* keep as-is */
2885 break;
2887 lcid = GetSystemDefaultLCID();
2888 break;
2890 case LOCALE_NEUTRAL:
2891 lcid = GetUserDefaultLCID();
2892 break;
2893 default:
2894 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2895 langid = LANGIDFROMLCID(lcid);
2897 {
2899 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2900 }
2901 }
2902 return lcid;
2903}
2904
2905
2906/******************************************************************************
2907 * IsValidLocale (KERNEL32.@)
2908 *
2909 * Determine if a locale is valid.
2910 *
2911 * PARAMS
2912 * lcid [I] LCID of the locale to check
2913 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2914 *
2915 * RETURNS
2916 * TRUE, if lcid is valid,
2917 * FALSE, otherwise.
2918 *
2919 * NOTES
2920 * Wine does not currently make the distinction between supported and installed. All
2921 * languages supported are installed by default.
2922 */
2924{
2925 /* check if language is registered in the kernel32 resources */
2928}
2929
2930#ifndef __REACTOS__
2931/******************************************************************************
2932 * IsValidLocaleName (KERNEL32.@)
2933 */
2935{
2936 struct locale_name locale_name;
2937
2938 if (!locale)
2939 return FALSE;
2940
2941 /* string parsing */
2942 parse_locale_name( locale, &locale_name );
2943
2944 TRACE( "found lcid %x for %s, matches %d\n",
2946
2947 return locale_name.matches > 0;
2948}
2949#endif // !__REACTOS__
2950
2953{
2954 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2955 char buf[20];
2956
2957 sprintf(buf, "%08x", (UINT)LangID);
2958 return lpfnLocaleEnum( buf );
2959}
2960
2963{
2964 static const WCHAR formatW[] = {'%','0','8','x',0};
2965 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2966 WCHAR buf[20];
2967 sprintfW( buf, formatW, (UINT)LangID );
2968 return lpfnLocaleEnum( buf );
2969}
2970
2971/******************************************************************************
2972 * EnumSystemLocalesA (KERNEL32.@)
2973 *
2974 * Call a users function for each locale available on the system.
2975 *
2976 * PARAMS
2977 * lpfnLocaleEnum [I] Callback function to call for each locale
2978 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2979 *
2980 * RETURNS
2981 * Success: TRUE.
2982 * Failure: FALSE. Use GetLastError() to determine the cause.
2983 */
2985{
2986 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2989 (LONG_PTR)lpfnLocaleEnum);
2990 return TRUE;
2991}
2992
2993
2994/******************************************************************************
2995 * EnumSystemLocalesW (KERNEL32.@)
2996 *
2997 * See EnumSystemLocalesA.
2998 */
3000{
3001 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
3004 (LONG_PTR)lpfnLocaleEnum);
3005 return TRUE;
3006}
3007
3008
3010{
3014};
3015
3018{
3020 WCHAR buffer[256];
3021 DWORD neutral;
3022 unsigned int flags;
3023
3025 buffer, sizeof(buffer) / sizeof(WCHAR) );
3027 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
3028 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
3029 neutral = 0;
3032 if (data->flags && !(data->flags & flags)) return TRUE;
3033 return data->proc( buffer, flags, data->lparam );
3034}
3035
3036/******************************************************************************
3037 * EnumSystemLocalesEx (KERNEL32.@)
3038 */
3040{
3042
3043 if (reserved)
3044 {
3046 return FALSE;
3047 }
3048 data.proc = proc;
3049 data.flags = flags;
3050 data.lparam = lparam;
3054 return TRUE;
3055}
3056
3057
3058/***********************************************************************
3059 * VerLanguageNameA (KERNEL32.@)
3060 *
3061 * Get the name of a language.
3062 *
3063 * PARAMS
3064 * wLang [I] LANGID of the language
3065 * szLang [O] Destination for the language name
3066 *
3067 * RETURNS
3068 * Success: The size of the language name. If szLang is non-NULL, it is filled
3069 * with the name.
3070 * Failure: 0. Use GetLastError() to determine the cause.
3071 *
3072 */
3074{
3075 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3076}
3077
3078
3079/***********************************************************************
3080 * VerLanguageNameW (KERNEL32.@)
3081 *
3082 * See VerLanguageNameA.
3083 */
3085{
3086 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3087}
3088
3089
3090/******************************************************************************
3091 * GetStringTypeW (KERNEL32.@)
3092 *
3093 * See GetStringTypeA.
3094 */
3096{
3097 static const unsigned char type2_map[16] =
3098 {
3099 C2_NOTAPPLICABLE, /* unassigned */
3100 C2_LEFTTORIGHT, /* L */
3101 C2_RIGHTTOLEFT, /* R */
3102 C2_EUROPENUMBER, /* EN */
3103 C2_EUROPESEPARATOR, /* ES */
3104 C2_EUROPETERMINATOR, /* ET */
3105 C2_ARABICNUMBER, /* AN */
3106 C2_COMMONSEPARATOR, /* CS */
3107 C2_BLOCKSEPARATOR, /* B */
3108 C2_SEGMENTSEPARATOR, /* S */
3109 C2_WHITESPACE, /* WS */
3110 C2_OTHERNEUTRAL, /* ON */
3111 C2_RIGHTTOLEFT, /* AL */
3112 C2_NOTAPPLICABLE, /* NSM */
3113 C2_NOTAPPLICABLE, /* BN */
3114 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
3115 };
3116
3117 if (!src)
3118 {
3120 return FALSE;
3121 }
3122
3123 if (count == -1) count = strlenW(src) + 1;
3124 switch(type)
3125 {
3126 case CT_CTYPE1:
3127 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
3128 break;
3129 case CT_CTYPE2:
3130 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
3131 break;
3132 case CT_CTYPE3:
3133 {
3134 WARN("CT_CTYPE3: semi-stub.\n");
3135 while (count--)
3136 {
3137 int c = *src;
3138 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3139
3140 type1 = get_char_typeW( *src++ ) & 0xfff;
3141 /* try to construct type3 from type1 */
3142 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3143 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3144 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3145 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3146 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3147 if (c == 0x0640) type3 |= C3_KASHIDA;
3148 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3149
3150 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3151 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3152
3153 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3154 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3155 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3156 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3157 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3158 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3159 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3160 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3161
3162 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3163 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3164 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3165 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3166 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3167 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3168 *chartype++ = type3;
3169 }
3170 break;
3171 }
3172 default:
3174 return FALSE;
3175 }
3176 return TRUE;
3177}
3178
3179
3180/******************************************************************************
3181 * GetStringTypeExW (KERNEL32.@)
3182 *
3183 * See GetStringTypeExA.
3184 */
3186{
3187 /* locale is ignored for Unicode */
3188 return GetStringTypeW( type, src, count, chartype );
3189}
3190
3191
3192/******************************************************************************
3193 * GetStringTypeA (KERNEL32.@)
3194 *
3195 * Get characteristics of the characters making up a string.
3196 *
3197 * PARAMS
3198 * locale [I] Locale Id for the string
3199 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3200 * src [I] String to analyse
3201 * count [I] Length of src in chars, or -1 if src is NUL terminated
3202 * chartype [O] Destination for the calculated characteristics
3203 *
3204 * RETURNS
3205 * Success: TRUE. chartype is filled with the requested characteristics of each char
3206 * in src.
3207 * Failure: FALSE. Use GetLastError() to determine the cause.
3208 */
3210{
3211 UINT cp;
3212 INT countW;
3213 LPWSTR srcW;
3214 BOOL ret = FALSE;
3215
3216 if(count == -1) count = strlen(src) + 1;
3217
3218 if (!(cp = get_lcid_codepage( locale )))
3219 {
3220 FIXME("For locale %04x using current ANSI code page\n", locale);
3221 cp = GetACP();
3222 }
3223
3224 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3225 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3226 {
3227 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3228 /*
3229 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3230 * string, with multibyte characters there maybe be more bytes in count
3231 * than character space in the buffer!
3232 */
3233 ret = GetStringTypeW(type, srcW, countW, chartype);
3234 HeapFree(GetProcessHeap(), 0, srcW);
3235 }
3236 return ret;
3237}
3238
3239/******************************************************************************
3240 * GetStringTypeExA (KERNEL32.@)
3241 *
3242 * Get characteristics of the characters making up a string.
3243 *
3244 * PARAMS
3245 * locale [I] Locale Id for the string
3246 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3247 * src [I] String to analyse
3248 * count [I] Length of src in chars, or -1 if src is NUL terminated
3249 * chartype [O] Destination for the calculated characteristics
3250 *
3251 * RETURNS
3252 * Success: TRUE. chartype is filled with the requested characteristics of each char
3253 * in src.
3254 * Failure: FALSE. Use GetLastError() to determine the cause.
3255 */
3257{
3258 return GetStringTypeA(locale, type, src, count, chartype);
3259}
3260
3261#ifdef __REACTOS__
3262static inline void map_byterev(const WCHAR *src, int len, WCHAR *dst)
3263{
3264 while (len--)
3265 *dst++ = RtlUshortByteSwap(*src++);
3266}
3267
3268static int map_to_hiragana(const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3269{
3270 int pos;
3271 for (pos = 0; srclen; src++, srclen--, pos++)
3272 {
3273 /*
3274 * U+30A1 ... U+30F3: Katakana
3275 * U+30F4: Katakana Letter VU
3276 * U+30F5: Katakana Letter Small KA
3277 * U+30FD: Katakana Iteration Mark
3278 * U+30FE: Katakana Voiced Iteration Mark
3279 */
3280 WCHAR wch = *src;
3281 if ((0x30A1 <= wch && wch <= 0x30F3) ||
3282 wch == 0x30F4 || wch == 0x30F5 || wch == 0x30FD || wch == 0x30FE)
3283 {
3284 wch -= 0x60; /* Katakana to Hiragana */
3285 }
3286 if (pos < dstlen)
3287 dst[pos] = wch;
3288 }
3289 return pos;
3290}
3291
3292static int map_to_katakana(const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3293{
3294 int pos;
3295 for (pos = 0; srclen; src++, srclen--, pos++)
3296 {
3297 /*
3298 * U+3041 ... U+3093: Hiragana
3299 * U+3094: Hiragana Letter VU
3300 * U+3095: Hiragana Letter Small KA
3301 * U+309D: Hiragana Iteration Mark
3302 * U+309E: Hiragana Voiced Iteration Mark
3303 */
3304 WCHAR wch = *src;
3305 if ((0x3041 <= wch && wch <= 0x3093) ||
3306 wch == 3094 || wch == 0x3095 || wch == 0x309D || wch == 0x309E)
3307 {
3308 wch += 0x60; /* Hiragana to Katakana */
3309 }
3310 if (pos < dstlen)
3311 dst[pos] = wch;
3312 }
3313 return pos;
3314}
3315
3316/* The table that contains fullwidth characters and halfwidth characters */
3317typedef WCHAR FULL2HALF_ENTRY[3];
3318static const FULL2HALF_ENTRY full2half_table[] =
3319{
3320#define DEFINE_FULL2HALF(full, half1, half2) { full, half1, half2 },
3321#include "full2half.h"
3322#undef DEFINE_FULL2HALF
3323};
3324#define GET_FULL(table, index) ((table)[index][0])
3325#define GET_HALF1(table, index) ((table)[index][1])
3326#define GET_HALF2(table, index) ((table)[index][2])
3327
3328/* The table that contains dakuten entries */
3329typedef WCHAR DAKUTEN_ENTRY[3];
3330static const DAKUTEN_ENTRY dakuten_table[] =
3331{
3332#define DEFINE_DAKUTEN(voiced, single1, single2, half1, half2) { voiced, single1, single2 },
3333#include "dakuten.h"
3334#undef DEFINE_DAKUTEN
3335};
3336#define GET_VOICED(table, index) ((table)[index][0])
3337#define GET_SINGLE1(table, index) ((table)[index][1])
3338#define GET_SINGLE2(table, index) ((table)[index][2])
3339
3340static int map_to_halfwidth(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3341{
3342 int pos, i;
3343 const int count1 = (int)ARRAY_SIZE(full2half_table);
3344 const FULL2HALF_ENTRY *table1 = full2half_table;
3345
3346 for (pos = 0; srclen; src++, srclen--, pos++)
3347 {
3348 WCHAR ch = *src;
3349
3350 if (flags & LCMAP_KATAKANA)
3351 map_to_katakana(&ch, 1, &ch, 1);
3352 else if (flags & LCMAP_HIRAGANA)
3353 map_to_hiragana(&ch, 1, &ch, 1);
3354
3355 if (ch < 0x3000) /* Quick judgment */
3356 {
3357 if (pos < dstlen)
3358 dst[pos] = ch;
3359 continue;
3360 }
3361
3362 if (0xFF01 <= ch && ch <= 0xFF5E) /* U+FF01 ... U+FF5E */
3363 {
3364 if (pos < dstlen)
3365 dst[pos] = ch - 0xFEE0; /* Fullwidth ASCII to halfwidth ASCII */
3366 continue;
3367 }
3368
3369 /* Search in table1 (full/half) */
3370 for (i = count1 - 1; i >= 0; --i) /* In reverse order */
3371 {
3372 if (GET_FULL(table1, i) != ch)
3373 continue;
3374
3375 if (GET_HALF2(table1, i) == 0)
3376 {
3377 if (pos < dstlen)
3378 dst[pos] = GET_HALF1(table1, i);
3379 }
3380 else if (!dstlen)
3381 {
3382 pos++;
3383 }
3384 else if (pos + 1 < dstlen)
3385 {
3386 dst[pos++] = GET_HALF1(table1, i);
3387 dst[pos ] = GET_HALF2(table1, i);
3388 }
3389 else
3390 {
3391 dst[pos] = ch;
3392 }
3393 break;
3394 }
3395
3396 if (i >= 0)
3397 continue;
3398
3399 if (pos < dstlen)
3400 dst[pos] = ch;
3401 }
3402
3403 return pos;
3404}
3405
3406static int map_to_fullwidth(const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3407{
3408 int pos, i;
3409 const FULL2HALF_ENTRY *table1 = full2half_table;
3410 const DAKUTEN_ENTRY *table2 = dakuten_table;
3411 const int count1 = (int)ARRAY_SIZE(full2half_table);
3412 const int count2 = (int)ARRAY_SIZE(dakuten_table);
3413
3414 for (pos = 0; srclen; src++, srclen--, pos++)
3415 {
3416 WCHAR ch = *src;
3417
3418 if (ch == 0x20) /* U+0020: Space */
3419 {
3420 if (pos < dstlen)
3421 dst[pos] = 0x3000; /* U+3000: Ideographic Space */
3422 continue;
3423 }
3424
3425 if (0x21 <= ch && ch <= 0x7E) /* Mappable halfwidth ASCII */
3426 {
3427 if (pos < dstlen)
3428 dst[pos] = ch + 0xFEE0; /* U+FF01 ... U+FF5E */
3429 continue;
3430 }
3431
3432 if (ch < 0xFF00) /* Quick judgment */
3433 {
3434 if (pos < dstlen)
3435 dst[pos] = ch;
3436 continue;
3437 }
3438
3439 /* Search in table1 (full/half) */
3440 for (i = count1 - 1; i >= 0; --i) /* In reverse order */
3441 {
3442 if (GET_HALF1(table1, i) != ch)
3443 continue; /* Mismatched */
3444
3445 if (GET_HALF2(table1, i) == 0)
3446 {
3447 if (pos < dstlen)
3448 dst[pos] = GET_FULL(table1, i);
3449 break;
3450 }
3451
3452 if (srclen <= 1 || GET_HALF2(table1, i) != src[1])
3453 continue; /* Mismatched */
3454
3455 --srclen;
3456 ++src;
3457
3458 if (pos < dstlen)
3459 dst[pos] = GET_FULL(table1, i);
3460 break;
3461 }
3462
3463 if (i >= 0)
3464 continue;
3465
3466 /* Search in table2 (dakuten) */
3467 for (i = count2 - 1; i >= 0; --i) /* In reverse order */
3468 {
3469 if (GET_SINGLE1(table2, i) != ch)
3470 continue; /* Mismatched */
3471
3472 if (srclen <= 1 || GET_SINGLE2(table2, i) != src[1])
3473 continue; /* Mismatched */
3474
3475 --srclen;
3476 ++src;
3477
3478 if (pos < dstlen)
3479 dst[pos] = GET_VOICED(table2, i);
3480 break;
3481 }
3482
3483 if (i >= 0)
3484 continue;
3485
3486 if (pos < dstlen)
3487 dst[pos] = ch;
3488 }
3489
3490 return pos;
3491}
3492
3493static int map_to_lowercase(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3494{
3495 int pos;
3496 for (pos = 0; srclen; src++, srclen--)
3497 {
3498 WCHAR wch = *src;
3500 continue;
3501 if (pos < dstlen)
3502 dst[pos] = tolowerW(wch);
3503 pos++;
3504 }
3505 return pos;
3506}
3507
3508static int map_to_uppercase(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3509{
3510 int pos;
3511 for (pos = 0; srclen; src++, srclen--)
3512 {
3513 WCHAR wch = *src;
3515 continue;
3516 if (pos < dstlen)
3517 dst[pos] = toupperW(wch);
3518 pos++;
3519 }
3520 return pos;
3521}
3522
3523typedef struct tagWCHAR_PAIR
3524{
3525 WCHAR from, to;
3526} WCHAR_PAIR, *PWCHAR_PAIR;
3527
3528/* The table to convert Simplified Chinese to Traditional Chinese */
3529static const WCHAR_PAIR s_sim2tra[] =
3530{
3531#define DEFINE_SIM2TRA(from, to) { from, to },
3532#include "sim2tra.h"
3533#undef DEFINE_SIM2TRA
3534};
3535
3536/* The table to convert Traditional Chinese to Simplified Chinese */
3537static const WCHAR_PAIR s_tra2sim[] =
3538{
3539#define DEFINE_TRA2SIM(from, to) { from, to },
3540#include "tra2sim.h"
3541#undef DEFINE_TRA2SIM
3542};
3543
3544/* The comparison function to do bsearch */
3545static int compare_wchar_pair(const void *x, const void *y)
3546{
3547 const WCHAR_PAIR *a = x;
3548 const WCHAR_PAIR *b = y;
3549 if (a->from < b->from)
3550 return -1;
3551 if (a->from > b->from)
3552 return +1;
3553 return 0;
3554}
3555
3556static WCHAR find_wchar_pair(const WCHAR_PAIR *pairs, size_t count, WCHAR ch)
3557{
3558 PWCHAR_PAIR found = bsearch(&ch, pairs, count, sizeof(WCHAR_PAIR), compare_wchar_pair);
3559 if (found)
3560 return found->to;
3561 return ch;
3562}
3563
3564static int map_to_simplified_chinese(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3565{
3566 int pos;
3567 for (pos = 0; srclen; src++, srclen--)
3568 {
3569 WCHAR wch = *src;
3570 if (pos < dstlen)
3571 dst[pos] = find_wchar_pair(s_tra2sim, ARRAY_SIZE(s_tra2sim), wch);
3572 pos++;
3573 }
3574 return pos;
3575}
3576
3577static int map_to_traditional_chinese(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3578{
3579 int pos;
3580 for (pos = 0; srclen; src++, srclen--)
3581 {
3582 WCHAR wch = *src;
3583 if (pos < dstlen)
3584 dst[pos] = find_wchar_pair(s_sim2tra, ARRAY_SIZE(s_sim2tra), wch);
3585 pos++;
3586 }
3587 return pos;
3588}
3589
3590static int map_remove_ignored(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3591{
3592 int pos;
3593 WORD wC1, wC2, wC3;
3594 for (pos = 0; srclen; src++, srclen--)
3595 {
3596 WCHAR wch = *src;
3597 GetStringTypeW(CT_CTYPE1, &wch, 1, &wC1);
3598 GetStringTypeW(CT_CTYPE2, &wch, 1, &wC2);
3599 GetStringTypeW(CT_CTYPE3, &wch, 1, &wC3);
3601 {
3602 if ((wC1 & C1_PUNCT) || (wC3 & C3_SYMBOL))
3603 continue;
3604 }
3606 {
3607 if ((wC2 & C2_OTHERNEUTRAL) && (wC3 & (C3_NONSPACING | C3_DIACRITIC)))
3608 continue;
3609 }
3610 if (pos < dstlen)
3611 dst[pos] = wch;
3612 pos++;
3613 }
3614 return pos;
3615}
3616
3617static int lcmap_string(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3618{
3619 int ret = 0;
3620
3622 {
3624 return 0;
3625 }
3626
3628 {
3629 case LCMAP_HIRAGANA:
3630 ret = map_to_hiragana(src, srclen, dst, dstlen);
3631 break;
3632 case LCMAP_KATAKANA:
3633 ret = map_to_katakana(src, srclen, dst, dstlen);
3634 break;
3635 case LCMAP_HALFWIDTH:
3636 ret = map_to_halfwidth(flags, src, srclen, dst, dstlen);
3637 break;
3639 ret = map_to_halfwidth(flags, src, srclen, dst, dstlen);
3640 break;
3642 ret = map_to_halfwidth(flags, src, srclen, dst, dstlen);
3643 break;
3644 case LCMAP_FULLWIDTH:
3645 ret = map_to_fullwidth(src, srclen, dst, dstlen);
3646 break;
3648 ret = map_to_fullwidth(src, srclen, dst, dstlen);
3649 if (dstlen && ret)
3650 map_to_hiragana(dst, ret, dst, dstlen);
3651 break;
3653 ret = map_to_fullwidth(src, srclen, dst, dstlen);
3654 if (dstlen && ret)
3655 map_to_katakana(dst, ret, dst, dstlen);
3656 break;
3658 ret = map_to_simplified_chinese(flags, src, srclen, dst, dstlen);
3659 break;
3661 ret = map_to_traditional_chinese(flags, src, srclen, dst, dstlen);
3662 break;
3664 case NORM_IGNORESYMBOLS:
3667 {
3669 return 0;
3670 }
3671 ret = map_remove_ignored(flags, src, srclen, dst, dstlen);
3672 break;
3673 case 0:
3674 if (flags & LCMAP_LOWERCASE)
3675 {
3676 ret = map_to_lowercase(flags, src, srclen, dst, dstlen);
3677 flags &= ~LCMAP_LOWERCASE;
3678 break;
3679 }
3680 if (flags & LCMAP_UPPERCASE)
3681 {
3682 ret = map_to_uppercase(flags, src, srclen, dst, dstlen);
3683 flags &= ~LCMAP_UPPERCASE;
3684 break;
3685 }
3686 if (flags & LCMAP_BYTEREV)
3687 {
3688 if (dstlen == 0)
3689 {
3690 ret = srclen;
3691 break;
3692 }
3693 ret = min(srclen, dstlen);
3694 RtlCopyMemory(dst, src, ret * sizeof(WCHAR));
3695 break;
3696 }
3697 /* fall through */
3698 default:
3700 return 0;
3701 }
3702
3703 if (dstlen)
3704 {
3705 if (flags & LCMAP_LOWERCASE)
3706 map_to_lowercase(flags, dst, ret, dst, dstlen);
3707 if (flags & LCMAP_UPPERCASE)
3708 map_to_uppercase(flags, dst, ret, dst, dstlen);
3709 if (flags & LCMAP_BYTEREV)
3710 map_byterev(dst, min(ret, dstlen), dst);
3711
3712 if (dstlen < ret)
3713 {
3715 return 0;
3716 }
3717 }
3718
3719 return ret;
3720}
3721#endif // __REACTOS__
3722
3723/*************************************************************************
3724 * LCMapStringEx (KERNEL32.@)
3725 *
3726 * Map characters in a locale sensitive string.
3727 *
3728 * PARAMS
3729 * locale [I] Locale name for the conversion.
3730 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3731 * src [I] String to map
3732 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3733 * dst [O] Destination for mapped string
3734 * dstlen [I] Length of dst in characters
3735 * version [I] reserved, must be NULL
3736 * reserved [I] reserved, must be NULL
3737 * lparam [I] reserved, must be 0
3738 *
3739 * RETURNS
3740 * Success: The length of the mapped string in dst, including the NUL terminator.
3741 * Failure: 0. Use GetLastError() to determine the cause.
3742 */
3745{
3746 if (version) FIXME("unsupported version structure %p\n", version);
3747 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3748 if (handle)
3749 {
3750 static int once;
3751 if (!once++) FIXME("unsupported lparam %Ix\n", handle);
3752 }
3753
3754 if (!src || !srclen || dstlen < 0)
3755 {
3757 return 0;
3758 }
3759
3760 if (srclen < 0) srclen = lstrlenW(src) + 1;
3761
3762 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
3764
3765 flags &= ~LOCALE_USE_CP_ACP;
3766
3767 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3768 {
3770 return 0;
3771 }
3772
3773 if (!dstlen) dst = NULL;
3774
3775 if (flags & LCMAP_SORTKEY)
3776 {
3777 INT ret;
3778
3779 if (srclen < 0)
3780 srclen = strlenW(src);
3781
3782 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3783 if (ret == 0)
3785 else
3786 ret++;
3787 return ret;
3788 }
3789
3790 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3791 if (flags & SORT_STRINGSORT)
3792 {
3794 return 0;
3795 }
3796
3797 return lcmap_string(flags, src, srclen, dst, dstlen);
3798}
3799
3800/*************************************************************************
3801 * LCMapStringW (KERNEL32.@)
3802 *
3803 * See LCMapStringA.
3804 */
3807{
3808 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3810
3811 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3812}
3813
3814/*************************************************************************
3815 * LCMapStringA (KERNEL32.@)
3816 *
3817 * Map characters in a locale sensitive string.
3818 *
3819 * PARAMS
3820 * lcid [I] LCID for the conversion.
3821 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3822 * src [I] String to map
3823 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3824 * dst [O] Destination for mapped string
3825 * dstlen [I] Length of dst in characters
3826 *
3827 * RETURNS
3828 * Success: The length of the mapped string in dst, including the NUL terminator.
3829 * Failure: 0. Use GetLastError() to determine the cause.
3830 */
3833{
3834 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3835 LPWSTR srcW, dstW;
3836 INT ret = 0, srclenW, dstlenW;
3837 UINT locale_cp = CP_ACP;
3838
3839 if (!src || !srclen || dstlen < 0)
3840 {
3842 return 0;
3843 }
3844
3845 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3846
3847 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3848 if (srclenW)
3849 srcW = bufW;
3850 else
3851 {
3852 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3853 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3854 if (!srcW)
3855 {
3857 return 0;
3858 }
3859 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3860 }
3861
3862 if (flags & LCMAP_SORTKEY)
3863 {
3864 if (src == dst)
3865 {
3867 goto map_string_exit;
3868 }
3869 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3870 if (ret == 0)
3872 else
3873 ret++;
3874 goto map_string_exit;
3875 }
3876
3877 if (flags & SORT_STRINGSORT)
3878 {
3880 goto map_string_exit;
3881 }
3882
3883 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3884 if (!dstlenW)
3885 goto map_string_exit;
3886
3887 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3888 if (!dstW)
3889 {
3891 goto map_string_exit;
3892 }
3893
3894 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3895 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3896 HeapFree(GetProcessHeap(), 0, dstW);
3897
3898map_string_exit:
3899 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3900 return ret;
3901}
3902
3903/*************************************************************************
3904 * FoldStringA (KERNEL32.@)
3905 *
3906 * Map characters in a string.
3907 *
3908 * PARAMS
3909 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3910 * src [I] String to map
3911 * srclen [I] Length of src, or -1 if src is NUL terminated
3912 * dst [O] Destination for mapped string
3913 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3914 *
3915 * RETURNS
3916 * Success: The length of the string written to dst, including the terminating NUL. If
3917 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3918 * and dst may be NULL.
3919 * Failure: 0. Use GetLastError() to determine the cause.
3920 */
3923{
3924 INT ret = 0, srclenW = 0;
3925 WCHAR *srcW = NULL, *dstW = NULL;
3926
3927 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3928 {
3930 return 0;
3931 }
3932
3934 src, srclen, NULL, 0);
3935 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3936
3937 if (!srcW)
3938 {
3940 goto FoldStringA_exit;
3941 }
3942
3944 src, srclen, srcW, srclenW);
3945
3946 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3947
3948 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3949 if (ret && dstlen)
3950 {
3951 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3952
3953 if (!dstW)
3954 {
3956 goto FoldStringA_exit;
3957 }
3958
3959 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3960 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL,