kernel32: Add a stub for FlushProcessWriteBuffers.
[wine] / dlls / kernel32 / locale.c
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 "config.h"
25 #include "wine/port.h"
26
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFBundle.h>
37 # include <CoreFoundation/CFLocale.h>
38 # include <CoreFoundation/CFString.h>
39 #endif
40
41 #include "ntstatus.h"
42 #define WIN32_NO_STATUS
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winuser.h"  /* for RT_STRINGW */
46 #include "winternl.h"
47 #include "wine/unicode.h"
48 #include "winnls.h"
49 #include "winerror.h"
50 #include "winver.h"
51 #include "kernel_private.h"
52 #include "wine/debug.h"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55
56 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
57                                     LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
58
59 /* current code pages */
60 static const union cptable *ansi_cptable;
61 static const union cptable *oem_cptable;
62 static const union cptable *mac_cptable;
63 static const union cptable *unix_cptable;  /* NULL if UTF8 */
64
65 static const WCHAR szLocaleKeyName[] = {
66     'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
67     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
68     'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
69 };
70
71 static const WCHAR szLangGroupsKeyName[] = {
72     'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
73     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
74     'C','o','n','t','r','o','l','\\','N','l','s','\\',
75     'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
76 };
77
78 /* Charset to codepage map, sorted by name. */
79 static const struct charset_entry
80 {
81     const char *charset_name;
82     UINT        codepage;
83 } charset_names[] =
84 {
85     { "BIG5", 950 },
86     { "CP1250", 1250 },
87     { "CP1251", 1251 },
88     { "CP1252", 1252 },
89     { "CP1253", 1253 },
90     { "CP1254", 1254 },
91     { "CP1255", 1255 },
92     { "CP1256", 1256 },
93     { "CP1257", 1257 },
94     { "CP1258", 1258 },
95     { "CP932", 932 },
96     { "CP936", 936 },
97     { "CP949", 949 },
98     { "CP950", 950 },
99     { "EUCJP", 20932 },
100     { "GB2312", 936 },
101     { "IBM037", 37 },
102     { "IBM1026", 1026 },
103     { "IBM424", 424 },
104     { "IBM437", 437 },
105     { "IBM500", 500 },
106     { "IBM850", 850 },
107     { "IBM852", 852 },
108     { "IBM855", 855 },
109     { "IBM857", 857 },
110     { "IBM860", 860 },
111     { "IBM861", 861 },
112     { "IBM862", 862 },
113     { "IBM863", 863 },
114     { "IBM864", 864 },
115     { "IBM865", 865 },
116     { "IBM866", 866 },
117     { "IBM869", 869 },
118     { "IBM874", 874 },
119     { "IBM875", 875 },
120     { "ISO88591", 28591 },
121     { "ISO885910", 28600 },
122     { "ISO885913", 28603 },
123     { "ISO885914", 28604 },
124     { "ISO885915", 28605 },
125     { "ISO885916", 28606 },
126     { "ISO88592", 28592 },
127     { "ISO88593", 28593 },
128     { "ISO88594", 28594 },
129     { "ISO88595", 28595 },
130     { "ISO88596", 28596 },
131     { "ISO88597", 28597 },
132     { "ISO88598", 28598 },
133     { "ISO88599", 28599 },
134     { "KOI8R", 20866 },
135     { "KOI8U", 21866 },
136     { "UTF8", CP_UTF8 }
137 };
138
139
140 struct locale_name
141 {
142     WCHAR  win_name[128];   /* Windows name ("en-US") */
143     WCHAR  lang[128];       /* language ("en") (note: buffer contains the other strings too) */
144     WCHAR *country;         /* country ("US") */
145     WCHAR *charset;         /* charset ("UTF-8") for Unix format only */
146     WCHAR *script;          /* script ("Latn") for Windows format only */
147     WCHAR *modifier;        /* modifier or sort order */
148     LCID   lcid;            /* corresponding LCID */
149     int    matches;         /* number of elements matching LCID (0..4) */
150     UINT   codepage;        /* codepage corresponding to charset */
151 };
152
153 /* locale ids corresponding to the various Unix locale parameters */
154 static LCID lcid_LC_COLLATE;
155 static LCID lcid_LC_CTYPE;
156 static LCID lcid_LC_MESSAGES;
157 static LCID lcid_LC_MONETARY;
158 static LCID lcid_LC_NUMERIC;
159 static LCID lcid_LC_TIME;
160 static LCID lcid_LC_PAPER;
161 static LCID lcid_LC_MEASUREMENT;
162 static LCID lcid_LC_TELEPHONE;
163
164 /* Copy Ascii string to Unicode without using codepages */
165 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
166 {
167     while (n > 1 && *src)
168     {
169         *dst++ = (unsigned char)*src++;
170         n--;
171     }
172     if (n) *dst = 0;
173 }
174
175 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
176 {
177     return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
178 }
179
180 /***********************************************************************
181  *              get_lcid_codepage
182  *
183  * Retrieve the ANSI codepage for a given locale.
184  */
185 static inline UINT get_lcid_codepage( LCID lcid )
186 {
187     UINT ret;
188     if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
189                          sizeof(ret)/sizeof(WCHAR) )) ret = 0;
190     return ret;
191 }
192
193
194 /***********************************************************************
195  *              get_codepage_table
196  *
197  * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
198  */
199 static const union cptable *get_codepage_table( unsigned int codepage )
200 {
201     const union cptable *ret = NULL;
202
203     assert( ansi_cptable );  /* init must have been done already */
204
205     switch(codepage)
206     {
207     case CP_ACP:
208         return ansi_cptable;
209     case CP_OEMCP:
210         return oem_cptable;
211     case CP_MACCP:
212         return mac_cptable;
213     case CP_UTF7:
214     case CP_UTF8:
215         break;
216     case CP_THREAD_ACP:
217         if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
218         codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
219         /* fall through */
220     default:
221         if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
222         if (codepage == oem_cptable->info.codepage) return oem_cptable;
223         if (codepage == mac_cptable->info.codepage) return mac_cptable;
224         ret = wine_cp_get_table( codepage );
225         break;
226     }
227     return ret;
228 }
229
230
231 /***********************************************************************
232  *              charset_cmp (internal)
233  */
234 static int charset_cmp( const void *name, const void *entry )
235 {
236     const struct charset_entry *charset = entry;
237     return strcasecmp( name, charset->charset_name );
238 }
239
240 /***********************************************************************
241  *              find_charset
242  */
243 static UINT find_charset( const WCHAR *name )
244 {
245     const struct charset_entry *entry;
246     char charset_name[16];
247     size_t i, j;
248
249     /* remove punctuation characters from charset name */
250     for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
251         if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
252     charset_name[j] = 0;
253
254     entry = bsearch( charset_name, charset_names,
255                      sizeof(charset_names)/sizeof(charset_names[0]),
256                      sizeof(charset_names[0]), charset_cmp );
257     if (entry) return entry->codepage;
258     return 0;
259 }
260
261
262 /***********************************************************************
263  *           find_locale_id_callback
264  */
265 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
266                                               LPCWSTR name, WORD LangID, LPARAM lParam )
267 {
268     struct locale_name *data = (struct locale_name *)lParam;
269     WCHAR buffer[128];
270     int matches = 0;
271     LCID lcid = MAKELCID( LangID, SORT_DEFAULT );  /* FIXME: handle sort order */
272
273     if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
274
275     /* first check exact name */
276     if (data->win_name[0] &&
277         GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
278                         buffer, sizeof(buffer)/sizeof(WCHAR) ))
279     {
280         if (!strcmpW( data->win_name, buffer ))
281         {
282             matches = 4;  /* everything matches */
283             goto done;
284         }
285     }
286
287     if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
288                          buffer, sizeof(buffer)/sizeof(WCHAR) ))
289         return TRUE;
290     if (strcmpW( buffer, data->lang )) return TRUE;
291     matches++;  /* language name matched */
292
293     if (data->country)
294     {
295         if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
296                             buffer, sizeof(buffer)/sizeof(WCHAR) ))
297         {
298             if (strcmpW( buffer, data->country )) goto done;
299             matches++;  /* country name matched */
300         }
301     }
302     else  /* match default language */
303     {
304         if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
305     }
306
307     if (data->codepage)
308     {
309         UINT unix_cp;
310         if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
311                             (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
312         {
313             if (unix_cp == data->codepage) matches++;
314         }
315     }
316
317     /* FIXME: check sort order */
318
319 done:
320     if (matches > data->matches)
321     {
322         data->lcid = lcid;
323         data->matches = matches;
324     }
325     return (data->matches < 4);  /* no need to continue for perfect match */
326 }
327
328
329 /***********************************************************************
330  *              parse_locale_name
331  *
332  * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
333  * Unix format is: lang[_country][.charset][@modifier]
334  * Windows format is: lang[-script][-country][_modifier]
335  */
336 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
337 {
338     static const WCHAR sepW[] = {'-','_','.','@',0};
339     static const WCHAR winsepW[] = {'-','_',0};
340     static const WCHAR posixW[] = {'P','O','S','I','X',0};
341     static const WCHAR cW[] = {'C',0};
342     static const WCHAR latinW[] = {'l','a','t','i','n',0};
343     static const WCHAR latnW[] = {'-','L','a','t','n',0};
344     WCHAR *p;
345
346     TRACE("%s\n", debugstr_w(str));
347
348     name->country = name->charset = name->script = name->modifier = NULL;
349     name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
350     name->matches = 0;
351     name->codepage = 0;
352     name->win_name[0] = 0;
353     lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
354
355     if (!(p = strpbrkW( name->lang, sepW )))
356     {
357         if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
358         {
359             name->matches = 4;  /* perfect match for default English lcid */
360             return;
361         }
362         strcpyW( name->win_name, name->lang );
363     }
364     else if (*p == '-')  /* Windows format */
365     {
366         strcpyW( name->win_name, name->lang );
367         *p++ = 0;
368         name->country = p;
369         if (!(p = strpbrkW( p, winsepW ))) goto done;
370         if (*p == '-')
371         {
372             *p++ = 0;
373             name->script = name->country;
374             name->country = p;
375             if (!(p = strpbrkW( p, winsepW ))) goto done;
376         }
377         *p++ = 0;
378         name->modifier = p;
379     }
380     else  /* Unix format */
381     {
382         if (*p == '_')
383         {
384             *p++ = 0;
385             name->country = p;
386             p = strpbrkW( p, sepW + 2 );
387         }
388         if (p && *p == '.')
389         {
390             *p++ = 0;
391             name->charset = p;
392             p = strchrW( p, '@' );
393         }
394         if (p)
395         {
396             *p++ = 0;
397             name->modifier = p;
398         }
399
400         if (name->charset)
401             name->codepage = find_charset( name->charset );
402
403         /* rebuild a Windows name if possible */
404
405         if (name->charset) goto done;  /* can't specify charset in Windows format */
406         if (name->modifier && strcmpW( name->modifier, latinW ))
407             goto done;  /* only Latn script supported for now */
408         strcpyW( name->win_name, name->lang );
409         if (name->modifier) strcatW( name->win_name, latnW );
410         if (name->country)
411         {
412             p = name->win_name + strlenW(name->win_name);
413             *p++ = '-';
414             strcpyW( p, name->country );
415         }
416     }
417 done:
418     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
419                             find_locale_id_callback, (LPARAM)name );
420 }
421
422
423 /***********************************************************************
424  *           convert_default_lcid
425  *
426  * Get the default LCID to use for a given lctype in GetLocaleInfo.
427  */
428 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
429 {
430     if (lcid == LOCALE_SYSTEM_DEFAULT ||
431         lcid == LOCALE_USER_DEFAULT ||
432         lcid == LOCALE_NEUTRAL)
433     {
434         LCID default_id = 0;
435
436         switch(lctype & 0xffff)
437         {
438         case LOCALE_SSORTNAME:
439             default_id = lcid_LC_COLLATE;
440             break;
441
442         case LOCALE_FONTSIGNATURE:
443         case LOCALE_IDEFAULTANSICODEPAGE:
444         case LOCALE_IDEFAULTCODEPAGE:
445         case LOCALE_IDEFAULTEBCDICCODEPAGE:
446         case LOCALE_IDEFAULTMACCODEPAGE:
447         case LOCALE_IDEFAULTUNIXCODEPAGE:
448             default_id = lcid_LC_CTYPE;
449             break;
450
451         case LOCALE_ICURRDIGITS:
452         case LOCALE_ICURRENCY:
453         case LOCALE_IINTLCURRDIGITS:
454         case LOCALE_INEGCURR:
455         case LOCALE_INEGSEPBYSPACE:
456         case LOCALE_INEGSIGNPOSN:
457         case LOCALE_INEGSYMPRECEDES:
458         case LOCALE_IPOSSEPBYSPACE:
459         case LOCALE_IPOSSIGNPOSN:
460         case LOCALE_IPOSSYMPRECEDES:
461         case LOCALE_SCURRENCY:
462         case LOCALE_SINTLSYMBOL:
463         case LOCALE_SMONDECIMALSEP:
464         case LOCALE_SMONGROUPING:
465         case LOCALE_SMONTHOUSANDSEP:
466         case LOCALE_SNATIVECURRNAME:
467             default_id = lcid_LC_MONETARY;
468             break;
469
470         case LOCALE_IDIGITS:
471         case LOCALE_IDIGITSUBSTITUTION:
472         case LOCALE_ILZERO:
473         case LOCALE_INEGNUMBER:
474         case LOCALE_SDECIMAL:
475         case LOCALE_SGROUPING:
476         case LOCALE_SNAN:
477         case LOCALE_SNATIVEDIGITS:
478         case LOCALE_SNEGATIVESIGN:
479         case LOCALE_SNEGINFINITY:
480         case LOCALE_SPOSINFINITY:
481         case LOCALE_SPOSITIVESIGN:
482         case LOCALE_STHOUSAND:
483             default_id = lcid_LC_NUMERIC;
484             break;
485
486         case LOCALE_ICALENDARTYPE:
487         case LOCALE_ICENTURY:
488         case LOCALE_IDATE:
489         case LOCALE_IDAYLZERO:
490         case LOCALE_IFIRSTDAYOFWEEK:
491         case LOCALE_IFIRSTWEEKOFYEAR:
492         case LOCALE_ILDATE:
493         case LOCALE_IMONLZERO:
494         case LOCALE_IOPTIONALCALENDAR:
495         case LOCALE_ITIME:
496         case LOCALE_ITIMEMARKPOSN:
497         case LOCALE_ITLZERO:
498         case LOCALE_S1159:
499         case LOCALE_S2359:
500         case LOCALE_SABBREVDAYNAME1:
501         case LOCALE_SABBREVDAYNAME2:
502         case LOCALE_SABBREVDAYNAME3:
503         case LOCALE_SABBREVDAYNAME4:
504         case LOCALE_SABBREVDAYNAME5:
505         case LOCALE_SABBREVDAYNAME6:
506         case LOCALE_SABBREVDAYNAME7:
507         case LOCALE_SABBREVMONTHNAME1:
508         case LOCALE_SABBREVMONTHNAME2:
509         case LOCALE_SABBREVMONTHNAME3:
510         case LOCALE_SABBREVMONTHNAME4:
511         case LOCALE_SABBREVMONTHNAME5:
512         case LOCALE_SABBREVMONTHNAME6:
513         case LOCALE_SABBREVMONTHNAME7:
514         case LOCALE_SABBREVMONTHNAME8:
515         case LOCALE_SABBREVMONTHNAME9:
516         case LOCALE_SABBREVMONTHNAME10:
517         case LOCALE_SABBREVMONTHNAME11:
518         case LOCALE_SABBREVMONTHNAME12:
519         case LOCALE_SABBREVMONTHNAME13:
520         case LOCALE_SDATE:
521         case LOCALE_SDAYNAME1:
522         case LOCALE_SDAYNAME2:
523         case LOCALE_SDAYNAME3:
524         case LOCALE_SDAYNAME4:
525         case LOCALE_SDAYNAME5:
526         case LOCALE_SDAYNAME6:
527         case LOCALE_SDAYNAME7:
528         case LOCALE_SDURATION:
529         case LOCALE_SLONGDATE:
530         case LOCALE_SMONTHNAME1:
531         case LOCALE_SMONTHNAME2:
532         case LOCALE_SMONTHNAME3:
533         case LOCALE_SMONTHNAME4:
534         case LOCALE_SMONTHNAME5:
535         case LOCALE_SMONTHNAME6:
536         case LOCALE_SMONTHNAME7:
537         case LOCALE_SMONTHNAME8:
538         case LOCALE_SMONTHNAME9:
539         case LOCALE_SMONTHNAME10:
540         case LOCALE_SMONTHNAME11:
541         case LOCALE_SMONTHNAME12:
542         case LOCALE_SMONTHNAME13:
543         case LOCALE_SSHORTDATE:
544         case LOCALE_SSHORTESTDAYNAME1:
545         case LOCALE_SSHORTESTDAYNAME2:
546         case LOCALE_SSHORTESTDAYNAME3:
547         case LOCALE_SSHORTESTDAYNAME4:
548         case LOCALE_SSHORTESTDAYNAME5:
549         case LOCALE_SSHORTESTDAYNAME6:
550         case LOCALE_SSHORTESTDAYNAME7:
551         case LOCALE_STIME:
552         case LOCALE_STIMEFORMAT:
553         case LOCALE_SYEARMONTH:
554             default_id = lcid_LC_TIME;
555             break;
556
557         case LOCALE_IPAPERSIZE:
558             default_id = lcid_LC_PAPER;
559             break;
560
561         case LOCALE_IMEASURE:
562             default_id = lcid_LC_MEASUREMENT;
563             break;
564
565         case LOCALE_ICOUNTRY:
566             default_id = lcid_LC_TELEPHONE;
567             break;
568         }
569         if (default_id) lcid = default_id;
570     }
571     return ConvertDefaultLocale( lcid );
572 }
573
574 /***********************************************************************
575  *           is_genitive_name_supported
576  *
577  * Determine could LCTYPE basically support genitive name form or not.
578  */
579 static BOOL is_genitive_name_supported( LCTYPE lctype )
580 {
581     switch(lctype & 0xffff)
582     {
583     case LOCALE_SMONTHNAME1:
584     case LOCALE_SMONTHNAME2:
585     case LOCALE_SMONTHNAME3:
586     case LOCALE_SMONTHNAME4:
587     case LOCALE_SMONTHNAME5:
588     case LOCALE_SMONTHNAME6:
589     case LOCALE_SMONTHNAME7:
590     case LOCALE_SMONTHNAME8:
591     case LOCALE_SMONTHNAME9:
592     case LOCALE_SMONTHNAME10:
593     case LOCALE_SMONTHNAME11:
594     case LOCALE_SMONTHNAME12:
595     case LOCALE_SMONTHNAME13:
596          return TRUE;
597     default:
598          return FALSE;
599     }
600 }
601
602 /***********************************************************************
603  *              create_registry_key
604  *
605  * Create the Control Panel\\International registry key.
606  */
607 static inline HANDLE create_registry_key(void)
608 {
609     static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
610     static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
611     OBJECT_ATTRIBUTES attr;
612     UNICODE_STRING nameW;
613     HANDLE cpl_key, hkey = 0;
614
615     if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
616
617     attr.Length = sizeof(attr);
618     attr.RootDirectory = hkey;
619     attr.ObjectName = &nameW;
620     attr.Attributes = 0;
621     attr.SecurityDescriptor = NULL;
622     attr.SecurityQualityOfService = NULL;
623     RtlInitUnicodeString( &nameW, cplW );
624
625     if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
626     {
627         NtClose( attr.RootDirectory );
628         attr.RootDirectory = cpl_key;
629         RtlInitUnicodeString( &nameW, intlW );
630         if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
631     }
632     NtClose( attr.RootDirectory );
633     return hkey;
634 }
635
636
637 /* update the registry settings for a given locale parameter */
638 /* return TRUE if an update was needed */
639 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
640                                     const LCTYPE *values, UINT nb_values )
641 {
642     static const WCHAR formatW[] = { '%','0','8','x',0 };
643     WCHAR bufferW[40];
644     UNICODE_STRING nameW;
645     DWORD count, i;
646
647     RtlInitUnicodeString( &nameW, name );
648     count = sizeof(bufferW);
649     if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
650     {
651         const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
652         LPCWSTR text = (LPCWSTR)info->Data;
653
654         if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
655         TRACE( "updating registry, locale %s changed %s -> %08x\n",
656                debugstr_w(name), debugstr_w(text), lcid );
657     }
658     else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
659     sprintfW( bufferW, formatW, lcid );
660     NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
661
662     for (i = 0; i < nb_values; i++)
663     {
664         GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
665                         sizeof(bufferW)/sizeof(WCHAR) );
666         SetLocaleInfoW( lcid, values[i], bufferW );
667     }
668     return TRUE;
669 }
670
671
672 /***********************************************************************
673  *              LOCALE_InitRegistry
674  *
675  * Update registry contents on startup if the user locale has changed.
676  * This simulates the action of the Windows control panel.
677  */
678 void LOCALE_InitRegistry(void)
679 {
680     static const WCHAR acpW[] = {'A','C','P',0};
681     static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
682     static const WCHAR maccpW[] = {'M','A','C','C','P',0};
683     static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
684     static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
685     static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
686     static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
687     static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
688     static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
689     static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
690     static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
691     static const struct
692     {
693         LPCWSTR name;
694         USHORT value;
695     } update_cp_values[] = {
696         { acpW, LOCALE_IDEFAULTANSICODEPAGE },
697         { oemcpW, LOCALE_IDEFAULTCODEPAGE },
698         { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
699     };
700     static const LCTYPE lc_messages_values[] = {
701       LOCALE_SABBREVLANGNAME,
702       LOCALE_SCOUNTRY,
703       LOCALE_SLIST };
704     static const LCTYPE lc_monetary_values[] = {
705       LOCALE_SCURRENCY,
706       LOCALE_ICURRENCY,
707       LOCALE_INEGCURR,
708       LOCALE_ICURRDIGITS,
709       LOCALE_ILZERO,
710       LOCALE_SMONDECIMALSEP,
711       LOCALE_SMONGROUPING,
712       LOCALE_SMONTHOUSANDSEP };
713     static const LCTYPE lc_numeric_values[] = {
714       LOCALE_SDECIMAL,
715       LOCALE_STHOUSAND,
716       LOCALE_IDIGITS,
717       LOCALE_IDIGITSUBSTITUTION,
718       LOCALE_SNATIVEDIGITS,
719       LOCALE_INEGNUMBER,
720       LOCALE_SNEGATIVESIGN,
721       LOCALE_SPOSITIVESIGN,
722       LOCALE_SGROUPING };
723     static const LCTYPE lc_time_values[] = {
724       LOCALE_S1159,
725       LOCALE_S2359,
726       LOCALE_STIME,
727       LOCALE_ITIME,
728       LOCALE_ITLZERO,
729       LOCALE_SSHORTDATE,
730       LOCALE_SLONGDATE,
731       LOCALE_SDATE,
732       LOCALE_ITIMEMARKPOSN,
733       LOCALE_ICALENDARTYPE,
734       LOCALE_IFIRSTDAYOFWEEK,
735       LOCALE_IFIRSTWEEKOFYEAR,
736       LOCALE_STIMEFORMAT,
737       LOCALE_SYEARMONTH,
738       LOCALE_IDATE };
739     static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
740     static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
741     static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
742
743     UNICODE_STRING nameW;
744     WCHAR bufferW[80];
745     DWORD count, i;
746     HANDLE hkey;
747     LCID lcid = GetUserDefaultLCID();
748
749     if (!(hkey = create_registry_key()))
750         return;  /* don't do anything if we can't create the registry key */
751
752     locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
753                             sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
754     locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
755                             sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
756     locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
757                             sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
758     locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
759                             sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
760     locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
761                             sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
762     locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
763                             sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
764     locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
765                             sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
766
767     if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
768     {
769         static const WCHAR codepageW[] =
770             {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
771              'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
772              'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
773
774         OBJECT_ATTRIBUTES attr;
775         HANDLE nls_key;
776         DWORD len = 14;
777
778         RtlInitUnicodeString( &nameW, codepageW );
779         InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
780         while (codepageW[len])
781         {
782             nameW.Length = len * sizeof(WCHAR);
783             if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
784             NtClose( nls_key );
785             len++;
786             while (codepageW[len] && codepageW[len] != '\\') len++;
787         }
788         nameW.Length = len * sizeof(WCHAR);
789         if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
790         {
791             for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
792             {
793                 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
794                                         bufferW, sizeof(bufferW)/sizeof(WCHAR) );
795                 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
796                 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
797             }
798             NtClose( nls_key );
799         }
800     }
801
802     NtClose( hkey );
803 }
804
805
806 /***********************************************************************
807  *           setup_unix_locales
808  */
809 static UINT setup_unix_locales(void)
810 {
811     struct locale_name locale_name;
812     WCHAR buffer[128], ctype_buff[128];
813     char *locale;
814     UINT unix_cp = 0;
815
816     if ((locale = setlocale( LC_CTYPE, NULL )))
817     {
818         strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
819         parse_locale_name( ctype_buff, &locale_name );
820         lcid_LC_CTYPE = locale_name.lcid;
821         unix_cp = locale_name.codepage;
822     }
823     if (!lcid_LC_CTYPE)  /* this one needs a default value */
824         lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
825
826     TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
827            locale_name.lcid, locale_name.matches, debugstr_a(locale) );
828
829 #define GET_UNIX_LOCALE(cat) do \
830     if ((locale = setlocale( cat, NULL ))) \
831     { \
832         strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
833         if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
834         else { \
835             parse_locale_name( buffer, &locale_name );  \
836             lcid_##cat = locale_name.lcid; \
837             TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n",        \
838                    locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
839         } \
840     } while (0)
841
842     GET_UNIX_LOCALE( LC_COLLATE );
843     GET_UNIX_LOCALE( LC_MESSAGES );
844     GET_UNIX_LOCALE( LC_MONETARY );
845     GET_UNIX_LOCALE( LC_NUMERIC );
846     GET_UNIX_LOCALE( LC_TIME );
847 #ifdef LC_PAPER
848     GET_UNIX_LOCALE( LC_PAPER );
849 #endif
850 #ifdef LC_MEASUREMENT
851     GET_UNIX_LOCALE( LC_MEASUREMENT );
852 #endif
853 #ifdef LC_TELEPHONE
854     GET_UNIX_LOCALE( LC_TELEPHONE );
855 #endif
856
857 #undef GET_UNIX_LOCALE
858
859     return unix_cp;
860 }
861
862
863 /***********************************************************************
864  *              GetUserDefaultLangID (KERNEL32.@)
865  *
866  * Get the default language Id for the current user.
867  *
868  * PARAMS
869  *  None.
870  *
871  * RETURNS
872  *  The current LANGID of the default language for the current user.
873  */
874 LANGID WINAPI GetUserDefaultLangID(void)
875 {
876     return LANGIDFROMLCID(GetUserDefaultLCID());
877 }
878
879
880 /***********************************************************************
881  *              GetSystemDefaultLangID (KERNEL32.@)
882  *
883  * Get the default language Id for the system.
884  *
885  * PARAMS
886  *  None.
887  *
888  * RETURNS
889  *  The current LANGID of the default language for the system.
890  */
891 LANGID WINAPI GetSystemDefaultLangID(void)
892 {
893     return LANGIDFROMLCID(GetSystemDefaultLCID());
894 }
895
896
897 /***********************************************************************
898  *              GetUserDefaultLCID (KERNEL32.@)
899  *
900  * Get the default locale Id for the current user.
901  *
902  * PARAMS
903  *  None.
904  *
905  * RETURNS
906  *  The current LCID of the default locale for the current user.
907  */
908 LCID WINAPI GetUserDefaultLCID(void)
909 {
910     LCID lcid;
911     NtQueryDefaultLocale( TRUE, &lcid );
912     return lcid;
913 }
914
915
916 /***********************************************************************
917  *              GetSystemDefaultLCID (KERNEL32.@)
918  *
919  * Get the default locale Id for the system.
920  *
921  * PARAMS
922  *  None.
923  *
924  * RETURNS
925  *  The current LCID of the default locale for the system.
926  */
927 LCID WINAPI GetSystemDefaultLCID(void)
928 {
929     LCID lcid;
930     NtQueryDefaultLocale( FALSE, &lcid );
931     return lcid;
932 }
933
934
935 /***********************************************************************
936  *              GetUserDefaultUILanguage (KERNEL32.@)
937  *
938  * Get the default user interface language Id for the current user.
939  *
940  * PARAMS
941  *  None.
942  *
943  * RETURNS
944  *  The current LANGID of the default UI language for the current user.
945  */
946 LANGID WINAPI GetUserDefaultUILanguage(void)
947 {
948     LANGID lang;
949     NtQueryDefaultUILanguage( &lang );
950     return lang;
951 }
952
953
954 /***********************************************************************
955  *              GetSystemDefaultUILanguage (KERNEL32.@)
956  *
957  * Get the default user interface language Id for the system.
958  *
959  * PARAMS
960  *  None.
961  *
962  * RETURNS
963  *  The current LANGID of the default UI language for the system. This is
964  *  typically the same language used during the installation process.
965  */
966 LANGID WINAPI GetSystemDefaultUILanguage(void)
967 {
968     LANGID lang;
969     NtQueryInstallUILanguage( &lang );
970     return lang;
971 }
972
973
974 /***********************************************************************
975  *           LocaleNameToLCID  (KERNEL32.@)
976  */
977 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
978 {
979     struct locale_name locale_name;
980
981     if (flags) FIXME( "unsupported flags %x\n", flags );
982
983     if (name == LOCALE_NAME_USER_DEFAULT)
984         return GetUserDefaultLCID();
985
986     /* string parsing */
987     parse_locale_name( name, &locale_name );
988
989     TRACE( "found lcid %x for %s, matches %d\n",
990            locale_name.lcid, debugstr_w(name), locale_name.matches );
991
992     if (!locale_name.matches)
993         WARN( "locale %s not recognized, defaulting to English\n", debugstr_w(name) );
994     else if (locale_name.matches == 1)
995         WARN( "locale %s not recognized, defaulting to %s\n",
996               debugstr_w(name), debugstr_w(locale_name.lang) );
997
998     return locale_name.lcid;
999 }
1000
1001
1002 /***********************************************************************
1003  *           LCIDToLocaleName  (KERNEL32.@)
1004  */
1005 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1006 {
1007     if (flags) FIXME( "unsupported flags %x\n", flags );
1008
1009     return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1010 }
1011
1012
1013 /******************************************************************************
1014  *              get_locale_value_name
1015  *
1016  * Gets the registry value name for a given lctype.
1017  */
1018 static const WCHAR *get_locale_value_name( DWORD lctype )
1019 {
1020     static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1021     static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1022     static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1023     static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1024     static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1025     static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1026     static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1027     static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1028     static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1029     static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1030     static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1031     static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1032     static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1033     static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1034     static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1035     static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1036     static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1037     static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1038     static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1039     static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1040     static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1041     static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1042     static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1043     static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1044     static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1045     static const WCHAR sListW[] = {'s','L','i','s','t',0};
1046     static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1047     static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1048     static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1049     static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1050     static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1051     static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1052     static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1053     static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1054     static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1055     static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1056     static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1057     static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1058     static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1059
1060     switch (lctype)
1061     {
1062     /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1063      * the values are stored in the registry, confirmed under Windows.
1064      */
1065     case LOCALE_ICALENDARTYPE:    return iCalendarTypeW;
1066     case LOCALE_ICURRDIGITS:      return iCurrDigitsW;
1067     case LOCALE_ICURRENCY:        return iCurrencyW;
1068     case LOCALE_IDIGITS:          return iDigitsW;
1069     case LOCALE_IFIRSTDAYOFWEEK:  return iFirstDayOfWeekW;
1070     case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1071     case LOCALE_ILZERO:           return iLZeroW;
1072     case LOCALE_IMEASURE:         return iMeasureW;
1073     case LOCALE_INEGCURR:         return iNegCurrW;
1074     case LOCALE_INEGNUMBER:       return iNegNumberW;
1075     case LOCALE_IPAPERSIZE:       return iPaperSizeW;
1076     case LOCALE_ITIME:            return iTimeW;
1077     case LOCALE_S1159:            return s1159W;
1078     case LOCALE_S2359:            return s2359W;
1079     case LOCALE_SCURRENCY:        return sCurrencyW;
1080     case LOCALE_SDATE:            return sDateW;
1081     case LOCALE_SDECIMAL:         return sDecimalW;
1082     case LOCALE_SGROUPING:        return sGroupingW;
1083     case LOCALE_SLIST:            return sListW;
1084     case LOCALE_SLONGDATE:        return sLongDateW;
1085     case LOCALE_SMONDECIMALSEP:   return sMonDecimalSepW;
1086     case LOCALE_SMONGROUPING:     return sMonGroupingW;
1087     case LOCALE_SMONTHOUSANDSEP:  return sMonThousandSepW;
1088     case LOCALE_SNEGATIVESIGN:    return sNegativeSignW;
1089     case LOCALE_SPOSITIVESIGN:    return sPositiveSignW;
1090     case LOCALE_SSHORTDATE:       return sShortDateW;
1091     case LOCALE_STHOUSAND:        return sThousandW;
1092     case LOCALE_STIME:            return sTimeW;
1093     case LOCALE_STIMEFORMAT:      return sTimeFormatW;
1094     case LOCALE_SYEARMONTH:       return sYearMonthW;
1095
1096     /* The following are not listed under MSDN as supported,
1097      * but seem to be used and also stored in the registry.
1098      */
1099     case LOCALE_ICOUNTRY:         return iCountryW;
1100     case LOCALE_IDATE:            return iDateW;
1101     case LOCALE_ILDATE:           return iLDateW;
1102     case LOCALE_ITLZERO:          return iTLZeroW;
1103     case LOCALE_SCOUNTRY:         return sCountryW;
1104     case LOCALE_SABBREVLANGNAME:  return sLanguageW;
1105
1106     /* The following are used in XP and later */
1107     case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1108     case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
1109     case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
1110     }
1111     return NULL;
1112 }
1113
1114
1115 /******************************************************************************
1116  *              get_registry_locale_info
1117  *
1118  * Retrieve user-modified locale info from the registry.
1119  * Return length, 0 on error, -1 if not found.
1120  */
1121 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1122 {
1123     DWORD size;
1124     INT ret;
1125     HANDLE hkey;
1126     NTSTATUS status;
1127     UNICODE_STRING nameW;
1128     KEY_VALUE_PARTIAL_INFORMATION *info;
1129     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1130
1131     if (!(hkey = create_registry_key())) return -1;
1132
1133     RtlInitUnicodeString( &nameW, value );
1134     size = info_size + len * sizeof(WCHAR);
1135
1136     if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1137     {
1138         NtClose( hkey );
1139         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1140         return 0;
1141     }
1142
1143     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1144
1145     if (!status)
1146     {
1147         ret = (size - info_size) / sizeof(WCHAR);
1148         /* append terminating null if needed */
1149         if (!ret || ((WCHAR *)info->Data)[ret-1])
1150         {
1151             if (ret < len || !buffer) ret++;
1152             else
1153             {
1154                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1155                 ret = 0;
1156             }
1157         }
1158         if (ret && buffer)
1159         {
1160             memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1161             buffer[ret-1] = 0;
1162         }
1163     }
1164     else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1165     {
1166         ret = (size - info_size) / sizeof(WCHAR) + 1;
1167     }
1168     else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1169     {
1170         ret = -1;
1171     }
1172     else
1173     {
1174         SetLastError( RtlNtStatusToDosError(status) );
1175         ret = 0;
1176     }
1177     NtClose( hkey );
1178     HeapFree( GetProcessHeap(), 0, info );
1179     return ret;
1180 }
1181
1182
1183 /******************************************************************************
1184  *              GetLocaleInfoA (KERNEL32.@)
1185  *
1186  * Get information about an aspect of a locale.
1187  *
1188  * PARAMS
1189  *  lcid   [I] LCID of the locale
1190  *  lctype [I] LCTYPE_ flags from "winnls.h"
1191  *  buffer [O] Destination for the information
1192  *  len    [I] Length of buffer in characters
1193  *
1194  * RETURNS
1195  *  Success: The size of the data requested. If buffer is non-NULL, it is filled
1196  *           with the information.
1197  *  Failure: 0. Use GetLastError() to determine the cause.
1198  *
1199  * NOTES
1200  *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1201  *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1202  *    which is a bit string.
1203  */
1204 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1205 {
1206     WCHAR *bufferW;
1207     INT lenW, ret;
1208
1209     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1210
1211     if (len < 0 || (len && !buffer))
1212     {
1213         SetLastError( ERROR_INVALID_PARAMETER );
1214         return 0;
1215     }
1216     if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1217     {
1218         SetLastError( ERROR_INVALID_FLAGS );
1219         return 0;
1220     }
1221
1222     if (!len) buffer = NULL;
1223
1224     if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1225
1226     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1227     {
1228         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1229         return 0;
1230     }
1231     if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1232     {
1233         if ((lctype & LOCALE_RETURN_NUMBER) ||
1234             ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1235         {
1236             /* it's not an ASCII string, just bytes */
1237             ret *= sizeof(WCHAR);
1238             if (buffer)
1239             {
1240                 if (ret <= len) memcpy( buffer, bufferW, ret );
1241                 else
1242                 {
1243                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1244                     ret = 0;
1245                 }
1246             }
1247         }
1248         else
1249         {
1250             UINT codepage = CP_ACP;
1251             if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1252             ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1253         }
1254     }
1255     HeapFree( GetProcessHeap(), 0, bufferW );
1256     return ret;
1257 }
1258
1259
1260 /******************************************************************************
1261  *              GetLocaleInfoW (KERNEL32.@)
1262  *
1263  * See GetLocaleInfoA.
1264  */
1265 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1266 {
1267     LANGID lang_id;
1268     HRSRC hrsrc;
1269     HGLOBAL hmem;
1270     INT ret;
1271     UINT lcflags;
1272     const WCHAR *p;
1273     unsigned int i;
1274
1275     if (len < 0 || (len && !buffer))
1276     {
1277         SetLastError( ERROR_INVALID_PARAMETER );
1278         return 0;
1279     }
1280     if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1281        !is_genitive_name_supported( lctype ))
1282     {
1283         SetLastError( ERROR_INVALID_FLAGS );
1284         return 0;
1285     }
1286
1287     if (!len) buffer = NULL;
1288
1289     lcid = convert_default_lcid( lcid, lctype );
1290
1291     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1292     lctype &= 0xffff;
1293
1294     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1295
1296     /* first check for overrides in the registry */
1297
1298     if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1299         lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1300     {
1301         const WCHAR *value = get_locale_value_name(lctype);
1302
1303         if (value)
1304         {
1305             if (lcflags & LOCALE_RETURN_NUMBER)
1306             {
1307                 WCHAR tmp[16];
1308                 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1309                 if (ret > 0)
1310                 {
1311                     WCHAR *end;
1312                     UINT number = strtolW( tmp, &end, 10 );
1313                     if (*end)  /* invalid number */
1314                     {
1315                         SetLastError( ERROR_INVALID_FLAGS );
1316                         return 0;
1317                     }
1318                     ret = sizeof(UINT)/sizeof(WCHAR);
1319                     if (!buffer) return ret;
1320                     if (ret > len)
1321                     {
1322                         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1323                         return 0;
1324                     }
1325                     memcpy( buffer, &number, sizeof(number) );
1326                 }
1327             }
1328             else ret = get_registry_locale_info( value, buffer, len );
1329
1330             if (ret != -1) return ret;
1331         }
1332     }
1333
1334     /* now load it from kernel resources */
1335
1336     lang_id = LANGIDFROMLCID( lcid );
1337
1338     /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1339     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1340         lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1341
1342     if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1343                                    ULongToPtr((lctype >> 4) + 1), lang_id )))
1344     {
1345         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
1346         return 0;
1347     }
1348     if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1349         return 0;
1350
1351     p = LockResource( hmem );
1352     for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1353
1354     if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1355     else if (is_genitive_name_supported( lctype ) && *p)
1356     {
1357         /* genitive form's stored after a null separator from a nominative */
1358         for (i = 1; i <= *p; i++) if (!p[i]) break;
1359
1360         if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1361         {
1362             ret = *p - i + 1;
1363             p += i;
1364         }
1365         else ret = i;
1366     }
1367     else
1368         ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1369
1370     if (!buffer) return ret;
1371
1372     if (ret > len)
1373     {
1374         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1375         return 0;
1376     }
1377
1378     if (lcflags & LOCALE_RETURN_NUMBER)
1379     {
1380         UINT number;
1381         WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1382         if (!tmp) return 0;
1383         memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1384         tmp[*p] = 0;
1385         number = strtolW( tmp, &end, 10 );
1386         if (!*end)
1387             memcpy( buffer, &number, sizeof(number) );
1388         else  /* invalid number */
1389         {
1390             SetLastError( ERROR_INVALID_FLAGS );
1391             ret = 0;
1392         }
1393         HeapFree( GetProcessHeap(), 0, tmp );
1394
1395         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1396                lcid, lctype, buffer, len, number );
1397     }
1398     else
1399     {
1400         memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1401         if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1402
1403         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1404                lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1405     }
1406     return ret;
1407 }
1408
1409
1410 /******************************************************************************
1411  *              SetLocaleInfoA  [KERNEL32.@]
1412  *
1413  * Set information about an aspect of a locale.
1414  *
1415  * PARAMS
1416  *  lcid   [I] LCID of the locale
1417  *  lctype [I] LCTYPE_ flags from "winnls.h"
1418  *  data   [I] Information to set
1419  *
1420  * RETURNS
1421  *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
1422  *           whenever it is called without LOCALE_NOUSEROVERRIDE.
1423  *  Failure: FALSE. Use GetLastError() to determine the cause.
1424  *
1425  * NOTES
1426  *  - Values are only be set for the current user locale; the system locale
1427  *  settings cannot be changed.
1428  *  - Any settings changed by this call are lost when the locale is changed by
1429  *  the control panel (in Wine, this happens every time you change LANG).
1430  *  - The native implementation of this function does not check that lcid matches
1431  *  the current user locale, and simply sets the new values. Wine warns you in
1432  *  this case, but behaves the same.
1433  */
1434 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1435 {
1436     UINT codepage = CP_ACP;
1437     WCHAR *strW;
1438     DWORD len;
1439     BOOL ret;
1440
1441     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1442
1443     if (!data)
1444     {
1445         SetLastError( ERROR_INVALID_PARAMETER );
1446         return FALSE;
1447     }
1448     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1449     if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1450     {
1451         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1452         return FALSE;
1453     }
1454     MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1455     ret = SetLocaleInfoW( lcid, lctype, strW );
1456     HeapFree( GetProcessHeap(), 0, strW );
1457     return ret;
1458 }
1459
1460
1461 /******************************************************************************
1462  *              SetLocaleInfoW  (KERNEL32.@)
1463  *
1464  * See SetLocaleInfoA.
1465  */
1466 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1467 {
1468     const WCHAR *value;
1469     static const WCHAR intlW[] = {'i','n','t','l',0 };
1470     UNICODE_STRING valueW;
1471     NTSTATUS status;
1472     HANDLE hkey;
1473
1474     lctype &= 0xffff;
1475     value = get_locale_value_name( lctype );
1476
1477     if (!data || !value)
1478     {
1479         SetLastError( ERROR_INVALID_PARAMETER );
1480         return FALSE;
1481     }
1482
1483     if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1484     {
1485         SetLastError( ERROR_INVALID_FLAGS );
1486         return FALSE;
1487     }
1488
1489     TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1490
1491     /* FIXME: should check that data to set is sane */
1492
1493     /* FIXME: profile functions should map to registry */
1494     WriteProfileStringW( intlW, value, data );
1495
1496     if (!(hkey = create_registry_key())) return FALSE;
1497     RtlInitUnicodeString( &valueW, value );
1498     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1499
1500     if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1501     {
1502       /* Set I-value from S value */
1503       WCHAR *lpD, *lpM, *lpY;
1504       WCHAR szBuff[2];
1505
1506       lpD = strrchrW(data, 'd');
1507       lpM = strrchrW(data, 'M');
1508       lpY = strrchrW(data, 'y');
1509
1510       if (lpD <= lpM)
1511       {
1512         szBuff[0] = '1'; /* D-M-Y */
1513       }
1514       else
1515       {
1516         if (lpY <= lpM)
1517           szBuff[0] = '2'; /* Y-M-D */
1518         else
1519           szBuff[0] = '0'; /* M-D-Y */
1520       }
1521
1522       szBuff[1] = '\0';
1523
1524       if (lctype == LOCALE_SSHORTDATE)
1525         lctype = LOCALE_IDATE;
1526       else
1527         lctype = LOCALE_ILDATE;
1528
1529       value = get_locale_value_name( lctype );
1530
1531       WriteProfileStringW( intlW, value, szBuff );
1532
1533       RtlInitUnicodeString( &valueW, value );
1534       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1535     }
1536
1537     NtClose( hkey );
1538
1539     if (status) SetLastError( RtlNtStatusToDosError(status) );
1540     return !status;
1541 }
1542
1543
1544 /******************************************************************************
1545  *              GetACP   (KERNEL32.@)
1546  *
1547  * Get the current Ansi code page Id for the system.
1548  *
1549  * PARAMS
1550  *  None.
1551  *
1552  * RETURNS
1553  *    The current Ansi code page identifier for the system.
1554  */
1555 UINT WINAPI GetACP(void)
1556 {
1557     assert( ansi_cptable );
1558     return ansi_cptable->info.codepage;
1559 }
1560
1561
1562 /******************************************************************************
1563  *              SetCPGlobal   (KERNEL32.@)
1564  *
1565  * Set the current Ansi code page Id for the system.
1566  *
1567  * PARAMS
1568  *    acp [I] code page ID to be the new ACP.
1569  *
1570  * RETURNS
1571  *    The previous ACP.
1572  */
1573 UINT WINAPI SetCPGlobal( UINT acp )
1574 {
1575     UINT ret = GetACP();
1576     const union cptable *new_cptable = wine_cp_get_table( acp );
1577
1578     if (new_cptable) ansi_cptable = new_cptable;
1579     return ret;
1580 }
1581
1582
1583 /***********************************************************************
1584  *              GetOEMCP   (KERNEL32.@)
1585  *
1586  * Get the current OEM code page Id for the system.
1587  *
1588  * PARAMS
1589  *  None.
1590  *
1591  * RETURNS
1592  *    The current OEM code page identifier for the system.
1593  */
1594 UINT WINAPI GetOEMCP(void)
1595 {
1596     assert( oem_cptable );
1597     return oem_cptable->info.codepage;
1598 }
1599
1600
1601 /***********************************************************************
1602  *           IsValidCodePage   (KERNEL32.@)
1603  *
1604  * Determine if a given code page identifier is valid.
1605  *
1606  * PARAMS
1607  *  codepage [I] Code page Id to verify.
1608  *
1609  * RETURNS
1610  *  TRUE, If codepage is valid and available on the system,
1611  *  FALSE otherwise.
1612  */
1613 BOOL WINAPI IsValidCodePage( UINT codepage )
1614 {
1615     switch(codepage) {
1616     case CP_UTF7:
1617     case CP_UTF8:
1618         return TRUE;
1619     default:
1620         return wine_cp_get_table( codepage ) != NULL;
1621     }
1622 }
1623
1624
1625 /***********************************************************************
1626  *           IsDBCSLeadByteEx   (KERNEL32.@)
1627  *
1628  * Determine if a character is a lead byte in a given code page.
1629  *
1630  * PARAMS
1631  *  codepage [I] Code page for the test.
1632  *  testchar [I] Character to test
1633  *
1634  * RETURNS
1635  *  TRUE, if testchar is a lead byte in codepage,
1636  *  FALSE otherwise.
1637  */
1638 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1639 {
1640     const union cptable *table = get_codepage_table( codepage );
1641     return table && wine_is_dbcs_leadbyte( table, testchar );
1642 }
1643
1644
1645 /***********************************************************************
1646  *           IsDBCSLeadByte   (KERNEL32.@)
1647  *           IsDBCSLeadByte   (KERNEL.207)
1648  *
1649  * Determine if a character is a lead byte.
1650  *
1651  * PARAMS
1652  *  testchar [I] Character to test
1653  *
1654  * RETURNS
1655  *  TRUE, if testchar is a lead byte in the ANSI code page,
1656  *  FALSE otherwise.
1657  */
1658 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1659 {
1660     if (!ansi_cptable) return FALSE;
1661     return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1662 }
1663
1664
1665 /***********************************************************************
1666  *           GetCPInfo   (KERNEL32.@)
1667  *
1668  * Get information about a code page.
1669  *
1670  * PARAMS
1671  *  codepage [I] Code page number
1672  *  cpinfo   [O] Destination for code page information
1673  *
1674  * RETURNS
1675  *  Success: TRUE. cpinfo is updated with the information about codepage.
1676  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1677  */
1678 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1679 {
1680     const union cptable *table;
1681
1682     if (!cpinfo)
1683     {
1684         SetLastError( ERROR_INVALID_PARAMETER );
1685         return FALSE;
1686     }
1687
1688     if (!(table = get_codepage_table( codepage )))
1689     {
1690         switch(codepage)
1691         {
1692             case CP_UTF7:
1693             case CP_UTF8:
1694                 cpinfo->DefaultChar[0] = 0x3f;
1695                 cpinfo->DefaultChar[1] = 0;
1696                 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1697                 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1698                 return TRUE;
1699         }
1700
1701         SetLastError( ERROR_INVALID_PARAMETER );
1702         return FALSE;
1703     }
1704     if (table->info.def_char & 0xff00)
1705     {
1706         cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1707         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1708     }
1709     else
1710     {
1711         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1712         cpinfo->DefaultChar[1] = 0;
1713     }
1714     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1715         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1716     else
1717         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1718
1719     return TRUE;
1720 }
1721
1722 /***********************************************************************
1723  *           GetCPInfoExA   (KERNEL32.@)
1724  *
1725  * Get extended information about a code page.
1726  *
1727  * PARAMS
1728  *  codepage [I] Code page number
1729  *  dwFlags  [I] Reserved, must to 0.
1730  *  cpinfo   [O] Destination for code page information
1731  *
1732  * RETURNS
1733  *  Success: TRUE. cpinfo is updated with the information about codepage.
1734  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1735  */
1736 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1737 {
1738     CPINFOEXW cpinfoW;
1739
1740     if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1741       return FALSE;
1742
1743     /* the layout is the same except for CodePageName */
1744     memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1745     WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1746     return TRUE;
1747 }
1748
1749 /***********************************************************************
1750  *           GetCPInfoExW   (KERNEL32.@)
1751  *
1752  * Unicode version of GetCPInfoExA.
1753  */
1754 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1755 {
1756     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1757       return FALSE;
1758
1759     switch(codepage)
1760     {
1761         case CP_UTF7:
1762         {
1763             static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1764
1765             cpinfo->CodePage = CP_UTF7;
1766             cpinfo->UnicodeDefaultChar = 0x3f;
1767             strcpyW(cpinfo->CodePageName, utf7);
1768             break;
1769         }
1770
1771         case CP_UTF8:
1772         {
1773             static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1774
1775             cpinfo->CodePage = CP_UTF8;
1776             cpinfo->UnicodeDefaultChar = 0x3f;
1777             strcpyW(cpinfo->CodePageName, utf8);
1778             break;
1779         }
1780
1781         default:
1782         {
1783             const union cptable *table = get_codepage_table( codepage );
1784
1785             cpinfo->CodePage = table->info.codepage;
1786             cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1787             MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1788                                  sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1789             break;
1790         }
1791     }
1792     return TRUE;
1793 }
1794
1795 /***********************************************************************
1796  *              EnumSystemCodePagesA   (KERNEL32.@)
1797  *
1798  * Call a user defined function for every code page installed on the system.
1799  *
1800  * PARAMS
1801  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1802  *   flags            [I] Reserved, set to 0.
1803  *
1804  * RETURNS
1805  *  TRUE, If all code pages have been enumerated, or
1806  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1807  */
1808 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1809 {
1810     const union cptable *table;
1811     char buffer[10];
1812     int index = 0;
1813
1814     for (;;)
1815     {
1816         if (!(table = wine_cp_enum_table( index++ ))) break;
1817         sprintf( buffer, "%d", table->info.codepage );
1818         if (!lpfnCodePageEnum( buffer )) break;
1819     }
1820     return TRUE;
1821 }
1822
1823
1824 /***********************************************************************
1825  *              EnumSystemCodePagesW   (KERNEL32.@)
1826  *
1827  * See EnumSystemCodePagesA.
1828  */
1829 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1830 {
1831     const union cptable *table;
1832     WCHAR buffer[10], *p;
1833     int page, index = 0;
1834
1835     for (;;)
1836     {
1837         if (!(table = wine_cp_enum_table( index++ ))) break;
1838         p = buffer + sizeof(buffer)/sizeof(WCHAR);
1839         *--p = 0;
1840         page = table->info.codepage;
1841         do
1842         {
1843             *--p = '0' + (page % 10);
1844             page /= 10;
1845         } while( page );
1846         if (!lpfnCodePageEnum( p )) break;
1847     }
1848     return TRUE;
1849 }
1850
1851
1852 /***********************************************************************
1853  *              MultiByteToWideChar   (KERNEL32.@)
1854  *
1855  * Convert a multibyte character string into a Unicode string.
1856  *
1857  * PARAMS
1858  *   page   [I] Codepage character set to convert from
1859  *   flags  [I] Character mapping flags
1860  *   src    [I] Source string buffer
1861  *   srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1862  *   dst    [O] Destination buffer
1863  *   dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1864  *
1865  * RETURNS
1866  *   Success: If dstlen > 0, the number of characters written to dst.
1867  *            If dstlen == 0, the number of characters needed to perform the
1868  *            conversion. In both cases the count includes the terminating NUL.
1869  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1870  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1871  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
1872  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1873  *            possible for src.
1874  */
1875 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1876                                 LPWSTR dst, INT dstlen )
1877 {
1878     const union cptable *table;
1879     int ret;
1880
1881     if (!src || (!dst && dstlen))
1882     {
1883         SetLastError( ERROR_INVALID_PARAMETER );
1884         return 0;
1885     }
1886
1887     if (srclen < 0) srclen = strlen(src) + 1;
1888
1889     switch(page)
1890     {
1891     case CP_SYMBOL:
1892         if( flags)
1893         {
1894             SetLastError( ERROR_INVALID_PARAMETER );
1895             return 0;
1896         }
1897         ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1898         break;
1899     case CP_UTF7:
1900         FIXME("UTF-7 not supported\n");
1901         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1902         return 0;
1903     case CP_UNIXCP:
1904         if (unix_cptable)
1905         {
1906             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1907             break;
1908         }
1909 #ifdef __APPLE__
1910         flags |= MB_COMPOSITE;  /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1911 #endif
1912         /* fall through */
1913     case CP_UTF8:
1914         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1915         break;
1916     default:
1917         if (!(table = get_codepage_table( page )))
1918         {
1919             SetLastError( ERROR_INVALID_PARAMETER );
1920             return 0;
1921         }
1922         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1923         break;
1924     }
1925
1926     if (ret < 0)
1927     {
1928         switch(ret)
1929         {
1930         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1931         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1932         }
1933         ret = 0;
1934     }
1935     TRACE("cp %d %s -> %s, ret = %d\n",
1936           page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1937     return ret;
1938 }
1939
1940
1941 /***********************************************************************
1942  *              WideCharToMultiByte   (KERNEL32.@)
1943  *
1944  * Convert a Unicode character string into a multibyte string.
1945  *
1946  * PARAMS
1947  *   page    [I] Code page character set to convert to
1948  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
1949  *   src     [I] Source string buffer
1950  *   srclen  [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1951  *   dst     [O] Destination buffer
1952  *   dstlen  [I] Length of dst (in bytes), or 0 to compute the required length
1953  *   defchar [I] Default character to use for conversion if no exact
1954  *                  conversion can be made
1955  *   used    [O] Set if default character was used in the conversion
1956  *
1957  * RETURNS
1958  *   Success: If dstlen > 0, the number of characters written to dst.
1959  *            If dstlen == 0, number of characters needed to perform the
1960  *            conversion. In both cases the count includes the terminating NUL.
1961  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1962  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1963  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1964  *            parameter was given.
1965  */
1966 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1967                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1968 {
1969     const union cptable *table;
1970     int ret, used_tmp;
1971
1972     if (!src || (!dst && dstlen))
1973     {
1974         SetLastError( ERROR_INVALID_PARAMETER );
1975         return 0;
1976     }
1977
1978     if (srclen < 0) srclen = strlenW(src) + 1;
1979
1980     switch(page)
1981     {
1982     case CP_SYMBOL:
1983         if( flags || defchar || used)
1984         {
1985             SetLastError( ERROR_INVALID_PARAMETER );
1986             return 0;
1987         }
1988         ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
1989         break;
1990     case CP_UTF7:
1991         FIXME("UTF-7 not supported\n");
1992         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1993         return 0;
1994     case CP_UNIXCP:
1995         if (unix_cptable)
1996         {
1997             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
1998                                     defchar, used ? &used_tmp : NULL );
1999             break;
2000         }
2001         /* fall through */
2002     case CP_UTF8:
2003         if (used) *used = FALSE;  /* all chars are valid for UTF-8 */
2004         ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2005         break;
2006     default:
2007         if (!(table = get_codepage_table( page )))
2008         {
2009             SetLastError( ERROR_INVALID_PARAMETER );
2010             return 0;
2011         }
2012         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2013                                 defchar, used ? &used_tmp : NULL );
2014         if (used) *used = used_tmp;
2015         break;
2016     }
2017
2018     if (ret < 0)
2019     {
2020         switch(ret)
2021         {
2022         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2023         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2024         }
2025         ret = 0;
2026     }
2027     TRACE("cp %d %s -> %s, ret = %d\n",
2028           page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2029     return ret;
2030 }
2031
2032
2033 /***********************************************************************
2034  *           GetThreadLocale    (KERNEL32.@)
2035  *
2036  * Get the current threads locale.
2037  *
2038  * PARAMS
2039  *  None.
2040  *
2041  * RETURNS
2042  *  The LCID currently associated with the calling thread.
2043  */
2044 LCID WINAPI GetThreadLocale(void)
2045 {
2046     LCID ret = NtCurrentTeb()->CurrentLocale;
2047     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2048     return ret;
2049 }
2050
2051 /**********************************************************************
2052  *           SetThreadLocale    (KERNEL32.@)
2053  *
2054  * Set the current threads locale.
2055  *
2056  * PARAMS
2057  *  lcid [I] LCID of the locale to set
2058  *
2059  * RETURNS
2060  *  Success: TRUE. The threads locale is set to lcid.
2061  *  Failure: FALSE. Use GetLastError() to determine the cause.
2062  */
2063 BOOL WINAPI SetThreadLocale( LCID lcid )
2064 {
2065     TRACE("(0x%04X)\n", lcid);
2066
2067     lcid = ConvertDefaultLocale(lcid);
2068
2069     if (lcid != GetThreadLocale())
2070     {
2071         if (!IsValidLocale(lcid, LCID_SUPPORTED))
2072         {
2073             SetLastError(ERROR_INVALID_PARAMETER);
2074             return FALSE;
2075         }
2076
2077         NtCurrentTeb()->CurrentLocale = lcid;
2078     }
2079     return TRUE;
2080 }
2081
2082 /**********************************************************************
2083  *           SetThreadUILanguage    (KERNEL32.@)
2084  *
2085  * Set the current threads UI language.
2086  *
2087  * PARAMS
2088  *  langid [I] LANGID of the language to set, or 0 to use
2089  *             the available language which is best supported
2090  *             for console applications
2091  *
2092  * RETURNS
2093  *  Success: The return value is the same as the input value.
2094  *  Failure: The return value differs from the input value.
2095  *           Use GetLastError() to determine the cause.
2096  */
2097 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2098 {
2099     TRACE("(0x%04x) stub - returning success\n", langid);
2100     return langid;
2101 }
2102
2103 /******************************************************************************
2104  *              ConvertDefaultLocale (KERNEL32.@)
2105  *
2106  * Convert a default locale identifier into a real identifier.
2107  *
2108  * PARAMS
2109  *  lcid [I] LCID identifier of the locale to convert
2110  *
2111  * RETURNS
2112  *  lcid unchanged, if not a default locale or its sublanguage is
2113  *   not SUBLANG_NEUTRAL.
2114  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2115  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2116  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2117  */
2118 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2119 {
2120     LANGID langid;
2121
2122     switch (lcid)
2123     {
2124     case LOCALE_SYSTEM_DEFAULT:
2125         lcid = GetSystemDefaultLCID();
2126         break;
2127     case LOCALE_USER_DEFAULT:
2128     case LOCALE_NEUTRAL:
2129         lcid = GetUserDefaultLCID();
2130         break;
2131     default:
2132         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2133         langid = LANGIDFROMLCID(lcid);
2134         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2135         {
2136           langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2137           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2138         }
2139     }
2140     return lcid;
2141 }
2142
2143
2144 /******************************************************************************
2145  *           IsValidLocale   (KERNEL32.@)
2146  *
2147  * Determine if a locale is valid.
2148  *
2149  * PARAMS
2150  *  lcid  [I] LCID of the locale to check
2151  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2152  *
2153  * RETURNS
2154  *  TRUE,  if lcid is valid,
2155  *  FALSE, otherwise.
2156  *
2157  * NOTES
2158  *  Wine does not currently make the distinction between supported and installed. All
2159  *  languages supported are installed by default.
2160  */
2161 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2162 {
2163     /* check if language is registered in the kernel32 resources */
2164     return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2165                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2166 }
2167
2168
2169 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2170                                        LPCSTR name, WORD LangID, LONG_PTR lParam )
2171 {
2172     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2173     char buf[20];
2174
2175     sprintf(buf, "%08x", (UINT)LangID);
2176     return lpfnLocaleEnum( buf );
2177 }
2178
2179 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2180                                        LPCWSTR name, WORD LangID, LONG_PTR lParam )
2181 {
2182     static const WCHAR formatW[] = {'%','0','8','x',0};
2183     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2184     WCHAR buf[20];
2185     sprintfW( buf, formatW, (UINT)LangID );
2186     return lpfnLocaleEnum( buf );
2187 }
2188
2189 /******************************************************************************
2190  *           EnumSystemLocalesA  (KERNEL32.@)
2191  *
2192  * Call a users function for each locale available on the system.
2193  *
2194  * PARAMS
2195  *  lpfnLocaleEnum [I] Callback function to call for each locale
2196  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2197  *
2198  * RETURNS
2199  *  Success: TRUE.
2200  *  Failure: FALSE. Use GetLastError() to determine the cause.
2201  */
2202 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2203 {
2204     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2205     EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2206                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2207                             (LONG_PTR)lpfnLocaleEnum);
2208     return TRUE;
2209 }
2210
2211
2212 /******************************************************************************
2213  *           EnumSystemLocalesW  (KERNEL32.@)
2214  *
2215  * See EnumSystemLocalesA.
2216  */
2217 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2218 {
2219     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2220     EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2221                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2222                             (LONG_PTR)lpfnLocaleEnum);
2223     return TRUE;
2224 }
2225
2226
2227 struct enum_locale_ex_data
2228 {
2229     LOCALE_ENUMPROCEX proc;
2230     DWORD             flags;
2231     LPARAM            lparam;
2232 };
2233
2234 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2235                                           LPCWSTR name, WORD lang, LONG_PTR lparam )
2236 {
2237     struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2238     WCHAR buffer[256];
2239     DWORD neutral;
2240     unsigned int flags;
2241
2242     GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2243                     buffer, sizeof(buffer) / sizeof(WCHAR) );
2244     if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2245                          LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2246                          (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2247         neutral = 0;
2248     flags = LOCALE_WINDOWS;
2249     flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2250     if (data->flags && ~(data->flags & flags)) return TRUE;
2251     return data->proc( buffer, flags, data->lparam );
2252 }
2253
2254 /******************************************************************************
2255  *           EnumSystemLocalesEx  (KERNEL32.@)
2256  */
2257 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2258 {
2259     struct enum_locale_ex_data data;
2260
2261     if (reserved)
2262     {
2263         SetLastError( ERROR_INVALID_PARAMETER );
2264         return FALSE;
2265     }
2266     data.proc   = proc;
2267     data.flags  = flags;
2268     data.lparam = lparam;
2269     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2270                             (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2271                             enum_locale_ex_proc, (LONG_PTR)&data );
2272     return TRUE;
2273 }
2274
2275
2276 /***********************************************************************
2277  *           VerLanguageNameA  (KERNEL32.@)
2278  *
2279  * Get the name of a language.
2280  *
2281  * PARAMS
2282  *  wLang  [I] LANGID of the language
2283  *  szLang [O] Destination for the language name
2284  *
2285  * RETURNS
2286  *  Success: The size of the language name. If szLang is non-NULL, it is filled
2287  *           with the name.
2288  *  Failure: 0. Use GetLastError() to determine the cause.
2289  *
2290  */
2291 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2292 {
2293     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2294 }
2295
2296
2297 /***********************************************************************
2298  *           VerLanguageNameW  (KERNEL32.@)
2299  *
2300  * See VerLanguageNameA.
2301  */
2302 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2303 {
2304     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2305 }
2306
2307
2308 /******************************************************************************
2309  *           GetStringTypeW    (KERNEL32.@)
2310  *
2311  * See GetStringTypeA.
2312  */
2313 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2314 {
2315     static const unsigned char type2_map[16] =
2316     {
2317         C2_NOTAPPLICABLE,      /* unassigned */
2318         C2_LEFTTORIGHT,        /* L */
2319         C2_RIGHTTOLEFT,        /* R */
2320         C2_EUROPENUMBER,       /* EN */
2321         C2_EUROPESEPARATOR,    /* ES */
2322         C2_EUROPETERMINATOR,   /* ET */
2323         C2_ARABICNUMBER,       /* AN */
2324         C2_COMMONSEPARATOR,    /* CS */
2325         C2_BLOCKSEPARATOR,     /* B */
2326         C2_SEGMENTSEPARATOR,   /* S */
2327         C2_WHITESPACE,         /* WS */
2328         C2_OTHERNEUTRAL,       /* ON */
2329         C2_RIGHTTOLEFT,        /* AL */
2330         C2_NOTAPPLICABLE,      /* NSM */
2331         C2_NOTAPPLICABLE,      /* BN */
2332         C2_OTHERNEUTRAL        /* LRE, LRO, RLE, RLO, PDF */
2333     };
2334
2335     if (count == -1) count = strlenW(src) + 1;
2336     switch(type)
2337     {
2338     case CT_CTYPE1:
2339         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2340         break;
2341     case CT_CTYPE2:
2342         while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2343         break;
2344     case CT_CTYPE3:
2345     {
2346         WARN("CT_CTYPE3: semi-stub.\n");
2347         while (count--)
2348         {
2349             int c = *src;
2350             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2351
2352             type1 = get_char_typeW( *src++ ) & 0xfff;
2353             /* try to construct type3 from type1 */
2354             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2355             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2356             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2357             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2358             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2359             if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2360             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2361
2362             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2363             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2364             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2365             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2366             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2367             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2368             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2369             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2370
2371             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2372             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2373             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2374             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2375             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2376             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2377             *chartype++ = type3;
2378         }
2379         break;
2380     }
2381     default:
2382         SetLastError( ERROR_INVALID_PARAMETER );
2383         return FALSE;
2384     }
2385     return TRUE;
2386 }
2387
2388
2389 /******************************************************************************
2390  *           GetStringTypeExW    (KERNEL32.@)
2391  *
2392  * See GetStringTypeExA.
2393  */
2394 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2395 {
2396     /* locale is ignored for Unicode */
2397     return GetStringTypeW( type, src, count, chartype );
2398 }
2399
2400
2401 /******************************************************************************
2402  *           GetStringTypeA    (KERNEL32.@)
2403  *
2404  * Get characteristics of the characters making up a string.
2405  *
2406  * PARAMS
2407  *  locale   [I] Locale Id for the string
2408  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2409  *  src      [I] String to analyse
2410  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
2411  *  chartype [O] Destination for the calculated characteristics
2412  *
2413  * RETURNS
2414  *  Success: TRUE. chartype is filled with the requested characteristics of each char
2415  *           in src.
2416  *  Failure: FALSE. Use GetLastError() to determine the cause.
2417  */
2418 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2419 {
2420     UINT cp;
2421     INT countW;
2422     LPWSTR srcW;
2423     BOOL ret = FALSE;
2424
2425     if(count == -1) count = strlen(src) + 1;
2426
2427     if (!(cp = get_lcid_codepage( locale )))
2428     {
2429         FIXME("For locale %04x using current ANSI code page\n", locale);
2430         cp = GetACP();
2431     }
2432
2433     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2434     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2435     {
2436         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2437     /*
2438      * NOTE: the target buffer has 1 word for each CHARACTER in the source
2439      * string, with multibyte characters there maybe be more bytes in count
2440      * than character space in the buffer!
2441      */
2442         ret = GetStringTypeW(type, srcW, countW, chartype);
2443         HeapFree(GetProcessHeap(), 0, srcW);
2444     }
2445     return ret;
2446 }
2447
2448 /******************************************************************************
2449  *           GetStringTypeExA    (KERNEL32.@)
2450  *
2451  * Get characteristics of the characters making up a string.
2452  *
2453  * PARAMS
2454  *  locale   [I] Locale Id for the string
2455  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2456  *  src      [I] String to analyse
2457  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
2458  *  chartype [O] Destination for the calculated characteristics
2459  *
2460  * RETURNS
2461  *  Success: TRUE. chartype is filled with the requested characteristics of each char
2462  *           in src.
2463  *  Failure: FALSE. Use GetLastError() to determine the cause.
2464  */
2465 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2466 {
2467     return GetStringTypeA(locale, type, src, count, chartype);
2468 }
2469
2470
2471 /*************************************************************************
2472  *           LCMapStringW    (KERNEL32.@)
2473  *
2474  * See LCMapStringA.
2475  */
2476 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2477                         LPWSTR dst, INT dstlen)
2478 {
2479     LPWSTR dst_ptr;
2480
2481     if (!src || !srclen || dstlen < 0)
2482     {
2483         SetLastError(ERROR_INVALID_PARAMETER);
2484         return 0;
2485     }
2486
2487     /* mutually exclusive flags */
2488     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2489         (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2490         (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2491         (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2492     {
2493         SetLastError(ERROR_INVALID_FLAGS);
2494         return 0;
2495     }
2496
2497     if (!dstlen) dst = NULL;
2498
2499     lcid = ConvertDefaultLocale(lcid);
2500
2501     if (flags & LCMAP_SORTKEY)
2502     {
2503         INT ret;
2504         if (src == dst)
2505         {
2506             SetLastError(ERROR_INVALID_FLAGS);
2507             return 0;
2508         }
2509
2510         if (srclen < 0) srclen = strlenW(src);
2511
2512         TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2513               lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2514
2515         ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2516         if (ret == 0)
2517             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2518         else
2519             ret++;
2520         return ret;
2521     }
2522
2523     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2524     if (flags & SORT_STRINGSORT)
2525     {
2526         SetLastError(ERROR_INVALID_FLAGS);
2527         return 0;
2528     }
2529
2530     if (srclen < 0) srclen = strlenW(src) + 1;
2531
2532     TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2533           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2534
2535     if (!dst) /* return required string length */
2536     {
2537         INT len;
2538
2539         for (len = 0; srclen; src++, srclen--)
2540         {
2541             WCHAR wch = *src;
2542             /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2543              * and skips white space and punctuation characters for
2544              * NORM_IGNORESYMBOLS.
2545              */
2546             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2547                 continue;
2548             len++;
2549         }
2550         return len;
2551     }
2552
2553     if (flags & LCMAP_UPPERCASE)
2554     {
2555         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2556         {
2557             WCHAR wch = *src;
2558             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2559                 continue;
2560             *dst_ptr++ = toupperW(wch);
2561             dstlen--;
2562         }
2563     }
2564     else if (flags & LCMAP_LOWERCASE)
2565     {
2566         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2567         {
2568             WCHAR wch = *src;
2569             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2570                 continue;
2571             *dst_ptr++ = tolowerW(wch);
2572             dstlen--;
2573         }
2574     }
2575     else
2576     {
2577         if (src == dst)
2578         {
2579             SetLastError(ERROR_INVALID_FLAGS);
2580             return 0;
2581         }
2582         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2583         {
2584             WCHAR wch = *src;
2585             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2586                 continue;
2587             *dst_ptr++ = wch;
2588             dstlen--;
2589         }
2590     }
2591
2592     if (srclen)
2593     {
2594         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2595         return 0;
2596     }
2597
2598     return dst_ptr - dst;
2599 }
2600
2601 /*************************************************************************
2602  *           LCMapStringA    (KERNEL32.@)
2603  *
2604  * Map characters in a locale sensitive string.
2605  *
2606  * PARAMS
2607  *  lcid   [I] LCID for the conversion.
2608  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2609  *  src    [I] String to map
2610  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
2611  *  dst    [O] Destination for mapped string
2612  *  dstlen [I] Length of dst in characters
2613  *
2614  * RETURNS
2615  *  Success: The length of the mapped string in dst, including the NUL terminator.
2616  *  Failure: 0. Use GetLastError() to determine the cause.
2617  */
2618 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2619                         LPSTR dst, INT dstlen)
2620 {
2621     WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2622     LPWSTR srcW, dstW;
2623     INT ret = 0, srclenW, dstlenW;
2624     UINT locale_cp = CP_ACP;
2625
2626     if (!src || !srclen || dstlen < 0)
2627     {
2628         SetLastError(ERROR_INVALID_PARAMETER);
2629         return 0;
2630     }
2631
2632     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2633
2634     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2635     if (srclenW)
2636         srcW = bufW;
2637     else
2638     {
2639         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2640         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2641         if (!srcW)
2642         {
2643             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2644             return 0;
2645         }
2646         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2647     }
2648
2649     if (flags & LCMAP_SORTKEY)
2650     {
2651         if (src == dst)
2652         {
2653             SetLastError(ERROR_INVALID_FLAGS);
2654             goto map_string_exit;
2655         }
2656         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2657         if (ret == 0)
2658             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2659         else
2660             ret++;
2661         goto map_string_exit;
2662     }
2663
2664     if (flags & SORT_STRINGSORT)
2665     {
2666         SetLastError(ERROR_INVALID_FLAGS);
2667         goto map_string_exit;
2668     }
2669
2670     dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
2671     if (!dstlenW)
2672         goto map_string_exit;
2673
2674     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2675     if (!dstW)
2676     {
2677         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2678         goto map_string_exit;
2679     }
2680
2681     LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
2682     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2683     HeapFree(GetProcessHeap(), 0, dstW);
2684
2685 map_string_exit:
2686     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2687     return ret;
2688 }
2689
2690 /*************************************************************************
2691  *           FoldStringA    (KERNEL32.@)
2692  *
2693  * Map characters in a string.
2694  *
2695  * PARAMS
2696  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2697  *  src     [I] String to map
2698  *  srclen  [I] Length of src, or -1 if src is NUL terminated
2699  *  dst     [O] Destination for mapped string
2700  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
2701  *
2702  * RETURNS
2703  *  Success: The length of the string written to dst, including the terminating NUL. If
2704  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
2705  *           and dst may be NULL.
2706  *  Failure: 0. Use GetLastError() to determine the cause.
2707  */
2708 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2709                        LPSTR dst, INT dstlen)
2710 {
2711     INT ret = 0, srclenW = 0;
2712     WCHAR *srcW = NULL, *dstW = NULL;
2713
2714     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2715     {
2716         SetLastError(ERROR_INVALID_PARAMETER);
2717         return 0;
2718     }
2719
2720     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2721                                   src, srclen, NULL, 0);
2722     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2723
2724     if (!srcW)
2725     {
2726         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2727         goto FoldStringA_exit;
2728     }
2729
2730     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2731                         src, srclen, srcW, srclenW);
2732
2733     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2734
2735     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2736     if (ret && dstlen)
2737     {
2738         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2739
2740         if (!dstW)
2741         {
2742             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2743             goto FoldStringA_exit;
2744         }
2745
2746         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2747         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2748         {
2749             ret = 0;
2750             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2751         }
2752     }
2753
2754     HeapFree(GetProcessHeap(), 0, dstW);
2755
2756 FoldStringA_exit:
2757     HeapFree(GetProcessHeap(), 0, srcW);
2758     return ret;
2759 }
2760
2761 /*************************************************************************
2762  *           FoldStringW    (KERNEL32.@)
2763  *
2764  * See FoldStringA.
2765  */
2766 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2767                        LPWSTR dst, INT dstlen)
2768 {
2769     int ret;
2770
2771     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2772     {
2773     case 0:
2774         if (dwFlags)
2775           break;
2776         /* Fall through for dwFlags == 0 */
2777     case MAP_PRECOMPOSED|MAP_COMPOSITE:
2778     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2779     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2780         SetLastError(ERROR_INVALID_FLAGS);
2781         return 0;
2782     }
2783
2784     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2785     {
2786         SetLastError(ERROR_INVALID_PARAMETER);
2787         return 0;
2788     }
2789
2790     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2791     if (!ret)
2792         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2793     return ret;
2794 }
2795
2796 /******************************************************************************
2797  *           CompareStringW    (KERNEL32.@)
2798  *
2799  * See CompareStringA.
2800  */
2801 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2802                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2803 {
2804     INT ret;
2805
2806     if (!str1 || !str2)
2807     {
2808         SetLastError(ERROR_INVALID_PARAMETER);
2809         return 0;
2810     }
2811
2812     if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2813         SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2814     {
2815         SetLastError(ERROR_INVALID_FLAGS);
2816         return 0;
2817     }
2818
2819     /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2820     if (style & 0x10000000)
2821         WARN("Ignoring unknown style 0x10000000\n");
2822
2823     if (len1 < 0) len1 = strlenW(str1);
2824     if (len2 < 0) len2 = strlenW(str2);
2825
2826     ret = wine_compare_string(style, str1, len1, str2, len2);
2827
2828     if (ret) /* need to translate result */
2829         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2830     return CSTR_EQUAL;
2831 }
2832
2833 /******************************************************************************
2834  *           CompareStringA    (KERNEL32.@)
2835  *
2836  * Compare two locale sensitive strings.
2837  *
2838  * PARAMS
2839  *  lcid  [I] LCID for the comparison
2840  *  style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2841  *  str1  [I] First string to compare
2842  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
2843  *  str2  [I] Second string to compare
2844  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
2845  *
2846  * RETURNS
2847  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2848  *           str1 is less than, equal to or greater than str2 respectively.
2849  *  Failure: FALSE. Use GetLastError() to determine the cause.
2850  */
2851 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2852                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2853 {
2854     WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2855     WCHAR *buf2W = buf1W + 130;
2856     LPWSTR str1W, str2W;
2857     INT len1W, len2W, ret;
2858     UINT locale_cp = CP_ACP;
2859
2860     if (!str1 || !str2)
2861     {
2862         SetLastError(ERROR_INVALID_PARAMETER);
2863         return 0;
2864     }
2865     if (len1 < 0) len1 = strlen(str1);
2866     if (len2 < 0) len2 = strlen(str2);
2867
2868     if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2869
2870     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2871     if (len1W)
2872         str1W = buf1W;
2873     else
2874     {
2875         len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2876         str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2877         if (!str1W)
2878         {
2879             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2880             return 0;
2881         }
2882         MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2883     }
2884     len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2885     if (len2W)
2886         str2W = buf2W;
2887     else
2888     {
2889         len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2890         str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2891         if (!str2W)
2892         {
2893             if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2894             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2895             return 0;
2896         }
2897         MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2898     }
2899
2900     ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2901
2902     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2903     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2904     return ret;
2905 }
2906
2907 /*************************************************************************
2908  *           lstrcmp     (KERNEL32.@)
2909  *           lstrcmpA    (KERNEL32.@)
2910  *
2911  * Compare two strings using the current thread locale.
2912  *
2913  * PARAMS
2914  *  str1  [I] First string to compare
2915  *  str2  [I] Second string to compare
2916  *
2917  * RETURNS
2918  *  Success: A number less than, equal to or greater than 0 depending on whether
2919  *           str1 is less than, equal to or greater than str2 respectively.
2920  *  Failure: FALSE. Use GetLastError() to determine the cause.
2921  */
2922 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2923 {
2924     int ret;
2925     
2926     if ((str1 == NULL) && (str2 == NULL)) return 0;
2927     if (str1 == NULL) return -1;
2928     if (str2 == NULL) return 1;
2929
2930     ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2931     if (ret) ret -= 2;
2932     
2933     return ret;
2934 }
2935
2936 /*************************************************************************
2937  *           lstrcmpi     (KERNEL32.@)
2938  *           lstrcmpiA    (KERNEL32.@)
2939  *
2940  * Compare two strings using the current thread locale, ignoring case.
2941  *
2942  * PARAMS
2943  *  str1  [I] First string to compare
2944  *  str2  [I] Second string to compare
2945  *
2946  * RETURNS
2947  *  Success: A number less than, equal to or greater than 0 depending on whether
2948  *           str2 is less than, equal to or greater than str1 respectively.
2949  *  Failure: FALSE. Use GetLastError() to determine the cause.
2950  */
2951 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
2952 {
2953     int ret;
2954     
2955     if ((str1 == NULL) && (str2 == NULL)) return 0;
2956     if (str1 == NULL) return -1;
2957     if (str2 == NULL) return 1;
2958
2959     ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2960     if (ret) ret -= 2;
2961     
2962     return ret;
2963 }
2964
2965 /*************************************************************************
2966  *           lstrcmpW    (KERNEL32.@)
2967  *
2968  * See lstrcmpA.
2969  */
2970 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
2971 {
2972     int ret;
2973
2974     if ((str1 == NULL) && (str2 == NULL)) return 0;
2975     if (str1 == NULL) return -1;
2976     if (str2 == NULL) return 1;
2977
2978     ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
2979     if (ret) ret -= 2;
2980     
2981     return ret;
2982 }
2983
2984 /*************************************************************************
2985  *           lstrcmpiW    (KERNEL32.@)
2986  *
2987  * See lstrcmpiA.
2988  */
2989 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
2990 {
2991     int ret;
2992     
2993     if ((str1 == NULL) && (str2 == NULL)) return 0;
2994     if (str1 == NULL) return -1;
2995     if (str2 == NULL) return 1;
2996
2997     ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2998     if (ret) ret -= 2;
2999     
3000     return ret;
3001 }
3002
3003 /******************************************************************************
3004  *              LOCALE_Init
3005  */
3006 void LOCALE_Init(void)
3007 {
3008     extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3009                                              const union cptable *unix_cp );
3010
3011     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3012
3013 #ifdef __APPLE__
3014     /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3015     char user_locale[50];
3016
3017     CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3018     CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3019     CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3020     CFStringRef user_locale_string_ref;
3021
3022     if (user_locale_country_ref)
3023     {
3024         user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3025             user_locale_lang_ref, user_locale_country_ref);
3026     }
3027     else
3028     {
3029         user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3030             user_locale_lang_ref);
3031     }
3032
3033     CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3034
3035     unix_cp = CP_UTF8;  /* default to utf-8 even if we don't get a valid locale */
3036     setenv( "LANG", user_locale, 0 );
3037     TRACE( "setting locale to '%s'\n", user_locale );
3038 #endif /* __APPLE__ */
3039
3040     setlocale( LC_ALL, "" );
3041
3042     unix_cp = setup_unix_locales();
3043     if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3044
3045 #ifdef __APPLE__
3046     /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3047     if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3048     {
3049         /* Retrieve the preferred language as chosen in System Preferences. */
3050         /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3051            leave things be. */
3052         CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3053         CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3054         CFStringRef user_language_string_ref;
3055         if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3056             (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3057             !CFEqual(user_language_string_ref, user_locale_lang_ref))
3058         {
3059             struct locale_name locale_name;
3060             WCHAR buffer[128];
3061             CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3062             strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3063             parse_locale_name( buffer, &locale_name );
3064             lcid_LC_MESSAGES = locale_name.lcid;
3065             TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3066         }
3067         CFRelease( all_locales );
3068         if (preferred_locales)
3069             CFRelease( preferred_locales );
3070     }
3071
3072     CFRelease( user_locale_ref );
3073     CFRelease( user_locale_string_ref );
3074 #endif
3075
3076     NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3077     NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3078     NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3079
3080     ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3081     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3082                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3083     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3084                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3085     if (!unix_cp)
3086         GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3087                         (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3088
3089     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3090         ansi_cptable = wine_cp_get_table( 1252 );
3091     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3092         oem_cptable  = wine_cp_get_table( 437 );
3093     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3094         mac_cptable  = wine_cp_get_table( 10000 );
3095     if (unix_cp != CP_UTF8)
3096     {
3097         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3098             unix_cptable  = wine_cp_get_table( 28591 );
3099     }
3100
3101     __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3102
3103     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3104            ansi_cptable->info.codepage, oem_cptable->info.codepage,
3105            mac_cptable->info.codepage, unix_cp );
3106
3107     setlocale(LC_NUMERIC, "C");  /* FIXME: oleaut32 depends on this */
3108 }
3109
3110 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3111 {
3112     UNICODE_STRING keyName;
3113     OBJECT_ATTRIBUTES attr;
3114     HANDLE hkey;
3115
3116     RtlInitUnicodeString( &keyName, szKeyName );
3117     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3118
3119     if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3120         hkey = 0;
3121
3122     return hkey;
3123 }
3124
3125 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3126                               ULONG keyNameSize)
3127 {
3128     BYTE buffer[80];
3129     KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3130     DWORD dwLen;
3131
3132     if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3133                         sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3134         info->NameLength > keyNameSize)
3135     {
3136         return FALSE;
3137     }
3138
3139     TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3140
3141     memcpy( szKeyName, info->Name, info->NameLength);
3142     szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3143
3144     TRACE("returning %s\n", debugstr_w(szKeyName));
3145     return TRUE;
3146 }
3147
3148 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3149                              LPWSTR szValueName, ULONG valueNameSize,
3150                              LPWSTR szValueData, ULONG valueDataSize)
3151 {
3152     BYTE buffer[80];
3153     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3154     DWORD dwLen;
3155
3156     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3157         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3158         info->NameLength > valueNameSize ||
3159         info->DataLength > valueDataSize)
3160     {
3161         return FALSE;
3162     }
3163
3164     TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3165
3166     memcpy( szValueName, info->Name, info->NameLength);
3167     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3168     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3169     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3170
3171     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3172     return TRUE;
3173 }
3174
3175 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3176 {
3177     BYTE buffer[128];
3178     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3179     DWORD dwSize = sizeof(buffer);
3180     UNICODE_STRING valueName;
3181
3182     RtlInitUnicodeString( &valueName, szValueName );
3183
3184     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3185     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3186                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3187         info->DataLength == sizeof(DWORD))
3188     {
3189         memcpy(lpVal, info->Data, sizeof(DWORD));
3190         return TRUE;
3191     }
3192
3193     return FALSE;
3194 }
3195
3196 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3197 {
3198     LANGID  langId;
3199     LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3200     HRSRC   hResource;
3201     BOOL    bRet = FALSE;
3202
3203     /* FIXME: Is it correct to use the system default langid? */
3204     langId = GetSystemDefaultLangID();
3205
3206     if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3207         langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3208
3209     hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3210
3211     if (hResource)
3212     {
3213         HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3214
3215         if (hResDir)
3216         {
3217             ULONG   iResourceIndex = lgrpid & 0xf;
3218             LPCWSTR lpResEntry = LockResource( hResDir );
3219             ULONG   i;
3220
3221             for (i = 0; i < iResourceIndex; i++)
3222                 lpResEntry += *lpResEntry + 1;
3223
3224             if (*lpResEntry < nameSize)
3225             {
3226                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3227                 szName[*lpResEntry] = '\0';
3228                 bRet = TRUE;
3229             }
3230
3231         }
3232         FreeResource( hResource );
3233     }
3234     return bRet;
3235 }
3236
3237 /* Registry keys for NLS related information */
3238
3239 static const WCHAR szCountryListName[] = {
3240     'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3241     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3242     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3243     'T','e','l','e','p','h','o','n','y','\\',
3244     'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3245 };
3246
3247
3248 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3249 typedef struct
3250 {
3251   LANGUAGEGROUP_ENUMPROCA procA;
3252   LANGUAGEGROUP_ENUMPROCW procW;
3253   DWORD    dwFlags;
3254   LONG_PTR lParam;
3255 } ENUMLANGUAGEGROUP_CALLBACKS;
3256
3257 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3258 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3259 {
3260     WCHAR szNumber[10], szValue[4];
3261     HANDLE hKey;
3262     BOOL bContinue = TRUE;
3263     ULONG ulIndex = 0;
3264
3265     if (!lpProcs)
3266     {
3267         SetLastError(ERROR_INVALID_PARAMETER);
3268         return FALSE;
3269     }
3270
3271     switch (lpProcs->dwFlags)
3272     {
3273     case 0:
3274         /* Default to LGRPID_INSTALLED */
3275         lpProcs->dwFlags = LGRPID_INSTALLED;
3276         /* Fall through... */
3277     case LGRPID_INSTALLED:
3278     case LGRPID_SUPPORTED:
3279         break;
3280     default:
3281         SetLastError(ERROR_INVALID_FLAGS);
3282         return FALSE;
3283     }
3284
3285     hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3286
3287     if (!hKey)
3288         FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3289
3290     while (bContinue)
3291     {
3292         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3293                               szValue, sizeof(szValue) ))
3294         {
3295             BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
3296             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3297
3298             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3299                    bInstalled ? "" : "not ");
3300
3301             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3302             {
3303                 WCHAR szGrpName[48];
3304
3305                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3306                     szGrpName[0] = '\0';
3307
3308                 if (lpProcs->procW)
3309                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3310                                                 lpProcs->lParam );
3311                 else
3312                 {
3313                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3314                     char szGrpNameA[48];
3315
3316                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3317                      *        or whether the language names are ever localised. Assume CP_ACP.
3318                      */
3319
3320                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3321                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3322
3323                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3324                                                 lpProcs->lParam );
3325                 }
3326             }
3327
3328             ulIndex++;
3329         }
3330         else
3331             bContinue = FALSE;
3332
3333         if (!bContinue)
3334             break;
3335     }
3336
3337     if (hKey)
3338         NtClose( hKey );
3339
3340     return TRUE;
3341 }
3342
3343 /******************************************************************************
3344  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
3345  *
3346  * Call a users function for each language group available on the system.
3347  *
3348  * PARAMS
3349  *  pLangGrpEnumProc [I] Callback function to call for each language group
3350  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3351  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
3352  *
3353  * RETURNS
3354  *  Success: TRUE.
3355  *  Failure: FALSE. Use GetLastError() to determine the cause.
3356  */
3357 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3358                                       DWORD dwFlags, LONG_PTR lParam)
3359 {
3360     ENUMLANGUAGEGROUP_CALLBACKS procs;
3361
3362     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3363
3364     procs.procA = pLangGrpEnumProc;
3365     procs.procW = NULL;
3366     procs.dwFlags = dwFlags;
3367     procs.lParam = lParam;
3368
3369     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3370 }
3371
3372 /******************************************************************************
3373  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
3374  *
3375  * See EnumSystemLanguageGroupsA.
3376  */
3377 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3378                                       DWORD dwFlags, LONG_PTR lParam)
3379 {
3380     ENUMLANGUAGEGROUP_CALLBACKS procs;
3381
3382     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3383
3384     procs.procA = NULL;
3385     procs.procW = pLangGrpEnumProc;
3386     procs.dwFlags = dwFlags;
3387     procs.lParam = lParam;
3388
3389     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3390 }
3391
3392 /******************************************************************************
3393  *           IsValidLanguageGroup    (KERNEL32.@)
3394  *
3395  * Determine if a language group is supported and/or installed.
3396  *
3397  * PARAMS
3398  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
3399  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3400  *
3401  * RETURNS
3402  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3403  *  FALSE otherwise.
3404  */
3405 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3406 {
3407     static const WCHAR szFormat[] = { '%','x','\0' };
3408     WCHAR szValueName[16], szValue[2];
3409     BOOL bSupported = FALSE, bInstalled = FALSE;
3410     HANDLE hKey;
3411
3412
3413     switch (dwFlags)
3414     {
3415     case LGRPID_INSTALLED:
3416     case LGRPID_SUPPORTED:
3417
3418         hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3419
3420         sprintfW( szValueName, szFormat, lgrpid );
3421
3422         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3423         {
3424             bSupported = TRUE;
3425
3426             if (szValue[0] == '1')
3427                 bInstalled = TRUE;
3428         }
3429
3430         if (hKey)
3431             NtClose( hKey );
3432
3433         break;
3434     }
3435
3436     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3437         (dwFlags == LGRPID_INSTALLED && bInstalled))
3438         return TRUE;
3439
3440     return FALSE;
3441 }
3442
3443 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3444 typedef struct
3445 {
3446   LANGGROUPLOCALE_ENUMPROCA procA;
3447   LANGGROUPLOCALE_ENUMPROCW procW;
3448   DWORD    dwFlags;
3449   LGRPID   lgrpid;
3450   LONG_PTR lParam;
3451 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3452
3453 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3454 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3455 {
3456     static const WCHAR szAlternateSortsKeyName[] = {
3457       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3458     };
3459     WCHAR szNumber[10], szValue[4];
3460     HANDLE hKey;
3461     BOOL bContinue = TRUE, bAlternate = FALSE;
3462     LGRPID lgrpid;
3463     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
3464
3465     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3466     {
3467         SetLastError(ERROR_INVALID_PARAMETER);
3468         return FALSE;
3469     }
3470
3471     if (lpProcs->dwFlags)
3472     {
3473         SetLastError(ERROR_INVALID_FLAGS);
3474         return FALSE;
3475     }
3476
3477     hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3478
3479     if (!hKey)
3480         WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3481
3482     while (bContinue)
3483     {
3484         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3485                               szValue, sizeof(szValue) ))
3486         {
3487             lgrpid = strtoulW( szValue, NULL, 16 );
3488
3489             TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3490                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3491
3492             if (lgrpid == lpProcs->lgrpid)
3493             {
3494                 LCID lcid;
3495
3496                 lcid = strtoulW( szNumber, NULL, 16 );
3497
3498                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3499                  * '00000437          ;Georgian'
3500                  * At present we only pass the LCID string.
3501                  */
3502
3503                 if (lpProcs->procW)
3504                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3505                 else
3506                 {
3507                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3508
3509                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3510
3511                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3512                 }
3513             }
3514
3515             ulIndex++;
3516         }
3517         else
3518         {
3519             /* Finished enumerating this key */
3520             if (!bAlternate)
3521             {
3522                 /* Enumerate alternate sorts also */
3523                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3524                 bAlternate = TRUE;
3525                 ulIndex = 0;
3526             }
3527             else
3528                 bContinue = FALSE; /* Finished both keys */
3529         }
3530
3531         if (!bContinue)
3532             break;
3533     }
3534
3535     if (hKey)
3536         NtClose( hKey );
3537
3538     return TRUE;
3539 }
3540
3541 /******************************************************************************
3542  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
3543  *
3544  * Call a users function for every locale in a language group available on the system.
3545  *
3546  * PARAMS
3547  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
3548  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
3549  *  dwFlags            [I] Reserved, set to 0
3550  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
3551  *
3552  * RETURNS
3553  *  Success: TRUE.
3554  *  Failure: FALSE. Use GetLastError() to determine the cause.
3555  */
3556 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3557                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3558 {
3559     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3560
3561     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3562
3563     callbacks.procA   = pLangGrpLcEnumProc;
3564     callbacks.procW   = NULL;
3565     callbacks.dwFlags = dwFlags;
3566     callbacks.lgrpid  = lgrpid;
3567     callbacks.lParam  = lParam;
3568
3569     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3570 }
3571
3572 /******************************************************************************
3573  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
3574  *
3575  * See EnumLanguageGroupLocalesA.
3576  */
3577 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3578                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3579 {
3580     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3581
3582     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3583
3584     callbacks.procA   = NULL;
3585     callbacks.procW   = pLangGrpLcEnumProc;
3586     callbacks.dwFlags = dwFlags;
3587     callbacks.lgrpid  = lgrpid;
3588     callbacks.lParam  = lParam;
3589
3590     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3591 }
3592
3593 /******************************************************************************
3594  *           EnumSystemGeoID    (KERNEL32.@)
3595  *
3596  * Call a users function for every location available on the system.
3597  *
3598  * PARAMS
3599  *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3600  *  reserved     [I] Reserved, set to 0
3601  *  pGeoEnumProc [I] Callback function to call for each location
3602  *
3603  * RETURNS
3604  *  Success: TRUE.
3605  *  Failure: FALSE. Use GetLastError() to determine the cause.
3606  */
3607 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3608 {
3609     static const WCHAR szCountryCodeValueName[] = {
3610       'C','o','u','n','t','r','y','C','o','d','e','\0'
3611     };
3612     WCHAR szNumber[10];
3613     HANDLE hKey;
3614     ULONG ulIndex = 0;
3615
3616     TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3617
3618     if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3619     {
3620         SetLastError(ERROR_INVALID_PARAMETER);
3621         return FALSE;
3622     }
3623
3624     hKey = NLS_RegOpenKey( 0, szCountryListName );
3625
3626     while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3627     {
3628         BOOL bContinue = TRUE;
3629         DWORD dwGeoId;
3630         HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3631
3632         if (hSubKey)
3633         {
3634             if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3635             {
3636                 TRACE("Got geoid %d\n", dwGeoId);
3637
3638                 if (!pGeoEnumProc( dwGeoId ))
3639                     bContinue = FALSE;
3640             }
3641
3642             NtClose( hSubKey );
3643         }
3644
3645         if (!bContinue)
3646             break;
3647
3648         ulIndex++;
3649     }
3650
3651     if (hKey)
3652         NtClose( hKey );
3653
3654     return TRUE;
3655 }
3656
3657 /******************************************************************************
3658  *           InvalidateNLSCache           (KERNEL32.@)
3659  *
3660  * Invalidate the cache of NLS values.
3661  *
3662  * PARAMS
3663  *  None.
3664  *
3665  * RETURNS
3666  *  Success: TRUE.
3667  *  Failure: FALSE.
3668  */
3669 BOOL WINAPI InvalidateNLSCache(void)
3670 {
3671   FIXME("() stub\n");
3672   return FALSE;
3673 }
3674
3675 /******************************************************************************
3676  *           GetUserGeoID (KERNEL32.@)
3677  */
3678 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3679 {
3680     GEOID ret = GEOID_NOT_AVAILABLE;
3681     static const WCHAR geoW[] = {'G','e','o',0};
3682     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3683     WCHAR bufferW[40], *end;
3684     DWORD count;
3685     HANDLE hkey, hSubkey = 0;
3686     UNICODE_STRING keyW;
3687     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3688     RtlInitUnicodeString( &keyW, nationW );
3689     count = sizeof(bufferW);
3690
3691     if(!(hkey = create_registry_key())) return ret;
3692
3693     switch( GeoClass ){
3694     case GEOCLASS_NATION:
3695         if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3696         {
3697             if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3698                                 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3699                 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3700         }
3701         break;
3702     case GEOCLASS_REGION:
3703         FIXME("GEOCLASS_REGION not handled yet\n");
3704         break;
3705     }
3706
3707     NtClose(hkey);
3708     if (hSubkey) NtClose(hSubkey);
3709     return ret;
3710 }
3711
3712 /******************************************************************************
3713  *           SetUserGeoID (KERNEL32.@)
3714  */
3715 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3716 {
3717     static const WCHAR geoW[] = {'G','e','o',0};
3718     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3719     static const WCHAR formatW[] = {'%','i',0};
3720     UNICODE_STRING nameW,keyW;
3721     WCHAR bufferW[10];
3722     OBJECT_ATTRIBUTES attr;
3723     HANDLE hkey;
3724
3725     if(!(hkey = create_registry_key())) return FALSE;
3726
3727     attr.Length = sizeof(attr);
3728     attr.RootDirectory = hkey;
3729     attr.ObjectName = &nameW;
3730     attr.Attributes = 0;
3731     attr.SecurityDescriptor = NULL;
3732     attr.SecurityQualityOfService = NULL;
3733     RtlInitUnicodeString( &nameW, geoW );
3734     RtlInitUnicodeString( &keyW, nationW );
3735
3736     if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3737
3738     {
3739         NtClose(attr.RootDirectory);
3740         return FALSE;
3741     }
3742
3743     sprintfW(bufferW, formatW, GeoID);
3744     NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3745     NtClose(attr.RootDirectory);
3746     NtClose(hkey);
3747     return TRUE;
3748 }
3749
3750 typedef struct
3751 {
3752     union
3753     {
3754         UILANGUAGE_ENUMPROCA procA;
3755         UILANGUAGE_ENUMPROCW procW;
3756     } u;
3757     DWORD flags;
3758     LONG_PTR param;
3759 } ENUM_UILANG_CALLBACK;
3760
3761 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3762                                          LPCSTR name, WORD LangID, LONG_PTR lParam )
3763 {
3764     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3765     char buf[20];
3766
3767     sprintf(buf, "%08x", (UINT)LangID);
3768     return enum_uilang->u.procA( buf, enum_uilang->param );
3769 }
3770
3771 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3772                                          LPCWSTR name, WORD LangID, LONG_PTR lParam )
3773 {
3774     static const WCHAR formatW[] = {'%','0','8','x',0};
3775     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3776     WCHAR buf[20];
3777
3778     sprintfW( buf, formatW, (UINT)LangID );
3779     return enum_uilang->u.procW( buf, enum_uilang->param );
3780 }
3781
3782 /******************************************************************************
3783  *           EnumUILanguagesA (KERNEL32.@)
3784  */
3785 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3786 {
3787     ENUM_UILANG_CALLBACK enum_uilang;
3788
3789     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3790
3791     if(!pUILangEnumProc) {
3792         SetLastError(ERROR_INVALID_PARAMETER);
3793         return FALSE;
3794     }
3795     if(dwFlags) {
3796         SetLastError(ERROR_INVALID_FLAGS);
3797         return FALSE;
3798     }
3799
3800     enum_uilang.u.procA = pUILangEnumProc;
3801     enum_uilang.flags = dwFlags;
3802     enum_uilang.param = lParam;
3803
3804     EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3805                             (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3806                             (LONG_PTR)&enum_uilang);
3807     return TRUE;
3808 }
3809
3810 /******************************************************************************
3811  *           EnumUILanguagesW (KERNEL32.@)
3812  */
3813 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3814 {
3815     ENUM_UILANG_CALLBACK enum_uilang;
3816
3817     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3818
3819
3820     if(!pUILangEnumProc) {
3821         SetLastError(ERROR_INVALID_PARAMETER);
3822         return FALSE;
3823     }
3824     if(dwFlags) {
3825         SetLastError(ERROR_INVALID_FLAGS);
3826         return FALSE;
3827     }
3828
3829     enum_uilang.u.procW = pUILangEnumProc;
3830     enum_uilang.flags = dwFlags;
3831     enum_uilang.param = lParam;
3832
3833     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3834                             (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3835                             (LONG_PTR)&enum_uilang);
3836     return TRUE;
3837 }
3838
3839 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData, 
3840                 int cchData, LANGID language)
3841 {
3842     FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3843     return 0;
3844 }
3845
3846 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData, 
3847                 int cchData, LANGID language)
3848 {
3849     FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3850     return 0;
3851 }
3852
3853 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3854 {
3855     LCID userlcid;
3856
3857     TRACE("%p, %d\n", localename,  buffersize);
3858     
3859     userlcid = GetUserDefaultLCID();
3860     return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3861 }
3862
3863 /******************************************************************************
3864  *           NormalizeString (KERNEL32.@)
3865  */
3866 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3867                            LPWSTR lpDstString, INT cwDstLength)
3868 {
3869     FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3870     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3871     return 0;
3872 }
3873
3874 /******************************************************************************
3875  *           IsNormalizedString (KERNEL32.@)
3876  */
3877 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
3878 {
3879     FIXME("%x %p %d\n", NormForm, lpString, cwLength);
3880     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3881     return FALSE;
3882 }
3883
3884 enum {
3885     BASE = 36,
3886     TMIN = 1,
3887     TMAX = 26,
3888     SKEW = 38,
3889     DAMP = 700,
3890     INIT_BIAS = 72,
3891     INIT_N = 128
3892 };
3893
3894 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
3895 {
3896     INT k;
3897
3898     delta /= (firsttime ? DAMP : 2);
3899     delta += delta/numpoints;
3900
3901     for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
3902         delta /= BASE-TMIN;
3903     return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
3904 }
3905
3906 /******************************************************************************
3907  *           IdnToAscii (KERNEL32.@)
3908  * Implementation of Punycode based on RFC 3492.
3909  */
3910 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
3911                       LPWSTR lpASCIICharStr, INT cchASCIIChar)
3912 {
3913     static const WCHAR prefixW[] = {'x','n','-','-'};
3914
3915     WCHAR *norm_str;
3916     INT i, label_start, label_end, norm_len, out_label, out = 0;
3917
3918     TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
3919         lpASCIICharStr, cchASCIIChar);
3920
3921     norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
3922     if(!norm_len)
3923         return 0;
3924     norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
3925     if(!norm_str) {
3926         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3927         return 0;
3928     }
3929     norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
3930             cchUnicodeChar, norm_str, norm_len);
3931     if(!norm_len) {
3932         HeapFree(GetProcessHeap(), 0, norm_str);
3933         return 0;
3934     }
3935
3936     for(label_start=0; label_start<norm_len;) {
3937         INT n = INIT_N, bias = INIT_BIAS;
3938         INT delta = 0, b = 0, h;
3939
3940         out_label = out;
3941         for(i=label_start; i<norm_len && norm_str[i]!='.' &&
3942                 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
3943             if(norm_str[i] < 0x80)
3944                 b++;
3945         label_end = i;
3946
3947         if(b == label_end-label_start) {
3948             if(label_end < norm_len)
3949                 b++;
3950             if(!lpASCIICharStr) {
3951                 out += b;
3952             }else if(out+b <= cchASCIIChar) {
3953                 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
3954                 out += b;
3955             }else {
3956                 HeapFree(GetProcessHeap(), 0, norm_str);
3957                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3958                 return 0;
3959             }
3960             label_start = label_end+1;
3961             continue;
3962         }
3963
3964         if(!lpASCIICharStr) {
3965             out += 5+b; /* strlen(xn--...-) */
3966         }else if(out+5+b <= cchASCIIChar) {
3967             memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
3968             out += 4;
3969             for(i=label_start; i<label_end; i++)
3970                 if(norm_str[i] < 0x80)
3971                     lpASCIICharStr[out++] = norm_str[i];
3972             lpASCIICharStr[out++] = '-';
3973         }else {
3974             HeapFree(GetProcessHeap(), 0, norm_str);
3975             SetLastError(ERROR_INSUFFICIENT_BUFFER);
3976             return 0;
3977         }
3978         if(!b)
3979             out--;
3980
3981         for(h=b; h<label_end-label_start;) {
3982             INT m = 0xffff, q, k;
3983
3984             for(i=label_start; i<label_end; i++) {
3985                 if(norm_str[i]>=n && m>norm_str[i])
3986                     m = norm_str[i];
3987             }
3988             delta += (m-n)*(h+1);
3989             n = m;
3990
3991             for(i=label_start; i<label_end; i++) {
3992                 if(norm_str[i] < n) {
3993                     delta++;
3994                 }else if(norm_str[i] == n) {
3995                     for(q=delta, k=BASE; ; k+=BASE) {
3996                         INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
3997                         INT disp = q<t ? q : t+(q-t)%(BASE-t);
3998                         if(!lpASCIICharStr) {
3999                             out++;
4000                         }else if(out+1 <= cchASCIIChar) {
4001                             lpASCIICharStr[out++] = disp<='z'-'a' ?
4002                                 'a'+disp : '0'+disp-'z'+'a'-1;
4003                         }else {
4004                             HeapFree(GetProcessHeap(), 0, norm_str);
4005                             SetLastError(ERROR_INSUFFICIENT_BUFFER);
4006                             return 0;
4007                         }
4008                         if(q < t)
4009                             break;
4010                         q = (q-t)/(BASE-t);
4011                     }
4012                     bias = adapt(delta, h+1, h==b);
4013                     delta = 0;
4014                     h++;
4015                 }
4016             }
4017             delta++;
4018             n++;
4019         }
4020
4021         if(out-out_label > 63) {
4022             HeapFree(GetProcessHeap(), 0, norm_str);
4023             SetLastError(ERROR_INVALID_NAME);
4024             return 0;
4025         }
4026
4027         if(label_end < norm_len) {
4028             if(!lpASCIICharStr) {
4029                 out++;
4030             }else if(out+1 <= cchASCIIChar) {
4031                 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4032             }else {
4033                 HeapFree(GetProcessHeap(), 0, norm_str);
4034                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4035                 return 0;
4036             }
4037         }
4038         label_start = label_end+1;
4039     }
4040
4041     HeapFree(GetProcessHeap(), 0, norm_str);
4042     return out;
4043 }
4044
4045 /******************************************************************************
4046  *           IdnToNameprepUnicode (KERNEL32.@)
4047  */
4048 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4049                                 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4050 {
4051     enum {
4052         UNASSIGNED = 0x1,
4053         PROHIBITED = 0x2,
4054         BIDI_RAL   = 0x4,
4055         BIDI_L     = 0x8
4056     };
4057
4058     extern const unsigned short nameprep_char_type[];
4059     extern const WCHAR nameprep_mapping[];
4060     const WCHAR *ptr;
4061     WORD flags;
4062     WCHAR buf[64], *map_str, norm_str[64], ch;
4063     DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4064     BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4065
4066     TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4067         lpNameprepCharStr, cchNameprepChar);
4068
4069     if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4070         SetLastError(ERROR_INVALID_FLAGS);
4071         return 0;
4072     }
4073
4074     if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4075         SetLastError(ERROR_INVALID_PARAMETER);
4076         return 0;
4077     }
4078
4079     if(cchUnicodeChar == -1)
4080         cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4081     if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4082         SetLastError(ERROR_INVALID_NAME);
4083         return 0;
4084     }
4085
4086     for(label_start=0; label_start<cchUnicodeChar;) {
4087         ascii_only = TRUE;
4088         for(i=label_start; i<cchUnicodeChar; i++) {
4089             ch = lpUnicodeCharStr[i];
4090
4091             if(i!=cchUnicodeChar-1 && !ch) {
4092                 SetLastError(ERROR_INVALID_NAME);
4093                 return 0;
4094             }
4095             /* check if ch is one of label separators defined in RFC3490 */
4096             if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4097                 break;
4098
4099             if(ch > 0x7f) {
4100                 ascii_only = FALSE;
4101                 continue;
4102             }
4103
4104             if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4105                 continue;
4106             if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4107                     || (ch>='0' && ch<='9') || ch=='-')
4108                 continue;
4109
4110             SetLastError(ERROR_INVALID_NAME);
4111             return 0;
4112         }
4113         label_end = i;
4114         /* last label may be empty */
4115         if(label_start==label_end && ch) {
4116             SetLastError(ERROR_INVALID_NAME);
4117             return 0;
4118         }
4119
4120         if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4121                     lpUnicodeCharStr[label_end-1]=='-')) {
4122             SetLastError(ERROR_INVALID_NAME);
4123             return 0;
4124         }
4125
4126         if(ascii_only) {
4127             /* maximal label length is 63 characters */
4128             if(label_end-label_start > 63) {
4129                 SetLastError(ERROR_INVALID_NAME);
4130                 return 0;
4131             }
4132             if(label_end < cchUnicodeChar)
4133                 label_end++;
4134
4135             if(!lpNameprepCharStr) {
4136                 out += label_end-label_start;
4137             }else if(out+label_end-label_start <= cchNameprepChar) {
4138                 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4139                         (label_end-label_start)*sizeof(WCHAR));
4140                 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4141                     lpNameprepCharStr[out+label_end-label_start-1] = '.';
4142                 out += label_end-label_start;
4143             }else {
4144                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4145                 return 0;
4146             }
4147
4148             label_start = label_end;
4149             continue;
4150         }
4151
4152         map_len = 0;
4153         for(i=label_start; i<label_end; i++) {
4154             ch = lpUnicodeCharStr[i];
4155             ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4156             ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4157
4158             if(!ptr[0]) map_len++;
4159             else if(!ptr[1]) map_len++;
4160             else if(!ptr[2]) map_len += 2;
4161             else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4162         }
4163         if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4164             map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4165             if(!map_str) {
4166                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4167                 return 0;
4168             }
4169         }else {
4170             map_str = buf;
4171         }
4172         map_len = 0;
4173         for(i=label_start; i<label_end; i++) {
4174             ch = lpUnicodeCharStr[i];
4175             ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4176             ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4177
4178             if(!ptr[0]) {
4179                 map_str[map_len++] = ch;
4180             }else if(!ptr[1]) {
4181                 map_str[map_len++] = ptr[0];
4182             }else if(!ptr[2]) {
4183                 map_str[map_len++] = ptr[0];
4184                 map_str[map_len++] = ptr[1];
4185             }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4186                 map_str[map_len++] = ptr[0];
4187                 map_str[map_len++] = ptr[1];
4188                 map_str[map_len++] = ptr[2];
4189             }
4190         }
4191
4192         norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4193                 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4194         if(map_str != buf)
4195             HeapFree(GetProcessHeap(), 0, map_str);
4196         if(!norm_len) {
4197             if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4198                 SetLastError(ERROR_INVALID_NAME);
4199             return 0;
4200         }
4201
4202         if(label_end < cchUnicodeChar) {
4203             norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4204             label_end++;
4205         }
4206
4207         if(!lpNameprepCharStr) {
4208             out += norm_len;
4209         }else if(out+norm_len <= cchNameprepChar) {
4210             memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4211             out += norm_len;
4212         }else {
4213             SetLastError(ERROR_INSUFFICIENT_BUFFER);
4214             return 0;
4215         }
4216
4217         have_bidi_ral = prohibit_bidi_ral = FALSE;
4218         mask = PROHIBITED;
4219         if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4220             mask |= UNASSIGNED;
4221         for(i=0; i<norm_len; i++) {
4222             ch = norm_str[i];
4223             flags = get_table_entry( nameprep_char_type, ch );
4224
4225             if(flags & mask) {
4226                 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4227                         : ERROR_NO_UNICODE_TRANSLATION);
4228                 return 0;
4229             }
4230
4231             if(flags & BIDI_RAL)
4232                 have_bidi_ral = TRUE;
4233             if(flags & BIDI_L)
4234                 prohibit_bidi_ral = TRUE;
4235         }
4236
4237         if(have_bidi_ral) {
4238             ch = norm_str[0];
4239             flags = get_table_entry( nameprep_char_type, ch );
4240             if((flags & BIDI_RAL) == 0)
4241                 prohibit_bidi_ral = TRUE;
4242
4243             ch = norm_str[norm_len-1];
4244             flags = get_table_entry( nameprep_char_type, ch );
4245             if((flags & BIDI_RAL) == 0)
4246                 prohibit_bidi_ral = TRUE;
4247         }
4248
4249         if(have_bidi_ral && prohibit_bidi_ral) {
4250             SetLastError(ERROR_INVALID_NAME);
4251             return 0;
4252         }
4253
4254         label_start = label_end;
4255     }
4256
4257     return out;
4258 }
4259
4260 /******************************************************************************
4261  *           IdnToUnicode (KERNEL32.@)
4262  */
4263 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4264                         LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4265 {
4266     extern const unsigned short nameprep_char_type[];
4267
4268     INT i, label_start, label_end, out_label, out = 0;
4269     WCHAR ch;
4270
4271     TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4272         lpUnicodeCharStr, cchUnicodeChar);
4273
4274     for(label_start=0; label_start<cchASCIIChar;) {
4275         INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4276
4277         out_label = out;
4278         for(i=label_start; i<cchASCIIChar; i++) {
4279             ch = lpASCIICharStr[i];
4280
4281             if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4282                 SetLastError(ERROR_INVALID_NAME);
4283                 return 0;
4284             }
4285
4286             if(!ch || ch=='.')
4287                 break;
4288             if(ch == '-')
4289                 delim = i;
4290
4291             if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4292                 continue;
4293             if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4294                     || (ch>='0' && ch<='9') || ch=='-')
4295                 continue;
4296
4297             SetLastError(ERROR_INVALID_NAME);
4298             return 0;
4299         }
4300         label_end = i;
4301         /* last label may be empty */
4302         if(label_start==label_end && ch) {
4303             SetLastError(ERROR_INVALID_NAME);
4304             return 0;
4305         }
4306
4307         if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4308                     lpUnicodeCharStr[label_end-1]=='-')) {
4309             SetLastError(ERROR_INVALID_NAME);
4310             return 0;
4311         }
4312         if(label_end-label_start > 63) {
4313             SetLastError(ERROR_INVALID_NAME);
4314             return 0;
4315         }
4316
4317         if(label_end-label_start<4 ||
4318                 tolowerW(lpASCIICharStr[label_start])!='x' ||
4319                 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4320                 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4321             if(label_end < cchUnicodeChar)
4322                 label_end++;
4323
4324             if(!lpUnicodeCharStr) {
4325                 out += label_end-label_start;
4326             }else if(out+label_end-label_start <= cchUnicodeChar) {
4327                 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4328                         (label_end-label_start)*sizeof(WCHAR));
4329                 out += label_end-label_start;
4330             }else {
4331                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4332                 return 0;
4333             }
4334
4335             label_start = label_end;
4336             continue;
4337         }
4338
4339         if(delim == label_start+3)
4340             delim++;
4341         if(!lpUnicodeCharStr) {
4342             out += delim-label_start-4;
4343         }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4344             memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4345                     (delim-label_start-4)*sizeof(WCHAR));
4346             out += delim-label_start-4;
4347         }else {
4348             SetLastError(ERROR_INSUFFICIENT_BUFFER);
4349             return 0;
4350         }
4351         if(out != out_label)
4352             delim++;
4353
4354         for(i=delim; i<label_end;) {
4355             old_pos = pos;
4356             w = 1;
4357             for(k=BASE; ; k+=BASE) {
4358                 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4359                 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4360                     SetLastError(ERROR_INVALID_NAME);
4361                     return 0;
4362                 }
4363                 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4364                 pos += digit*w;
4365                 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4366                 if(digit < t)
4367                     break;
4368                 w *= BASE-t;
4369             }
4370             bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4371             n += pos/(out-out_label+1);
4372             pos %= out-out_label+1;
4373
4374             if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4375                     get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4376                 SetLastError(ERROR_INVALID_NAME);
4377                 return 0;
4378             }
4379             if(!lpUnicodeCharStr) {
4380                 out++;
4381             }else if(out+1 <= cchASCIIChar) {
4382                 memmove(lpUnicodeCharStr+out_label+pos+1,
4383                         lpUnicodeCharStr+out_label+pos,
4384                         (out-out_label-pos)*sizeof(WCHAR));
4385                 lpUnicodeCharStr[out_label+pos] = n;
4386                 out++;
4387             }else {
4388                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4389                 return 0;
4390             }
4391             pos++;
4392         }
4393
4394         if(out-out_label > 63) {
4395             SetLastError(ERROR_INVALID_NAME);
4396             return 0;
4397         }
4398
4399         if(label_end < cchASCIIChar) {
4400             if(!lpUnicodeCharStr) {
4401                 out++;
4402             }else if(out+1 <= cchUnicodeChar) {
4403                 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4404             }else {
4405                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4406                 return 0;
4407             }
4408         }
4409         label_start = label_end+1;
4410     }
4411
4412     return out;
4413 }