Stub implementation od IPersist, IPersistMoniker, IPersistFile,
[wine] / dlls / kernel / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <assert.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <stdlib.h>
33
34 #include "ntstatus.h"
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"  /* for RT_STRINGW */
38 #include "winreg.h"
39 #include "winternl.h"
40 #include "wine/unicode.h"
41 #include "winnls.h"
42 #include "winerror.h"
43 #include "winver.h"
44 #include "thread.h"
45 #include "kernel_private.h"
46 #include "wine/debug.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(nls);
49
50 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|LOCALE_RETURN_NUMBER)
51
52 /* current code pages */
53 static const union cptable *ansi_cptable;
54 static const union cptable *oem_cptable;
55 static const union cptable *mac_cptable;
56 static const union cptable *unix_cptable;  /* NULL if UTF8 */
57
58 static HKEY NLS_RegOpenKey(HKEY hRootKey, LPCWSTR szKeyName);
59 static HKEY NLS_RegOpenSubKey(HKEY hRootKey, LPCWSTR szKeyName);
60
61 static const WCHAR szNlsKeyName[] = {
62     'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
63     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
64     'C','o','n','t','r','o','l','\\','N','l','s','\0'
65 };
66
67 /* Charset to codepage map, sorted by name. */
68 static const struct charset_entry
69 {
70     const char *charset_name;
71     UINT        codepage;
72 } charset_names[] =
73 {
74     { "BIG5", 950 },
75     { "CP1250", 1250 },
76     { "CP1251", 1251 },
77     { "CP1252", 1252 },
78     { "CP1253", 1253 },
79     { "CP1254", 1254 },
80     { "CP1255", 1255 },
81     { "CP1256", 1256 },
82     { "CP1257", 1257 },
83     { "CP1258", 1258 },
84     { "CP932", 932 },
85     { "CP936", 936 },
86     { "CP949", 949 },
87     { "CP950", 950 },
88     { "EUCJP", 20932 },
89     { "GB2312", 936 },
90     { "IBM037", 37 },
91     { "IBM1026", 1026 },
92     { "IBM424", 424 },
93     { "IBM437", 437 },
94     { "IBM500", 500 },
95     { "IBM850", 850 },
96     { "IBM852", 852 },
97     { "IBM855", 855 },
98     { "IBM857", 857 },
99     { "IBM860", 860 },
100     { "IBM861", 861 },
101     { "IBM862", 862 },
102     { "IBM863", 863 },
103     { "IBM864", 864 },
104     { "IBM865", 865 },
105     { "IBM866", 866 },
106     { "IBM869", 869 },
107     { "IBM874", 874 },
108     { "IBM875", 875 },
109     { "ISO88591", 28591 },
110     { "ISO885910", 28600 },
111     { "ISO885913", 28603 },
112     { "ISO885914", 28604 },
113     { "ISO885915", 28605 },
114     { "ISO885916", 28606 },
115     { "ISO88592", 28592 },
116     { "ISO88593", 28593 },
117     { "ISO88594", 28594 },
118     { "ISO88595", 28595 },
119     { "ISO88596", 28596 },
120     { "ISO88597", 28597 },
121     { "ISO88598", 28598 },
122     { "ISO88599", 28599 },
123     { "KOI8R", 20866 },
124     { "KOI8U", 21866 },
125     { "UTF8", CP_UTF8 }
126 };
127
128 #define NLS_MAX_LANGUAGES 20
129 typedef struct {
130     WCHAR lang[128];
131     WCHAR country[4];
132     LANGID found_lang_id[NLS_MAX_LANGUAGES];
133     WCHAR found_language[NLS_MAX_LANGUAGES][3];
134     WCHAR found_country[NLS_MAX_LANGUAGES][3];
135     int n_found;
136 } LANG_FIND_DATA;
137
138
139 /* copy Unicode string to Ascii without using codepages */
140 static inline void strcpyWtoA( char *dst, const WCHAR *src )
141 {
142     while ((*dst++ = *src++));
143 }
144
145 /* Copy Ascii string to Unicode without using codepages */
146 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
147 {
148     while (n > 1 && *src)
149     {
150         *dst++ = (unsigned char)*src++;
151         n--;
152     }
153     if (n) *dst = 0;
154 }
155
156 /* return a printable string for a language id */
157 static const char *debugstr_lang( LANGID lang )
158 {
159     WCHAR langW[4], countryW[4];
160     char buffer[8];
161     LCID lcid = MAKELCID( lang, SORT_DEFAULT );
162
163     GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME|LOCALE_NOUSEROVERRIDE, langW, sizeof(langW)/sizeof(WCHAR));
164     GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE, countryW, sizeof(countryW)/sizeof(WCHAR));
165     strcpyWtoA( buffer, langW );
166     strcat( buffer, "_" );
167     strcpyWtoA( buffer + strlen(buffer), countryW );
168     return wine_dbg_sprintf( "%s", buffer );
169 }
170
171 /***********************************************************************
172  *              get_lcid_codepage
173  *
174  * Retrieve the ANSI codepage for a given locale.
175  */
176 inline static UINT get_lcid_codepage( LCID lcid )
177 {
178     UINT ret;
179     if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
180                          sizeof(ret)/sizeof(WCHAR) )) ret = 0;
181     return ret;
182 }
183
184
185 /***********************************************************************
186  *              get_codepage_table
187  *
188  * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
189  */
190 static const union cptable *get_codepage_table( unsigned int codepage )
191 {
192     const union cptable *ret = NULL;
193
194     assert( ansi_cptable );  /* init must have been done already */
195
196     switch(codepage)
197     {
198     case CP_ACP:
199         return ansi_cptable;
200     case CP_OEMCP:
201         return oem_cptable;
202     case CP_MACCP:
203         return mac_cptable;
204     case CP_UTF7:
205     case CP_UTF8:
206         break;
207     case CP_THREAD_ACP:
208         if (!(codepage = NtCurrentTeb()->code_page)) return ansi_cptable;
209         /* fall through */
210     default:
211         if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
212         if (codepage == oem_cptable->info.codepage) return oem_cptable;
213         if (codepage == mac_cptable->info.codepage) return mac_cptable;
214         ret = wine_cp_get_table( codepage );
215         break;
216     }
217     return ret;
218 }
219
220 /***********************************************************************
221  *              create_registry_key
222  *
223  * Create the Control Panel\\International registry key.
224  */
225 inline static HKEY create_registry_key(void)
226 {
227     static const WCHAR intlW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
228                                   'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
229     OBJECT_ATTRIBUTES attr;
230     UNICODE_STRING nameW;
231     HKEY hkey;
232
233     if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
234
235     attr.Length = sizeof(attr);
236     attr.RootDirectory = hkey;
237     attr.ObjectName = &nameW;
238     attr.Attributes = 0;
239     attr.SecurityDescriptor = NULL;
240     attr.SecurityQualityOfService = NULL;
241     RtlInitUnicodeString( &nameW, intlW );
242
243     if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) hkey = 0;
244     NtClose( attr.RootDirectory );
245     return hkey;
246 }
247
248
249 /***********************************************************************
250  *              LOCALE_InitRegistry
251  *
252  * Update registry contents on startup if the user locale has changed.
253  * This simulates the action of the Windows control panel.
254  */
255 void LOCALE_InitRegistry(void)
256 {
257     static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
258     static const WCHAR acpW[] = {'A','C','P',0};
259     static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
260     static const WCHAR maccpW[] = {'M','A','C','C','P',0};
261     static const struct
262     {
263         LPCWSTR name;
264         USHORT value;
265     } update_cp_values[] = {
266         { acpW, LOCALE_IDEFAULTANSICODEPAGE },
267         { oemcpW, LOCALE_IDEFAULTCODEPAGE },
268         { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
269     };
270     static const USHORT updateValues[] = {
271       LOCALE_SLANGUAGE,
272       LOCALE_SCOUNTRY, LOCALE_ICOUNTRY,
273       LOCALE_S1159, LOCALE_S2359,
274       LOCALE_STIME, LOCALE_ITIME,
275       LOCALE_ITLZERO,
276       LOCALE_SSHORTDATE,
277       LOCALE_SLONGDATE,
278       LOCALE_SDATE,
279       LOCALE_SCURRENCY, LOCALE_ICURRENCY,
280       LOCALE_INEGCURR,
281       LOCALE_ICURRDIGITS,
282       LOCALE_SDECIMAL,
283       LOCALE_SLIST,
284       LOCALE_STHOUSAND,
285       LOCALE_IDIGITS,
286       LOCALE_IDIGITSUBSTITUTION,
287       LOCALE_SNATIVEDIGITS,
288       LOCALE_ITIMEMARKPOSN,
289       LOCALE_ICALENDARTYPE,
290       LOCALE_ILZERO,
291       LOCALE_IMEASURE
292     };
293     static const WCHAR LocaleW[] = {'L','o','c','a','l','e',0};
294     UNICODE_STRING nameW;
295     char buffer[20];
296     WCHAR bufferW[80];
297     DWORD count, i;
298     HKEY hkey;
299     LCID lcid = GetUserDefaultLCID();
300
301     if (!(hkey = create_registry_key()))
302         return;  /* don't do anything if we can't create the registry key */
303
304     RtlInitUnicodeString( &nameW, LocaleW );
305     count = sizeof(bufferW);
306     if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, (LPBYTE)bufferW, count, &count))
307     {
308         const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
309         LPCWSTR szValueText = (LPCWSTR)info->Data;
310
311         if (strtoulW( szValueText, NULL, 16 ) == lcid)  /* already set correctly */
312         {
313             NtClose( hkey );
314             return;
315         }
316         TRACE( "updating registry, locale changed %s -> %08lx\n", debugstr_w(szValueText), lcid );
317     }
318     else TRACE( "updating registry, locale changed none -> %08lx\n", lcid );
319
320     sprintf( buffer, "%08lx", lcid );
321     /* Note: '9' constant below is strlen(buffer) + 1 */
322     RtlMultiByteToUnicodeN( bufferW, sizeof(bufferW), NULL, buffer, 9 );
323     NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, 9 * sizeof(WCHAR) );
324     NtClose( hkey );
325
326     for (i = 0; i < sizeof(updateValues)/sizeof(updateValues[0]); i++)
327     {
328         GetLocaleInfoW( lcid, updateValues[i] | LOCALE_NOUSEROVERRIDE, bufferW,
329                         sizeof(bufferW)/sizeof(WCHAR) );
330         SetLocaleInfoW( lcid, updateValues[i], bufferW );
331     }
332
333     hkey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), CodepageW );
334
335     for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
336     {
337         count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
338                                 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
339         RtlInitUnicodeString( &nameW, update_cp_values[i].name );
340         NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
341     }
342
343     NtClose( hkey );
344 }
345
346
347 /***********************************************************************
348  *           find_language_id_proc
349  */
350 static BOOL CALLBACK find_language_id_proc( HMODULE hModule, LPCWSTR type,
351                                             LPCWSTR name, WORD LangID, LPARAM lParam )
352 {
353     LANG_FIND_DATA *l_data = (LANG_FIND_DATA *)lParam;
354     LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
355     WCHAR buf_language[128];
356     WCHAR buf_country[128];
357     WCHAR buf_en_language[128];
358
359     if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
360         return TRUE; /* continue search */
361
362     buf_language[0] = 0;
363     buf_country[0] = 0;
364
365     GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME|LOCALE_NOUSEROVERRIDE,
366                    buf_language, sizeof(buf_language)/sizeof(WCHAR));
367     GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
368                    buf_country, sizeof(buf_country)/sizeof(WCHAR));
369
370     if(l_data->lang[0] && !strcmpiW(l_data->lang, buf_language))
371     {
372         if(l_data->country[0])
373         {
374             if(!strcmpiW(l_data->country, buf_country))
375             {
376                 l_data->found_lang_id[0] = LangID;
377                 l_data->n_found = 1;
378                 TRACE("Found id %04X for lang %s country %s\n",
379                       LangID, debugstr_w(l_data->lang), debugstr_w(l_data->country));
380                 return FALSE; /* stop enumeration */
381             }
382         }
383         else goto found; /* l_data->country not specified */
384     }
385
386     /* Just in case, check LOCALE_SENGLANGUAGE too,
387      * in hope that possible alias name might have that value.
388      */
389     buf_en_language[0] = 0;
390     GetLocaleInfoW(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE,
391                    buf_en_language, sizeof(buf_en_language)/sizeof(WCHAR));
392
393     if(l_data->lang[0] && !strcmpiW(l_data->lang, buf_en_language)) goto found;
394     return TRUE;  /* not found, continue search */
395
396 found:
397     l_data->found_lang_id[l_data->n_found] = LangID;
398     strncpyW(l_data->found_country[l_data->n_found], buf_country, 3);
399     strncpyW(l_data->found_language[l_data->n_found], buf_language, 3);
400     l_data->n_found++;
401     TRACE("Found id %04X for lang %s\n", LangID, debugstr_w(l_data->lang));
402     return (l_data->n_found < NLS_MAX_LANGUAGES); /* continue search, unless we have enough */
403 }
404
405
406 /***********************************************************************
407  *           get_language_id
408  *
409  * INPUT:
410  *      Lang: a string whose two first chars are the iso name of a language.
411  *      Country: a string whose two first chars are the iso name of country
412  *      Charset: a string defining the chosen charset encoding
413  *      Dialect: a string defining a variation of the locale
414  *
415  *      all those values are from the standardized format of locale
416  *      name in unix which is: Lang[_Country][.Charset][@Dialect]
417  *
418  * RETURNS:
419  *      the numeric code of the language used by Windows
420  *
421  * FIXME: Charset and Dialect are not handled
422  */
423 static LANGID get_language_id(LPCSTR Lang, LPCSTR Country, LPCSTR Charset, LPCSTR Dialect)
424 {
425     LANG_FIND_DATA l_data;
426
427     if(!Lang)
428     {
429         l_data.found_lang_id[0] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
430         goto END;
431     }
432
433     l_data.n_found = 0;
434     strcpynAtoW(l_data.lang, Lang, sizeof(l_data.lang));
435
436     if (Country) strcpynAtoW(l_data.country, Country, sizeof(l_data.country));
437     else l_data.country[0] = 0;
438
439     EnumResourceLanguagesW(kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
440                            find_language_id_proc, (LPARAM)&l_data);
441
442     if (l_data.n_found == 1) goto END;
443
444     if(!l_data.n_found)
445     {
446         if(l_data.country[0])
447         {
448             /* retry without country name */
449             l_data.country[0] = 0;
450             EnumResourceLanguagesW(kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
451                                    find_language_id_proc, (LONG)&l_data);
452             if (!l_data.n_found)
453             {
454                 MESSAGE("Warning: Language '%s_%s' was not recognized, defaulting to English.\n",
455                         Lang, Country);
456                 l_data.found_lang_id[0] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
457             }
458             else MESSAGE("Warning: Language '%s_%s' was not recognized, defaulting to '%s'.\n",
459                          Lang, Country, debugstr_lang(l_data.found_lang_id[0]) );
460         }
461         else
462         {
463             MESSAGE("Warning: Language '%s' was not recognized, defaulting to English.\n", Lang);
464             l_data.found_lang_id[0] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
465         }
466     }
467     else
468     {
469         int i;
470
471         if (Country && Country[0])
472             MESSAGE("For language '%s_%s' several language ids were found:\n", Lang, Country);
473         else
474             MESSAGE("For language '%s' several language ids were found:\n", Lang);
475
476         /* print a list of languages with their description */
477         for (i = 0; i < l_data.n_found; i++)
478         {
479             WCHAR buffW[128];
480             char buffA[128];
481             GetLocaleInfoW( MAKELCID( l_data.found_lang_id[i], SORT_DEFAULT ),
482                            LOCALE_SLANGUAGE|LOCALE_NOUSEROVERRIDE, buffW, sizeof(buffW)/sizeof(WCHAR));
483             strcpyWtoA( buffA, buffW );
484             MESSAGE( "   %s (%04X) - %s\n", debugstr_lang(l_data.found_lang_id[i]),
485                      l_data.found_lang_id[i], buffA );
486         }
487         MESSAGE("Defaulting to '%s'. You should specify the exact language you want\n"
488                 "by defining your LANG environment variable like this: LANG=%s\n",
489                 debugstr_lang(l_data.found_lang_id[0]), debugstr_lang(l_data.found_lang_id[0]) );
490     }
491 END:
492     TRACE("Returning %04X (%s)\n", l_data.found_lang_id[0], debugstr_lang(l_data.found_lang_id[0]));
493     return l_data.found_lang_id[0];
494 }
495
496
497 /***********************************************************************
498  *              charset_cmp (internal)
499  */
500 static int charset_cmp( const void *name, const void *entry )
501 {
502     const struct charset_entry *charset = (const struct charset_entry *)entry;
503     return strcasecmp( (const char *)name, charset->charset_name );
504 }
505
506 /***********************************************************************
507  *              get_env_lcid
508  */
509 static LCID get_env_lcid( UINT *unix_cp, const char *env_str )
510 {
511     char *buf, *lang,*country,*charset,*dialect,*next;
512     LCID ret = 0;
513
514     if ((lang = getenv( "LC_ALL" )) ||
515         (env_str && (lang = getenv( env_str ))) ||
516         (lang = getenv( "LANG" )))
517     {
518         if (!strcmp(lang,"POSIX") || !strcmp(lang,"C")) goto done;
519
520         buf = RtlAllocateHeap( GetProcessHeap(), 0, strlen(lang) + 1 );
521         strcpy( buf, lang );
522         lang=buf;
523
524         do {
525             next=strchr(lang,':'); if (next) *next++='\0';
526             dialect=strchr(lang,'@'); if (dialect) *dialect++='\0';
527             charset=strchr(lang,'.'); if (charset) *charset++='\0';
528             country=strchr(lang,'_'); if (country) *country++='\0';
529
530             ret = get_language_id(lang, country, charset, dialect);
531             if (ret && charset && unix_cp)
532             {
533                 const struct charset_entry *entry;
534                 char charset_name[16];
535                 size_t i, j;
536
537                 /* remove punctuation characters from charset name */
538                 for (i = j = 0; charset[i] && j < sizeof(charset_name)-1; i++)
539                     if (isalnum(charset[i])) charset_name[j++] = charset[i];
540                 charset_name[j] = 0;
541
542                 entry = bsearch( charset_name, charset_names,
543                                  sizeof(charset_names)/sizeof(charset_names[0]),
544                                  sizeof(charset_names[0]), charset_cmp );
545                 if (entry)
546                 {
547                     *unix_cp = entry->codepage;
548                     TRACE("charset %s was mapped to cp %u\n", charset, *unix_cp);
549                 }
550                 else
551                     FIXME("charset %s was not recognized\n", charset);
552             }
553
554             lang=next;
555         } while (lang && !ret);
556
557         if (!ret) MESSAGE("Warning: language '%s' not recognized, defaulting to English\n", buf);
558         RtlFreeHeap( GetProcessHeap(), 0, buf );
559     }
560
561  done:
562     if (!ret) ret = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT) ;
563     return ret;
564 }
565
566
567 /***********************************************************************
568  *              GetUserDefaultLangID (KERNEL32.@)
569  *
570  * Get the default language Id for the current user.
571  *
572  * PARAMS
573  *  None.
574  *
575  * RETURNS
576  *  The current LANGID of the default language for the current user.
577  */
578 LANGID WINAPI GetUserDefaultLangID(void)
579 {
580     return LANGIDFROMLCID(GetUserDefaultLCID());
581 }
582
583
584 /***********************************************************************
585  *              GetSystemDefaultLangID (KERNEL32.@)
586  *
587  * Get the default language Id for the system.
588  *
589  * PARAMS
590  *  None.
591  *
592  * RETURNS
593  *  The current LANGID of the default language for the system.
594  */
595 LANGID WINAPI GetSystemDefaultLangID(void)
596 {
597     return LANGIDFROMLCID(GetSystemDefaultLCID());
598 }
599
600
601 /***********************************************************************
602  *              GetUserDefaultLCID (KERNEL32.@)
603  *
604  * Get the default locale Id for the current user.
605  *
606  * PARAMS
607  *  None.
608  *
609  * RETURNS
610  *  The current LCID of the default locale for the current user.
611  */
612 LCID WINAPI GetUserDefaultLCID(void)
613 {
614     LCID lcid;
615     NtQueryDefaultLocale( TRUE, &lcid );
616     return lcid;
617 }
618
619
620 /***********************************************************************
621  *              GetSystemDefaultLCID (KERNEL32.@)
622  *
623  * Get the default locale Id for the system.
624  *
625  * PARAMS
626  *  None.
627  *
628  * RETURNS
629  *  The current LCID of the default locale for the system.
630  */
631 LCID WINAPI GetSystemDefaultLCID(void)
632 {
633     LCID lcid;
634     NtQueryDefaultLocale( FALSE, &lcid );
635     return lcid;
636 }
637
638
639 /***********************************************************************
640  *              GetUserDefaultUILanguage (KERNEL32.@)
641  *
642  * Get the default user interface language Id for the current user.
643  *
644  * PARAMS
645  *  None.
646  *
647  * RETURNS
648  *  The current LANGID of the default UI language for the current user.
649  */
650 LANGID WINAPI GetUserDefaultUILanguage(void)
651 {
652     LANGID lang;
653     NtQueryDefaultUILanguage( &lang );
654     return lang;
655 }
656
657
658 /***********************************************************************
659  *              GetSystemDefaultUILanguage (KERNEL32.@)
660  *
661  * Get the default user interface language Id for the system.
662  *
663  * PARAMS
664  *  None.
665  *
666  * RETURNS
667  *  The current LANGID of the default UI language for the system. This is
668  *  typically the same language used during the installation process.
669  */
670 LANGID WINAPI GetSystemDefaultUILanguage(void)
671 {
672     LANGID lang;
673     NtQueryInstallUILanguage( &lang );
674     return lang;
675 }
676
677
678 /******************************************************************************
679  *              get_locale_value_name
680  *
681  * Gets the registry value name for a given lctype.
682  */
683 static const WCHAR *get_locale_value_name( DWORD lctype )
684 {
685     static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
686     static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
687     static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
688     static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
689     static const WCHAR iDateW[] = {'i','D','a','t','e',0};
690     static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
691     static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
692     static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
693     static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
694     static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
695     static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
696     static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
697     static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
698     static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
699     static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
700     static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
701     static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
702     static const WCHAR s1159W[] = {'s','1','1','5','9',0};
703     static const WCHAR s2359W[] = {'s','2','3','5','9',0};
704     static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
705     static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
706     static const WCHAR sDateW[] = {'s','D','a','t','e',0};
707     static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
708     static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
709     static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
710     static const WCHAR sListW[] = {'s','L','i','s','t',0};
711     static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
712     static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
713     static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
714     static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
715     static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
716     static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
717     static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
718     static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
719     static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
720     static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
721     static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
722     static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
723     static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
724
725     switch (lctype)
726     {
727     /* These values are used by SetLocaleInfo and GetLocaleInfo, and
728      * the values are stored in the registry, confirmed under Windows.
729      */
730     case LOCALE_ICALENDARTYPE:    return iCalendarTypeW;
731     case LOCALE_ICURRDIGITS:      return iCurrDigitsW;
732     case LOCALE_ICURRENCY:        return iCurrencyW;
733     case LOCALE_IDIGITS:          return iDigitsW;
734     case LOCALE_IFIRSTDAYOFWEEK:  return iFirstDayOfWeekW;
735     case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
736     case LOCALE_ILZERO:           return iLZeroW;
737     case LOCALE_IMEASURE:         return iMeasureW;
738     case LOCALE_INEGCURR:         return iNegCurrW;
739     case LOCALE_INEGNUMBER:       return iNegNumberW;
740     case LOCALE_IPAPERSIZE:       return iPaperSizeW;
741     case LOCALE_ITIME:            return iTimeW;
742     case LOCALE_S1159:            return s1159W;
743     case LOCALE_S2359:            return s2359W;
744     case LOCALE_SCURRENCY:        return sCurrencyW;
745     case LOCALE_SDATE:            return sDateW;
746     case LOCALE_SDECIMAL:         return sDecimalW;
747     case LOCALE_SGROUPING:        return sGroupingW;
748     case LOCALE_SLIST:            return sListW;
749     case LOCALE_SLONGDATE:        return sLongDateW;
750     case LOCALE_SMONDECIMALSEP:   return sMonDecimalSepW;
751     case LOCALE_SMONGROUPING:     return sMonGroupingW;
752     case LOCALE_SMONTHOUSANDSEP:  return sMonThousandSepW;
753     case LOCALE_SNEGATIVESIGN:    return sNegativeSignW;
754     case LOCALE_SPOSITIVESIGN:    return sPositiveSignW;
755     case LOCALE_SSHORTDATE:       return sShortDateW;
756     case LOCALE_STHOUSAND:        return sThousandW;
757     case LOCALE_STIME:            return sTimeW;
758     case LOCALE_STIMEFORMAT:      return sTimeFormatW;
759     case LOCALE_SYEARMONTH:       return sYearMonthW;
760
761     /* The following are not listed under MSDN as supported,
762      * but seem to be used and also stored in the registry.
763      */
764     case LOCALE_ICOUNTRY:         return iCountryW;
765     case LOCALE_IDATE:            return iDateW;
766     case LOCALE_ILDATE:           return iLDateW;
767     case LOCALE_ITLZERO:          return iTLZeroW;
768     case LOCALE_SCOUNTRY:         return sCountryW;
769     case LOCALE_SLANGUAGE:        return sLanguageW;
770
771     /* The following are used in XP and later */
772     case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
773     case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
774     case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
775     }
776     return NULL;
777 }
778
779
780 /******************************************************************************
781  *              get_registry_locale_info
782  *
783  * Retrieve user-modified locale info from the registry.
784  * Return length, 0 on error, -1 if not found.
785  */
786 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
787 {
788     DWORD size;
789     INT ret;
790     HKEY hkey;
791     NTSTATUS status;
792     UNICODE_STRING nameW;
793     KEY_VALUE_PARTIAL_INFORMATION *info;
794     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
795
796     if (!(hkey = create_registry_key())) return -1;
797
798     RtlInitUnicodeString( &nameW, value );
799     size = info_size + len * sizeof(WCHAR);
800
801     if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
802     {
803         NtClose( hkey );
804         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
805         return 0;
806     }
807
808     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
809     if (status == STATUS_BUFFER_OVERFLOW && !buffer) status = 0;
810
811     if (!status)
812     {
813         ret = (size - info_size) / sizeof(WCHAR);
814         /* append terminating null if needed */
815         if (!ret || ((WCHAR *)info->Data)[ret-1])
816         {
817             if (ret < len || !buffer) ret++;
818             else
819             {
820                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
821                 ret = 0;
822             }
823         }
824         if (ret && buffer)
825         {
826             memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
827             buffer[ret-1] = 0;
828         }
829     }
830     else
831     {
832         if (status == STATUS_OBJECT_NAME_NOT_FOUND) ret = -1;
833         else
834         {
835             SetLastError( RtlNtStatusToDosError(status) );
836             ret = 0;
837         }
838     }
839     NtClose( hkey );
840     HeapFree( GetProcessHeap(), 0, info );
841     return ret;
842 }
843
844
845 /******************************************************************************
846  *              GetLocaleInfoA (KERNEL32.@)
847  *
848  * Get information about an aspect of a locale.
849  *
850  * PARAMS
851  *  lcid   [I] LCID of the locale
852  *  lctype [I] LCTYPE_ flags from "winnls.h"
853  *  buffer [O] Destination for the information
854  *  len    [I] Length of buffer in characters
855  *
856  * RETURNS
857  *  Success: The size of the data requested. If buffer is non-NULL, it is filled
858  *           with the information.
859  *  Failure: 0. Use GetLastError() to determine the cause.
860  *
861  * NOTES
862  *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
863  *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
864  *    which is a bit string.
865  */
866 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
867 {
868     WCHAR *bufferW;
869     INT lenW, ret;
870
871     TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
872
873     if (len < 0 || (len && !buffer))
874     {
875         SetLastError( ERROR_INVALID_PARAMETER );
876         return 0;
877     }
878     if (!len) buffer = NULL;
879
880     if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
881
882     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
883     {
884         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
885         return 0;
886     }
887     if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
888     {
889         if ((lctype & LOCALE_RETURN_NUMBER) ||
890             ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
891         {
892             /* it's not an ASCII string, just bytes */
893             ret *= sizeof(WCHAR);
894             if (buffer)
895             {
896                 if (ret <= len) memcpy( buffer, bufferW, ret );
897                 else
898                 {
899                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
900                     ret = 0;
901                 }
902             }
903         }
904         else
905         {
906             UINT codepage = CP_ACP;
907             if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
908             ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
909         }
910     }
911     HeapFree( GetProcessHeap(), 0, bufferW );
912     return ret;
913 }
914
915
916 /******************************************************************************
917  *              GetLocaleInfoW (KERNEL32.@)
918  *
919  * See GetLocaleInfoA.
920  */
921 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
922 {
923     LANGID lang_id;
924     HRSRC hrsrc;
925     HGLOBAL hmem;
926     INT ret;
927     UINT lcflags;
928     const WCHAR *p;
929     unsigned int i;
930
931     if (len < 0 || (len && !buffer))
932     {
933         SetLastError( ERROR_INVALID_PARAMETER );
934         return 0;
935     }
936     if (!len) buffer = NULL;
937
938     lcid = ConvertDefaultLocale(lcid);
939
940     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
941     lctype &= 0xffff;
942
943     TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
944
945     /* first check for overrides in the registry */
946
947     if (!(lcflags & LOCALE_NOUSEROVERRIDE) && lcid == GetUserDefaultLCID())
948     {
949         const WCHAR *value = get_locale_value_name(lctype);
950
951         if (value)
952         {
953             if (lcflags & LOCALE_RETURN_NUMBER)
954             {
955                 WCHAR tmp[16];
956                 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
957                 if (ret > 0)
958                 {
959                     WCHAR *end;
960                     UINT number = strtolW( tmp, &end, 10 );
961                     if (*end)  /* invalid number */
962                     {
963                         SetLastError( ERROR_INVALID_FLAGS );
964                         return 0;
965                     }
966                     ret = sizeof(UINT)/sizeof(WCHAR);
967                     if (!buffer) return ret;
968                     if (ret > len)
969                     {
970                         SetLastError( ERROR_INSUFFICIENT_BUFFER );
971                         return 0;
972                     }
973                     memcpy( buffer, &number, sizeof(number) );
974                 }
975             }
976             else ret = get_registry_locale_info( value, buffer, len );
977
978             if (ret != -1) return ret;
979         }
980     }
981
982     /* now load it from kernel resources */
983
984     lang_id = LANGIDFROMLCID( lcid );
985
986     /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
987     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
988         lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
989
990     if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
991                                    (LPCWSTR)((lctype >> 4) + 1), lang_id )))
992     {
993         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
994         return 0;
995     }
996     if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
997         return 0;
998
999     p = LockResource( hmem );
1000     for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1001
1002     if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1003     else ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1004
1005     if (!buffer) return ret;
1006
1007     if (ret > len)
1008     {
1009         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1010         return 0;
1011     }
1012
1013     if (lcflags & LOCALE_RETURN_NUMBER)
1014     {
1015         UINT number;
1016         WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1017         if (!tmp) return 0;
1018         memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1019         tmp[*p] = 0;
1020         number = strtolW( tmp, &end, 10 );
1021         if (!*end)
1022             memcpy( buffer, &number, sizeof(number) );
1023         else  /* invalid number */
1024         {
1025             SetLastError( ERROR_INVALID_FLAGS );
1026             ret = 0;
1027         }
1028         HeapFree( GetProcessHeap(), 0, tmp );
1029
1030         TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning number %d\n",
1031                lcid, lctype, buffer, len, number );
1032     }
1033     else
1034     {
1035         memcpy( buffer, p + 1, *p * sizeof(WCHAR) );
1036         if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1037
1038         TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning %d %s\n",
1039                lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1040     }
1041     return ret;
1042 }
1043
1044
1045 /******************************************************************************
1046  *              SetLocaleInfoA  [KERNEL32.@]
1047  *
1048  * Set information about an aspect of a locale.
1049  *
1050  * PARAMS
1051  *  lcid   [I] LCID of the locale
1052  *  lctype [I] LCTYPE_ flags from "winnls.h"
1053  *  data   [I] Information to set
1054  *
1055  * RETURNS
1056  *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
1057  *           whenever it is called without LOCALE_NOUSEROVERRIDE.
1058  *  Failure: FALSE. Use GetLastError() to determine the cause.
1059  *
1060  * NOTES
1061  *  - Values are only be set for the current user locale; the system locale
1062  *  settings cannot be changed.
1063  *  - Any settings changed by this call are lost when the locale is changed by
1064  *  the control panel (in Wine, this happens every time you change LANG).
1065  *  - The native implementation of this function does not check that lcid matches
1066  *  the current user locale, and simply sets the new values. Wine warns you in
1067  *  this case, but behaves the same.
1068  */
1069 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1070 {
1071     UINT codepage = CP_ACP;
1072     WCHAR *strW;
1073     DWORD len;
1074     BOOL ret;
1075
1076     lcid = ConvertDefaultLocale(lcid);
1077
1078     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1079
1080     if (!data)
1081     {
1082         SetLastError( ERROR_INVALID_PARAMETER );
1083         return FALSE;
1084     }
1085     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1086     if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1087     {
1088         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1089         return FALSE;
1090     }
1091     MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1092     ret = SetLocaleInfoW( lcid, lctype, strW );
1093     HeapFree( GetProcessHeap(), 0, strW );
1094     return ret;
1095 }
1096
1097
1098 /******************************************************************************
1099  *              SetLocaleInfoW  (KERNEL32.@)
1100  *
1101  * See SetLocaleInfoA.
1102  */
1103 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1104 {
1105     const WCHAR *value;
1106     static const WCHAR intlW[] = {'i','n','t','l',0 };
1107     UNICODE_STRING valueW;
1108     NTSTATUS status;
1109     HKEY hkey;
1110
1111     lcid = ConvertDefaultLocale(lcid);
1112
1113     lctype &= 0xffff;
1114     value = get_locale_value_name( lctype );
1115
1116     if (!data || !value)
1117     {
1118         SetLastError( ERROR_INVALID_PARAMETER );
1119         return FALSE;
1120     }
1121
1122     if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1123     {
1124         SetLastError( ERROR_INVALID_FLAGS );
1125         return FALSE;
1126     }
1127
1128     if (lcid != GetUserDefaultLCID())
1129     {
1130         /* Windows does not check that the lcid matches the current lcid */
1131         WARN("locale 0x%08lx isn't the current locale (0x%08lx), setting anyway!\n",
1132              lcid, GetUserDefaultLCID());
1133     }
1134
1135     TRACE("setting %lx (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1136
1137     /* FIXME: should check that data to set is sane */
1138
1139     /* FIXME: profile functions should map to registry */
1140     WriteProfileStringW( intlW, value, data );
1141
1142     if (!(hkey = create_registry_key())) return FALSE;
1143     RtlInitUnicodeString( &valueW, value );
1144     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1145
1146     if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1147     {
1148       /* Set I-value from S value */
1149       WCHAR *lpD, *lpM, *lpY;
1150       WCHAR szBuff[2];
1151
1152       lpD = strrchrW(data, 'd');
1153       lpM = strrchrW(data, 'M');
1154       lpY = strrchrW(data, 'y');
1155
1156       if (lpD <= lpM)
1157       {
1158         szBuff[0] = '1'; /* D-M-Y */
1159       }
1160       else
1161       {
1162         if (lpY <= lpM)
1163           szBuff[0] = '2'; /* Y-M-D */
1164         else
1165           szBuff[0] = '0'; /* M-D-Y */
1166       }
1167
1168       szBuff[1] = '\0';
1169
1170       if (lctype == LOCALE_SSHORTDATE)
1171         lctype = LOCALE_IDATE;
1172       else
1173         lctype = LOCALE_ILDATE;
1174
1175       value = get_locale_value_name( lctype );
1176
1177       WriteProfileStringW( intlW, value, szBuff );
1178
1179       RtlInitUnicodeString( &valueW, value );
1180       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1181     }
1182
1183     NtClose( hkey );
1184
1185     if (status) SetLastError( RtlNtStatusToDosError(status) );
1186     return !status;
1187 }
1188
1189
1190 /******************************************************************************
1191  *              GetACP   (KERNEL32.@)
1192  *
1193  * Get the current Ansi code page Id for the system.
1194  *
1195  * PARAMS
1196  *  None.
1197  *
1198  * RETURNS
1199  *    The current Ansi code page identifier for the system.
1200  */
1201 UINT WINAPI GetACP(void)
1202 {
1203     assert( ansi_cptable );
1204     return ansi_cptable->info.codepage;
1205 }
1206
1207
1208 /******************************************************************************
1209  *              SetCPGlobal   (KERNEL32.@)
1210  *
1211  * Set the current Ansi code page Id for the system.
1212  *
1213  * PARAMS
1214  *    acp [I] code page ID to be the new ACP.
1215  *
1216  * RETURNS
1217  *    The previous ACP.
1218  */
1219 UINT WINAPI SetCPGlobal( UINT acp )
1220 {
1221     UINT ret = GetACP();
1222     const union cptable *new_cptable = wine_cp_get_table( acp );
1223
1224     if (new_cptable) ansi_cptable = new_cptable;
1225     return ret;
1226 }
1227
1228
1229 /***********************************************************************
1230  *              GetOEMCP   (KERNEL32.@)
1231  *
1232  * Get the current OEM code page Id for the system.
1233  *
1234  * PARAMS
1235  *  None.
1236  *
1237  * RETURNS
1238  *    The current OEM code page identifier for the system.
1239  */
1240 UINT WINAPI GetOEMCP(void)
1241 {
1242     assert( oem_cptable );
1243     return oem_cptable->info.codepage;
1244 }
1245
1246
1247 /***********************************************************************
1248  *           IsValidCodePage   (KERNEL32.@)
1249  *
1250  * Determine if a given code page identifier is valid.
1251  *
1252  * PARAMS
1253  *  codepage [I] Code page Id to verify.
1254  *
1255  * RETURNS
1256  *  TRUE, If codepage is valid and available on the system,
1257  *  FALSE otherwise.
1258  */
1259 BOOL WINAPI IsValidCodePage( UINT codepage )
1260 {
1261     switch(codepage) {
1262     case CP_UTF7:
1263     case CP_UTF8:
1264         return TRUE;
1265     default:
1266         return wine_cp_get_table( codepage ) != NULL;
1267     }
1268 }
1269
1270
1271 /***********************************************************************
1272  *           IsDBCSLeadByteEx   (KERNEL32.@)
1273  *
1274  * Determine if a character is a lead byte in a given code page.
1275  *
1276  * PARAMS
1277  *  codepage [I] Code page for the test.
1278  *  testchar [I] Character to test
1279  *
1280  * RETURNS
1281  *  TRUE, if testchar is a lead byte in codepage,
1282  *  FALSE otherwise.
1283  */
1284 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1285 {
1286     const union cptable *table = get_codepage_table( codepage );
1287     return table && is_dbcs_leadbyte( table, testchar );
1288 }
1289
1290
1291 /***********************************************************************
1292  *           IsDBCSLeadByte   (KERNEL32.@)
1293  *           IsDBCSLeadByte   (KERNEL.207)
1294  *
1295  * Determine if a character is a lead byte.
1296  *
1297  * PARAMS
1298  *  testchar [I] Character to test
1299  *
1300  * RETURNS
1301  *  TRUE, if testchar is a lead byte in the Ansii code page,
1302  *  FALSE otherwise.
1303  */
1304 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1305 {
1306     if (!ansi_cptable) return FALSE;
1307     return is_dbcs_leadbyte( ansi_cptable, testchar );
1308 }
1309
1310
1311 /***********************************************************************
1312  *           GetCPInfo   (KERNEL32.@)
1313  *
1314  * Get information about a code page.
1315  *
1316  * PARAMS
1317  *  codepage [I] Code page number
1318  *  cpinfo   [O] Destination for code page information
1319  *
1320  * RETURNS
1321  *  Success: TRUE. cpinfo is updated with the information about codepage.
1322  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1323  */
1324 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1325 {
1326     const union cptable *table;
1327
1328     if (!cpinfo)
1329     {
1330         SetLastError( ERROR_INVALID_PARAMETER );
1331         return FALSE;
1332     }
1333
1334     if (!(table = get_codepage_table( codepage )))
1335     {
1336         switch(codepage)
1337         {
1338             case CP_UTF7:
1339             case CP_UTF8:
1340                 cpinfo->DefaultChar[0] = 0x3f;
1341                 cpinfo->DefaultChar[1] = 0;
1342                 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1343                 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1344                 return TRUE;
1345         }
1346
1347         SetLastError( ERROR_INVALID_PARAMETER );
1348         return FALSE;
1349     }
1350     if (table->info.def_char & 0xff00)
1351     {
1352         cpinfo->DefaultChar[0] = table->info.def_char & 0xff00;
1353         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1354     }
1355     else
1356     {
1357         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1358         cpinfo->DefaultChar[1] = 0;
1359     }
1360     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1361         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1362     else
1363         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1364
1365     return TRUE;
1366 }
1367
1368 /***********************************************************************
1369  *           GetCPInfoExA   (KERNEL32.@)
1370  *
1371  * Get extended information about a code page.
1372  *
1373  * PARAMS
1374  *  codepage [I] Code page number
1375  *  dwFlags  [I] Reserved, must to 0.
1376  *  cpinfo   [O] Destination for code page information
1377  *
1378  * RETURNS
1379  *  Success: TRUE. cpinfo is updated with the information about codepage.
1380  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1381  */
1382 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1383 {
1384     CPINFOEXW cpinfoW;
1385
1386     if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1387       return FALSE;
1388
1389     /* the layout is the same except for CodePageName */
1390     memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1391     WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1392     return TRUE;
1393 }
1394
1395 /***********************************************************************
1396  *           GetCPInfoExW   (KERNEL32.@)
1397  *
1398  * Unicode version of GetCPInfoExA.
1399  */
1400 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1401 {
1402     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1403       return FALSE;
1404
1405     switch(codepage)
1406     {
1407         case CP_UTF7:
1408         {
1409             static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1410
1411             cpinfo->CodePage = CP_UTF7;
1412             cpinfo->UnicodeDefaultChar = 0x3f;
1413             strcpyW(cpinfo->CodePageName, utf7);
1414             break;
1415         }
1416
1417         case CP_UTF8:
1418         {
1419             static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1420
1421             cpinfo->CodePage = CP_UTF8;
1422             cpinfo->UnicodeDefaultChar = 0x3f;
1423             strcpyW(cpinfo->CodePageName, utf8);
1424             break;
1425         }
1426
1427         default:
1428         {
1429             const union cptable *table = get_codepage_table( codepage );
1430
1431             cpinfo->CodePage = table->info.codepage;
1432             cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1433             MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1434                                  sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1435             break;
1436         }
1437     }
1438     return TRUE;
1439 }
1440
1441 /***********************************************************************
1442  *              EnumSystemCodePagesA   (KERNEL32.@)
1443  *
1444  * Call a user defined function for every code page installed on the system.
1445  *
1446  * PARAMS
1447  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1448  *   flags            [I] Reserved, set to 0.
1449  *
1450  * RETURNS
1451  *  TRUE, If all code pages have been enumerated, or
1452  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1453  */
1454 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1455 {
1456     const union cptable *table;
1457     char buffer[10];
1458     int index = 0;
1459
1460     for (;;)
1461     {
1462         if (!(table = wine_cp_enum_table( index++ ))) break;
1463         sprintf( buffer, "%d", table->info.codepage );
1464         if (!lpfnCodePageEnum( buffer )) break;
1465     }
1466     return TRUE;
1467 }
1468
1469
1470 /***********************************************************************
1471  *              EnumSystemCodePagesW   (KERNEL32.@)
1472  *
1473  * See EnumSystemCodePagesA.
1474  */
1475 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1476 {
1477     const union cptable *table;
1478     WCHAR buffer[10], *p;
1479     int page, index = 0;
1480
1481     for (;;)
1482     {
1483         if (!(table = wine_cp_enum_table( index++ ))) break;
1484         p = buffer + sizeof(buffer)/sizeof(WCHAR);
1485         *--p = 0;
1486         page = table->info.codepage;
1487         do
1488         {
1489             *--p = '0' + (page % 10);
1490             page /= 10;
1491         } while( page );
1492         if (!lpfnCodePageEnum( p )) break;
1493     }
1494     return TRUE;
1495 }
1496
1497
1498 /***********************************************************************
1499  *              MultiByteToWideChar   (KERNEL32.@)
1500  *
1501  * Convert a multibyte character string into a Unicode string.
1502  *
1503  * PARAMS
1504  *   page   [I] Codepage character set to convert from
1505  *   flags  [I] Character mapping flags
1506  *   src    [I] Source string buffer
1507  *   srclen [I] Length of src, or -1 if src is NUL terminated
1508  *   dst    [O] Destination buffer
1509  *   dstlen [I] Length of dst, or 0 to compute the required length
1510  *
1511  * RETURNS
1512  *   Success: If dstlen > 0, the number of characters written to dst.
1513  *            If dstlen == 0, the number of characters needed to perform the
1514  *            conversion. In both cases the count includes the terminating NUL.
1515  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1516  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1517  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
1518  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1519  *            possible for src.
1520  */
1521 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1522                                 LPWSTR dst, INT dstlen )
1523 {
1524     const union cptable *table;
1525     int ret;
1526
1527     if (!src || (!dst && dstlen))
1528     {
1529         SetLastError( ERROR_INVALID_PARAMETER );
1530         return 0;
1531     }
1532
1533     if (srclen < 0) srclen = strlen(src) + 1;
1534
1535     if (flags & MB_USEGLYPHCHARS) FIXME("MB_USEGLYPHCHARS not supported\n");
1536
1537     switch(page)
1538     {
1539     case CP_SYMBOL:
1540         if( flags)
1541         {
1542             SetLastError( ERROR_INVALID_PARAMETER );
1543             return 0;
1544         }
1545         ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1546         break;
1547     case CP_UTF7:
1548         FIXME("UTF-7 not supported\n");
1549         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1550         return 0;
1551     case CP_UNIXCP:
1552         if (unix_cptable)
1553         {
1554             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1555             break;
1556         }
1557         /* fall through */
1558     case CP_UTF8:
1559         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1560         break;
1561     default:
1562         if (!(table = get_codepage_table( page )))
1563         {
1564             SetLastError( ERROR_INVALID_PARAMETER );
1565             return 0;
1566         }
1567         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1568         break;
1569     }
1570
1571     if (ret < 0)
1572     {
1573         switch(ret)
1574         {
1575         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1576         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1577         }
1578         ret = 0;
1579     }
1580     return ret;
1581 }
1582
1583
1584 /***********************************************************************
1585  *              WideCharToMultiByte   (KERNEL32.@)
1586  *
1587  * Convert a Unicode character string into a multibyte string.
1588  *
1589  * PARAMS
1590  *   page    [I] Code page character set to convert to
1591  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
1592  *   src     [I] Source string buffer
1593  *   srclen  [I] Length of src, or -1 if src is NUL terminated
1594  *   dst     [O] Destination buffer
1595  *   dstlen  [I] Length of dst, or 0 to compute the required length
1596  *   defchar [I] Default character to use for conversion if no exact
1597  *                  conversion can be made
1598  *   used    [O] Set if default character was used in the conversion
1599  *
1600  * RETURNS
1601  *   Success: If dstlen > 0, the number of characters written to dst.
1602  *            If dstlen == 0, number of characters needed to perform the
1603  *            conversion. In both cases the count includes the terminating NUL.
1604  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1605  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1606  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1607  *            parameter was given.
1608  */
1609 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1610                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1611 {
1612     const union cptable *table;
1613     int ret, used_tmp;
1614
1615     if (!src || (!dst && dstlen))
1616     {
1617         SetLastError( ERROR_INVALID_PARAMETER );
1618         return 0;
1619     }
1620
1621     if (srclen < 0) srclen = strlenW(src) + 1;
1622
1623     switch(page)
1624     {
1625     case CP_SYMBOL:
1626         if( flags || defchar || used)
1627         {
1628             SetLastError( ERROR_INVALID_PARAMETER );
1629             return 0;
1630         }
1631         ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
1632         break;
1633     case CP_UTF7:
1634         FIXME("UTF-7 not supported\n");
1635         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1636         return 0;
1637     case CP_UNIXCP:
1638         if (unix_cptable)
1639         {
1640             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
1641                                     defchar, used ? &used_tmp : NULL );
1642             break;
1643         }
1644         /* fall through */
1645     case CP_UTF8:
1646         if (used) *used = FALSE;  /* all chars are valid for UTF-8 */
1647         ret = wine_utf8_wcstombs( src, srclen, dst, dstlen );
1648         break;
1649     default:
1650         if (!(table = get_codepage_table( page )))
1651         {
1652             SetLastError( ERROR_INVALID_PARAMETER );
1653             return 0;
1654         }
1655         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
1656                                 defchar, used ? &used_tmp : NULL );
1657         if (used) *used = used_tmp;
1658         break;
1659     }
1660
1661     if (ret < 0)
1662     {
1663         switch(ret)
1664         {
1665         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1666         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1667         }
1668         ret = 0;
1669     }
1670     TRACE("cp %d %s -> %s\n", page, debugstr_w(src), debugstr_a(dst));
1671     return ret;
1672 }
1673
1674
1675 /***********************************************************************
1676  *           GetThreadLocale    (KERNEL32.@)
1677  *
1678  * Get the current threads locale.
1679  *
1680  * PARAMS
1681  *  None.
1682  *
1683  * RETURNS
1684  *  The LCID currently assocated with the calling thread.
1685  */
1686 LCID WINAPI GetThreadLocale(void)
1687 {
1688     LCID ret = NtCurrentTeb()->CurrentLocale;
1689     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
1690     return ret;
1691 }
1692
1693 /**********************************************************************
1694  *           SetThreadLocale    (KERNEL32.@)
1695  *
1696  * Set the current threads locale.
1697  *
1698  * PARAMS
1699  *  lcid [I] LCID of the locale to set
1700  *
1701  * RETURNS
1702  *  Success: TRUE. The threads locale is set to lcid.
1703  *  Failure: FALSE. Use GetLastError() to determine the cause.
1704  */
1705 BOOL WINAPI SetThreadLocale( LCID lcid )
1706 {
1707     TRACE("(0x%04lX)\n", lcid);
1708
1709     lcid = ConvertDefaultLocale(lcid);
1710
1711     if (lcid != GetThreadLocale())
1712     {
1713         if (!IsValidLocale(lcid, LCID_SUPPORTED))
1714         {
1715             SetLastError(ERROR_INVALID_PARAMETER);
1716             return FALSE;
1717         }
1718
1719         NtCurrentTeb()->CurrentLocale = lcid;
1720         NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
1721     }
1722     return TRUE;
1723 }
1724
1725 /******************************************************************************
1726  *              ConvertDefaultLocale (KERNEL32.@)
1727  *
1728  * Convert a default locale identifier into a real identifier.
1729  *
1730  * PARAMS
1731  *  lcid [I] LCID identifier of the locale to convert
1732  *
1733  * RETURNS
1734  *  lcid unchanged, if not a default locale or its sublanguage is
1735  *   not SUBLANG_NEUTRAL.
1736  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
1737  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
1738  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
1739  */
1740 LCID WINAPI ConvertDefaultLocale( LCID lcid )
1741 {
1742     LANGID langid;
1743
1744     switch (lcid)
1745     {
1746     case LOCALE_SYSTEM_DEFAULT:
1747         lcid = GetSystemDefaultLCID();
1748         break;
1749     case LOCALE_USER_DEFAULT:
1750     case LOCALE_NEUTRAL:
1751         lcid = GetUserDefaultLCID();
1752         break;
1753     default:
1754         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
1755         langid = LANGIDFROMLCID(lcid);
1756         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
1757         {
1758           langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
1759           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
1760         }
1761     }
1762     return lcid;
1763 }
1764
1765
1766 /******************************************************************************
1767  *           IsValidLocale   (KERNEL32.@)
1768  *
1769  * Determine if a locale is valid.
1770  *
1771  * PARAMS
1772  *  lcid  [I] LCID of the locale to check
1773  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
1774  *
1775  * RETURN
1776  *  TRUE,  if lcid is valid,
1777  *  FALSE, otherwise.
1778  *
1779  * NOTES
1780  *  Wine does not currently make the distinction between supported and installed. All
1781  *  languages supported are installed by default.
1782  */
1783 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
1784 {
1785     /* check if language is registered in the kernel32 resources */
1786     return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1787                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
1788 }
1789
1790
1791 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
1792                                        LPCSTR name, WORD LangID, LONG lParam )
1793 {
1794     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
1795     char buf[20];
1796
1797     sprintf(buf, "%08x", (UINT)LangID);
1798     return lpfnLocaleEnum( buf );
1799 }
1800
1801 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
1802                                        LPCWSTR name, WORD LangID, LONG lParam )
1803 {
1804     static const WCHAR formatW[] = {'%','0','8','x',0};
1805     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
1806     WCHAR buf[20];
1807     sprintfW( buf, formatW, (UINT)LangID );
1808     return lpfnLocaleEnum( buf );
1809 }
1810
1811 /******************************************************************************
1812  *           EnumSystemLocalesA  (KERNEL32.@)
1813  *
1814  * Call a users function for each locale available on the system.
1815  *
1816  * PARAMS
1817  *  lpfnLocaleEnum [I] Callback function to call for each locale
1818  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
1819  *
1820  * RETURNS
1821  *  Success: TRUE.
1822  *  Failure: FALSE. Use GetLastError() to determine the cause.
1823  */
1824 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
1825 {
1826     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1827     EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
1828                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
1829                             (LONG)lpfnLocaleEnum);
1830     return TRUE;
1831 }
1832
1833
1834 /******************************************************************************
1835  *           EnumSystemLocalesW  (KERNEL32.@)
1836  *
1837  * See EnumSystemLocalesA.
1838  */
1839 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
1840 {
1841     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1842     EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
1843                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
1844                             (LONG)lpfnLocaleEnum);
1845     return TRUE;
1846 }
1847
1848
1849 /***********************************************************************
1850  *           VerLanguageNameA  (KERNEL32.@)
1851  *
1852  * Get the name of a language.
1853  *
1854  * PARAMS
1855  *  wLang  [I] LANGID of the language
1856  *  szLang [O] Destination for the language name
1857  *
1858  * RETURNS
1859  *  Success: The size of the language name. If szLang is non-NULL, it is filled
1860  *           with the name.
1861  *  Failure: 0. Use GetLastError() to determine the cause.
1862  *
1863  */
1864 DWORD WINAPI VerLanguageNameA( UINT wLang, LPSTR szLang, UINT nSize )
1865 {
1866     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1867 }
1868
1869
1870 /***********************************************************************
1871  *           VerLanguageNameW  (KERNEL32.@)
1872  *
1873  * See VerLanguageNameA.
1874  */
1875 DWORD WINAPI VerLanguageNameW( UINT wLang, LPWSTR szLang, UINT nSize )
1876 {
1877     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1878 }
1879
1880
1881 /******************************************************************************
1882  *           GetStringTypeW    (KERNEL32.@)
1883  *
1884  * See GetStringTypeA.
1885  */
1886 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1887 {
1888     if (count == -1) count = strlenW(src) + 1;
1889     switch(type)
1890     {
1891     case CT_CTYPE1:
1892         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
1893         break;
1894     case CT_CTYPE2:
1895         while (count--) *chartype++ = get_char_typeW( *src++ ) >> 12;
1896         break;
1897     case CT_CTYPE3:
1898     {
1899         WARN("CT_CTYPE3: semi-stub.\n");
1900         while (count--)
1901         {
1902             int c = *src;
1903             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
1904
1905             type1 = get_char_typeW( *src++ ) & 0xfff;
1906             /* try to construct type3 from type1 */
1907             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
1908             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
1909             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
1910             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
1911             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
1912             if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
1913             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
1914
1915             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
1916             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
1917             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
1918             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
1919             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
1920             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
1921             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
1922             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
1923
1924             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
1925             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
1926             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
1927             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
1928             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
1929             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
1930             *chartype++ = type3;
1931         }
1932         break;
1933     }
1934     default:
1935         SetLastError( ERROR_INVALID_PARAMETER );
1936         return FALSE;
1937     }
1938     return TRUE;
1939 }
1940
1941
1942 /******************************************************************************
1943  *           GetStringTypeExW    (KERNEL32.@)
1944  *
1945  * See GetStringTypeExA.
1946  */
1947 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1948 {
1949     /* locale is ignored for Unicode */
1950     return GetStringTypeW( type, src, count, chartype );
1951 }
1952
1953
1954 /******************************************************************************
1955  *           GetStringTypeA    (KERNEL32.@)
1956  *
1957  * Get characteristics of the characters making up a string.
1958  *
1959  * PARAMS
1960  *  locale   [I] Locale Id for the string
1961  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
1962  *  src      [I] String to analyse
1963  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
1964  *  chartype [O] Destination for the calculated characteristics
1965  *
1966  * RETURNS
1967  *  Success: TRUE. chartype is filled with the requested characteristics of each char
1968  *           in src.
1969  *  Failure: FALSE. Use GetLastError() to determine the cause.
1970  */
1971 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
1972 {
1973     UINT cp;
1974     INT countW;
1975     LPWSTR srcW;
1976     BOOL ret = FALSE;
1977
1978     if(count == -1) count = strlen(src) + 1;
1979
1980     if (!(cp = get_lcid_codepage( locale )))
1981     {
1982         FIXME("For locale %04lx using current ANSI code page\n", locale);
1983         cp = GetACP();
1984     }
1985
1986     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
1987     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
1988     {
1989         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
1990     /*
1991      * NOTE: the target buffer has 1 word for each CHARACTER in the source
1992      * string, with multibyte characters there maybe be more bytes in count
1993      * than character space in the buffer!
1994      */
1995         ret = GetStringTypeW(type, srcW, countW, chartype);
1996         HeapFree(GetProcessHeap(), 0, srcW);
1997     }
1998     return ret;
1999 }
2000
2001 /******************************************************************************
2002  *           GetStringTypeExA    (KERNEL32.@)
2003  *
2004  * Get characteristics of the characters making up a string.
2005  *
2006  * PARAMS
2007  *  locale   [I] Locale Id for the string
2008  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2009  *  src      [I] String to analyse
2010  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
2011  *  chartype [O] Destination for the calculated characteristics
2012  *
2013  * RETURNS
2014  *  Success: TRUE. chartype is filled with the requested characteristics of each char
2015  *           in src.
2016  *  Failure: FALSE. Use GetLastError() to determine the cause.
2017  */
2018 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2019 {
2020     return GetStringTypeA(locale, type, src, count, chartype);
2021 }
2022
2023
2024 /*************************************************************************
2025  *           LCMapStringW    (KERNEL32.@)
2026  *
2027  * See LCMapStringA.
2028  */
2029 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2030                         LPWSTR dst, INT dstlen)
2031 {
2032     LPWSTR dst_ptr;
2033
2034     if (!src || !srclen || dstlen < 0)
2035     {
2036         SetLastError(ERROR_INVALID_PARAMETER);
2037         return 0;
2038     }
2039
2040     /* mutually exclusive flags */
2041     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2042         (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2043         (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2044         (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2045     {
2046         SetLastError(ERROR_INVALID_FLAGS);
2047         return 0;
2048     }
2049
2050     if (!dstlen) dst = NULL;
2051
2052     lcid = ConvertDefaultLocale(lcid);
2053
2054     if (flags & LCMAP_SORTKEY)
2055     {
2056         if (src == dst)
2057         {
2058             SetLastError(ERROR_INVALID_FLAGS);
2059             return 0;
2060         }
2061
2062         if (srclen < 0) srclen = strlenW(src);
2063
2064         TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
2065               lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2066
2067         return wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2068     }
2069
2070     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2071     if (flags & SORT_STRINGSORT)
2072     {
2073         SetLastError(ERROR_INVALID_FLAGS);
2074         return 0;
2075     }
2076
2077     if (srclen < 0) srclen = strlenW(src) + 1;
2078
2079     TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
2080           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2081
2082     if (!dst) /* return required string length */
2083     {
2084         INT len;
2085
2086         for (len = 0; srclen; src++, srclen--)
2087         {
2088             WCHAR wch = *src;
2089             /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2090              * and skips white space and punctuation characters for
2091              * NORM_IGNORESYMBOLS.
2092              */
2093             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2094                 continue;
2095             len++;
2096         }
2097         return len;
2098     }
2099
2100     if (flags & LCMAP_UPPERCASE)
2101     {
2102         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2103         {
2104             WCHAR wch = *src;
2105             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2106                 continue;
2107             *dst_ptr++ = toupperW(wch);
2108             dstlen--;
2109         }
2110     }
2111     else if (flags & LCMAP_LOWERCASE)
2112     {
2113         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2114         {
2115             WCHAR wch = *src;
2116             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2117                 continue;
2118             *dst_ptr++ = tolowerW(wch);
2119             dstlen--;
2120         }
2121     }
2122     else
2123     {
2124         if (src == dst)
2125         {
2126             SetLastError(ERROR_INVALID_FLAGS);
2127             return 0;
2128         }
2129         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2130         {
2131             WCHAR wch = *src;
2132             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2133                 continue;
2134             *dst_ptr++ = wch;
2135             dstlen--;
2136         }
2137     }
2138
2139     if (srclen)
2140     {
2141         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2142         return 0;
2143     }
2144
2145     return dst_ptr - dst;
2146 }
2147
2148 /*************************************************************************
2149  *           LCMapStringA    (KERNEL32.@)
2150  *
2151  * Map characters in a locale sensitive string.
2152  *
2153  * PARAMS
2154  *  lcid   [I] LCID for the conversion.
2155  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2156  *  src    [I] String to map
2157  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
2158  *  dst    [O] Destination for mapped string
2159  *  dstlen [I] Length of dst in characters
2160  *
2161  * RETURNS
2162  *  Success: The length of the mapped string in dst, including the NUL terminator.
2163  *  Failure: 0. Use GetLastError() to determine the cause.
2164  */
2165 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2166                         LPSTR dst, INT dstlen)
2167 {
2168     WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2169     LPWSTR srcW, dstW;
2170     INT ret = 0, srclenW, dstlenW;
2171     UINT locale_cp = CP_ACP;
2172
2173     if (!src || !srclen || dstlen < 0)
2174     {
2175         SetLastError(ERROR_INVALID_PARAMETER);
2176         return 0;
2177     }
2178
2179     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2180
2181     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2182     if (srclenW)
2183         srcW = bufW;
2184     else
2185     {
2186         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2187         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2188         if (!srcW)
2189         {
2190             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2191             return 0;
2192         }
2193         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2194     }
2195
2196     if (flags & LCMAP_SORTKEY)
2197     {
2198         if (src == dst)
2199         {
2200             SetLastError(ERROR_INVALID_FLAGS);
2201             goto map_string_exit;
2202         }
2203         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2204         goto map_string_exit;
2205     }
2206
2207     if (flags & SORT_STRINGSORT)
2208     {
2209         SetLastError(ERROR_INVALID_FLAGS);
2210         goto map_string_exit;
2211     }
2212
2213     dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
2214     if (!dstlenW)
2215         goto map_string_exit;
2216
2217     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2218     if (!dstW)
2219     {
2220         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2221         goto map_string_exit;
2222     }
2223
2224     LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
2225     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2226     HeapFree(GetProcessHeap(), 0, dstW);
2227
2228 map_string_exit:
2229     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2230     return ret;
2231 }
2232
2233 /*************************************************************************
2234  *           FoldStringA    (KERNEL32.@)
2235  *
2236  * Map characters in a string.
2237  *
2238  * PARAMS
2239  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2240  *  src     [I] String to map
2241  *  srclen  [I] Length of src, or -1 if src is NUL terminated
2242  *  dst     [O] Destination for mapped string
2243  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
2244  *
2245  * RETURNS
2246  *  Success: The length of the string written to dst, including the terminating NUL. If
2247  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
2248  *           and dst may be NULL.
2249  *  Failure: 0. Use GetLastError() to determine the cause.
2250  */
2251 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2252                        LPSTR dst, INT dstlen)
2253 {
2254     INT ret = 0, srclenW = 0;
2255     WCHAR *srcW = NULL, *dstW = NULL;
2256
2257     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2258     {
2259         SetLastError(ERROR_INVALID_PARAMETER);
2260         return 0;
2261     }
2262
2263     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2264                                   src, srclen, NULL, 0);
2265     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2266
2267     if (!srcW)
2268     {
2269         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2270         goto FoldStringA_exit;
2271     }
2272
2273     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2274                         src, srclen, srcW, srclenW);
2275
2276     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2277
2278     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2279     if (ret && dstlen)
2280     {
2281         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2282
2283         if (!dstW)
2284         {
2285             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2286             goto FoldStringA_exit;
2287         }
2288
2289         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2290         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2291         {
2292             ret = 0;
2293             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2294         }
2295     }
2296
2297     HeapFree(GetProcessHeap(), 0, dstW);
2298
2299 FoldStringA_exit:
2300     HeapFree(GetProcessHeap(), 0, srcW);
2301     return ret;
2302 }
2303
2304 /*************************************************************************
2305  *           FoldStringW    (KERNEL32.@)
2306  *
2307  * See FoldStringA.
2308  */
2309 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2310                        LPWSTR dst, INT dstlen)
2311 {
2312     int ret;
2313
2314     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2315     {
2316     case 0:
2317         if (dwFlags)
2318           break;
2319         /* Fall through for dwFlags == 0 */
2320     case MAP_PRECOMPOSED|MAP_COMPOSITE:
2321     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2322     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2323         SetLastError(ERROR_INVALID_FLAGS);
2324         return 0;
2325     }
2326
2327     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2328     {
2329         SetLastError(ERROR_INVALID_PARAMETER);
2330         return 0;
2331     }
2332
2333     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2334     if (!ret)
2335         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2336     return ret;
2337 }
2338
2339 /******************************************************************************
2340  *           CompareStringW    (KERNEL32.@)
2341  *
2342  * See CompareStringA.
2343  */
2344 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2345                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2346 {
2347     INT ret;
2348
2349     if (!str1 || !str2)
2350     {
2351         SetLastError(ERROR_INVALID_PARAMETER);
2352         return 0;
2353     }
2354
2355     if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2356         SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2357     {
2358         SetLastError(ERROR_INVALID_FLAGS);
2359         return 0;
2360     }
2361
2362     if (style & 0x10000000)
2363         FIXME("Ignoring unknown style 0x10000000\n");
2364
2365     if (len1 < 0) len1 = strlenW(str1);
2366     if (len2 < 0) len2 = strlenW(str2);
2367
2368     ret = wine_compare_string(style, str1, len1, str2, len2);
2369
2370     if (ret) /* need to translate result */
2371         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2372     return CSTR_EQUAL;
2373 }
2374
2375 /******************************************************************************
2376  *           CompareStringA    (KERNEL32.@)
2377  *
2378  * Compare two locale sensitive strings.
2379  *
2380  * PARAMS
2381  *  lcid  [I] LCID for the comparison
2382  *  style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2383  *  str1  [I] First string to compare
2384  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
2385  *  str2  [I] Second string to compare
2386  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
2387  *
2388  * RETURNS
2389  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2390  *           str2 is less than, equal to or greater than str1 respectively.
2391  *  Failure: FALSE. Use GetLastError() to determine the cause.
2392  */
2393 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2394                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2395 {
2396     WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2397     WCHAR *buf2W = buf1W + 130;
2398     LPWSTR str1W, str2W;
2399     INT len1W, len2W, ret;
2400     UINT locale_cp = CP_ACP;
2401
2402     if (!str1 || !str2)
2403     {
2404         SetLastError(ERROR_INVALID_PARAMETER);
2405         return 0;
2406     }
2407     if (len1 < 0) len1 = strlen(str1);
2408     if (len2 < 0) len2 = strlen(str2);
2409
2410     if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2411
2412     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2413     if (len1W)
2414         str1W = buf1W;
2415     else
2416     {
2417         len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2418         str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2419         if (!str1W)
2420         {
2421             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2422             return 0;
2423         }
2424         MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2425     }
2426     len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2427     if (len2W)
2428         str2W = buf2W;
2429     else
2430     {
2431         len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2432         str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2433         if (!str2W)
2434         {
2435             if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2436             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2437             return 0;
2438         }
2439         MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2440     }
2441
2442     ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2443
2444     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2445     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2446     return ret;
2447 }
2448
2449 /*************************************************************************
2450  *           lstrcmp     (KERNEL32.@)
2451  *           lstrcmpA    (KERNEL32.@)
2452  *
2453  * Compare two strings using the current thread locale.
2454  *
2455  * PARAMS
2456  *  str1  [I] First string to compare
2457  *  str2  [I] Second string to compare
2458  *
2459  * RETURNS
2460  *  Success: A number less than, equal to or greater than 0 depending on whether
2461  *           str2 is less than, equal to or greater than str1 respectively.
2462  *  Failure: FALSE. Use GetLastError() to determine the cause.
2463  */
2464 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2465 {
2466     int ret;
2467     
2468     if ((str1 == NULL) && (str2 == NULL)) return 0;
2469     if (str1 == NULL) return -1;
2470     if (str2 == NULL) return 1;
2471
2472     ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2473     if (ret) ret -= 2;
2474     
2475     return ret;
2476 }
2477
2478 /*************************************************************************
2479  *           lstrcmpi     (KERNEL32.@)
2480  *           lstrcmpiA    (KERNEL32.@)
2481  *
2482  * Compare two strings using the current thread locale, ignoring case.
2483  *
2484  * PARAMS
2485  *  str1  [I] First string to compare
2486  *  str2  [I] Second string to compare
2487  *
2488  * RETURNS
2489  *  Success: A number less than, equal to or greater than 0 depending on whether
2490  *           str2 is less than, equal to or greater than str1 respectively.
2491  *  Failure: FALSE. Use GetLastError() to determine the cause.
2492  */
2493 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
2494 {
2495     int ret;
2496     
2497     if ((str1 == NULL) && (str2 == NULL)) return 0;
2498     if (str1 == NULL) return -1;
2499     if (str2 == NULL) return 1;
2500
2501     ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2502     if (ret) ret -= 2;
2503     
2504     return ret;
2505 }
2506
2507 /*************************************************************************
2508  *           lstrcmpW    (KERNEL32.@)
2509  *
2510  * See lstrcmpA.
2511  */
2512 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
2513 {
2514     int ret;
2515
2516     if ((str1 == NULL) && (str2 == NULL)) return 0;
2517     if (str1 == NULL) return -1;
2518     if (str2 == NULL) return 1;
2519
2520     ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
2521     if (ret) ret -= 2;
2522     
2523     return ret;
2524 }
2525
2526 /*************************************************************************
2527  *           lstrcmpiW    (KERNEL32.@)
2528  *
2529  * See lstrcmpiA.
2530  */
2531 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
2532 {
2533     int ret;
2534     
2535     if ((str1 == NULL) && (str2 == NULL)) return 0;
2536     if (str1 == NULL) return -1;
2537     if (str2 == NULL) return 1;
2538
2539     ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2540     if (ret) ret -= 2;
2541     
2542     return ret;
2543 }
2544
2545 /******************************************************************************
2546  *              LOCALE_Init
2547  */
2548 void LOCALE_Init(void)
2549 {
2550     extern void __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
2551                                        const union cptable *unix_cp );
2552
2553     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp = ~0U;
2554     LCID lcid;
2555
2556     lcid = get_env_lcid( NULL, NULL );
2557     NtSetDefaultLocale( TRUE, lcid );
2558
2559     lcid = get_env_lcid( NULL, "LC_MESSAGES" );
2560     NtSetDefaultUILanguage( LANGIDFROMLCID(lcid) );
2561
2562     lcid = get_env_lcid( &unix_cp, "LC_CTYPE" );
2563     NtSetDefaultLocale( FALSE, lcid );
2564
2565     ansi_cp = get_lcid_codepage(lcid);
2566     GetLocaleInfoW( lcid, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
2567                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
2568     GetLocaleInfoW( lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
2569                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
2570     if (unix_cp == ~0U)
2571         GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
2572                     (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
2573
2574     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
2575         ansi_cptable = wine_cp_get_table( 1252 );
2576     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
2577         oem_cptable  = wine_cp_get_table( 437 );
2578     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
2579         mac_cptable  = wine_cp_get_table( 10000 );
2580     if (unix_cp != CP_UTF8)
2581     {
2582         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
2583             unix_cptable  = wine_cp_get_table( 28591 );
2584     }
2585
2586     __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
2587
2588     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
2589            ansi_cptable->info.codepage, oem_cptable->info.codepage,
2590            mac_cptable->info.codepage, unix_cp );
2591 }
2592
2593 static HKEY NLS_RegOpenKey(HKEY hRootKey, LPCWSTR szKeyName)
2594 {
2595     UNICODE_STRING keyName;
2596     OBJECT_ATTRIBUTES attr;
2597     HKEY hkey;
2598
2599     RtlInitUnicodeString( &keyName, szKeyName );
2600     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
2601
2602     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS)
2603         hkey = 0;
2604
2605     return hkey;
2606 }
2607
2608 static HKEY NLS_RegOpenSubKey(HKEY hRootKey, LPCWSTR szKeyName)
2609 {
2610     HKEY hKey = NLS_RegOpenKey(hRootKey, szKeyName);
2611
2612     if (hRootKey)
2613         NtClose( hRootKey );
2614
2615     return hKey;
2616 }
2617
2618 static BOOL NLS_RegEnumSubKey(HKEY hKey, UINT ulIndex, LPWSTR szKeyName,
2619                               ULONG keyNameSize)
2620 {
2621     BYTE buffer[80];
2622     KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
2623     DWORD dwLen;
2624
2625     if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
2626                         sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
2627         info->NameLength > keyNameSize)
2628     {
2629         return FALSE;
2630     }
2631
2632     TRACE("info->Name %s info->NameLength %ld\n", debugstr_w(info->Name), info->NameLength);
2633
2634     memcpy( szKeyName, info->Name, info->NameLength);
2635     szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
2636
2637     TRACE("returning %s\n", debugstr_w(szKeyName));
2638     return TRUE;
2639 }
2640
2641 static BOOL NLS_RegEnumValue(HKEY hKey, UINT ulIndex,
2642                              LPWSTR szValueName, ULONG valueNameSize,
2643                              LPWSTR szValueData, ULONG valueDataSize)
2644 {
2645     BYTE buffer[80];
2646     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
2647     DWORD dwLen;
2648
2649     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
2650         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
2651         info->NameLength > valueNameSize ||
2652         info->DataLength > valueDataSize)
2653     {
2654         return FALSE;
2655     }
2656
2657     TRACE("info->Name %s info->DataLength %ld\n", debugstr_w(info->Name), info->DataLength);
2658
2659     memcpy( szValueName, info->Name, info->NameLength);
2660     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
2661     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
2662     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
2663
2664     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
2665     return TRUE;
2666 }
2667
2668 static BOOL NLS_RegGetDword(HKEY hKey, LPCWSTR szValueName, DWORD *lpVal)
2669 {
2670     BYTE buffer[128];
2671     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
2672     DWORD dwSize = sizeof(buffer);
2673     UNICODE_STRING valueName;
2674
2675     RtlInitUnicodeString( &valueName, szValueName );
2676
2677     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
2678     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
2679                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
2680         info->DataLength == sizeof(DWORD))
2681     {
2682         memcpy(lpVal, info->Data, sizeof(DWORD));
2683         return TRUE;
2684     }
2685
2686     return FALSE;
2687 }
2688
2689 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
2690 {
2691     LANGID  langId;
2692     LPCWSTR szResourceName = (LPCWSTR)(((lgrpid + 0x2000) >> 4) + 1);
2693     HRSRC   hResource;
2694     BOOL    bRet = FALSE;
2695
2696     /* FIXME: Is it correct to use the system default langid? */
2697     langId = GetSystemDefaultLangID();
2698
2699     if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
2700         langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
2701
2702     hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
2703
2704     if (hResource)
2705     {
2706         HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
2707
2708         if (hResDir)
2709         {
2710             ULONG   iResourceIndex = lgrpid & 0xf;
2711             LPCWSTR lpResEntry = LockResource( hResDir );
2712             ULONG   i;
2713
2714             for (i = 0; i < iResourceIndex; i++)
2715                 lpResEntry += *lpResEntry + 1;
2716
2717             if (*lpResEntry < nameSize)
2718             {
2719                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
2720                 szName[*lpResEntry] = '\0';
2721                 bRet = TRUE;
2722             }
2723
2724         }
2725         FreeResource( hResource );
2726     }
2727     return bRet;
2728 }
2729
2730 /* Registry keys for NLS related information */
2731 static const WCHAR szLangGroupsKeyName[] = {
2732     'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s','\0'
2733 };
2734
2735 static const WCHAR szCountryListName[] = {
2736     'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
2737     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
2738     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2739     'T','e','l','e','p','h','o','n','y','\\',
2740     'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
2741 };
2742
2743
2744 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
2745 typedef struct
2746 {
2747   LANGUAGEGROUP_ENUMPROCA procA;
2748   LANGUAGEGROUP_ENUMPROCW procW;
2749   DWORD    dwFlags;
2750   LONG_PTR lParam;
2751 } ENUMLANGUAGEGROUP_CALLBACKS;
2752
2753 /* Internal implementation of EnumSystemLanguageGroupsA/W */
2754 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
2755 {
2756     WCHAR szNumber[10], szValue[4];
2757     HKEY hKey;
2758     BOOL bContinue = TRUE;
2759     ULONG ulIndex = 0;
2760
2761     if (!lpProcs)
2762     {
2763         SetLastError(ERROR_INVALID_PARAMETER);
2764         return FALSE;
2765     }
2766
2767     switch (lpProcs->dwFlags)
2768     {
2769     case 0:
2770         /* Default to LGRPID_INSTALLED */
2771         lpProcs->dwFlags = LGRPID_INSTALLED;
2772         /* Fall through... */
2773     case LGRPID_INSTALLED:
2774     case LGRPID_SUPPORTED:
2775         break;
2776     default:
2777         SetLastError(ERROR_INVALID_FLAGS);
2778         return FALSE;
2779     }
2780
2781     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2782
2783     if (!hKey)
2784         FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
2785
2786     while (bContinue)
2787     {
2788         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2789                               szValue, sizeof(szValue) ))
2790         {
2791             BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
2792             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
2793
2794             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
2795                    bInstalled ? "" : "not ");
2796
2797             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
2798             {
2799                 WCHAR szGrpName[48];
2800
2801                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
2802                     szGrpName[0] = '\0';
2803
2804                 if (lpProcs->procW)
2805                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
2806                                                 lpProcs->lParam );
2807                 else
2808                 {
2809                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
2810                     char szGrpNameA[48];
2811
2812                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
2813                      *        or whether the language names are ever localised. Assume CP_ACP.
2814                      */
2815
2816                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
2817                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
2818
2819                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
2820                                                 lpProcs->lParam );
2821                 }
2822             }
2823
2824             ulIndex++;
2825         }
2826         else
2827             bContinue = FALSE;
2828
2829         if (!bContinue)
2830             break;
2831     }
2832
2833     if (hKey)
2834         NtClose( hKey );
2835
2836     return TRUE;
2837 }
2838
2839 /******************************************************************************
2840  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
2841  *
2842  * Call a users function for each language group available on the system.
2843  *
2844  * PARAMS
2845  *  pLangGrpEnumProc [I] Callback function to call for each language group
2846  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
2847  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
2848  *
2849  * RETURNS
2850  *  Success: TRUE.
2851  *  Failure: FALSE. Use GetLastError() to determine the cause.
2852  */
2853 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
2854                                       DWORD dwFlags, LONG_PTR lParam)
2855 {
2856     ENUMLANGUAGEGROUP_CALLBACKS procs;
2857
2858     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2859
2860     procs.procA = pLangGrpEnumProc;
2861     procs.procW = NULL;
2862     procs.dwFlags = dwFlags;
2863     procs.lParam = lParam;
2864
2865     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2866 }
2867
2868 /******************************************************************************
2869  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
2870  *
2871  * See EnumSystemLanguageGroupsA.
2872  */
2873 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
2874                                       DWORD dwFlags, LONG_PTR lParam)
2875 {
2876     ENUMLANGUAGEGROUP_CALLBACKS procs;
2877
2878     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2879
2880     procs.procA = NULL;
2881     procs.procW = pLangGrpEnumProc;
2882     procs.dwFlags = dwFlags;
2883     procs.lParam = lParam;
2884
2885     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2886 }
2887
2888 /******************************************************************************
2889  *           IsValidLanguageGroup    (KERNEL32.@)
2890  *
2891  * Determine if a language group is supported and/or installed.
2892  *
2893  * PARAMS
2894  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
2895  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
2896  *
2897  * RETURNS
2898  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
2899  *  FALSE otherwise.
2900  */
2901 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
2902 {
2903     static const WCHAR szFormat[] = { '%','x','\0' };
2904     WCHAR szValueName[16], szValue[2];
2905     BOOL bSupported = FALSE, bInstalled = FALSE;
2906     HKEY hKey;
2907
2908
2909     switch (dwFlags)
2910     {
2911     case LGRPID_INSTALLED:
2912     case LGRPID_SUPPORTED:
2913
2914         hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2915
2916         sprintfW( szValueName, szFormat, lgrpid );
2917
2918         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)&szValue ))
2919         {
2920             bSupported = TRUE;
2921
2922             if (szValue[0] == '1')
2923                 bInstalled = TRUE;
2924         }
2925
2926         if (hKey)
2927             NtClose( hKey );
2928
2929         break;
2930     }
2931
2932     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
2933         (dwFlags == LGRPID_INSTALLED && bInstalled))
2934         return TRUE;
2935
2936     return FALSE;
2937 }
2938
2939 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
2940 typedef struct
2941 {
2942   LANGGROUPLOCALE_ENUMPROCA procA;
2943   LANGGROUPLOCALE_ENUMPROCW procW;
2944   DWORD    dwFlags;
2945   LGRPID   lgrpid;
2946   LONG_PTR lParam;
2947 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
2948
2949 /* Internal implementation of EnumLanguageGrouplocalesA/W */
2950 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
2951 {
2952     static const WCHAR szLocaleKeyName[] = {
2953       'L','o','c','a','l','e','\0'
2954     };
2955     static const WCHAR szAlternateSortsKeyName[] = {
2956       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
2957     };
2958     WCHAR szNumber[10], szValue[4];
2959     HKEY hKey;
2960     BOOL bContinue = TRUE, bAlternate = FALSE;
2961     LGRPID lgrpid;
2962     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
2963
2964     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
2965     {
2966         SetLastError(ERROR_INVALID_PARAMETER);
2967         return FALSE;
2968     }
2969
2970     if (lpProcs->dwFlags)
2971     {
2972         SetLastError(ERROR_INVALID_FLAGS);
2973         return FALSE;
2974     }
2975
2976     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLocaleKeyName );
2977
2978     if (!hKey)
2979         WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
2980
2981     while (bContinue)
2982     {
2983         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2984                               szValue, sizeof(szValue) ))
2985         {
2986             lgrpid = strtoulW( szValue, NULL, 16 );
2987
2988             TRACE("lcid %s, grpid %ld (%smatched)\n", debugstr_w(szNumber),
2989                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
2990
2991             if (lgrpid == lpProcs->lgrpid)
2992             {
2993                 LCID lcid;
2994
2995                 lcid = strtoulW( szNumber, NULL, 16 );
2996
2997                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
2998                  * '00000437          ;Georgian'
2999                  * At present we only pass the LCID string.
3000                  */
3001
3002                 if (lpProcs->procW)
3003                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3004                 else
3005                 {
3006                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3007
3008                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3009
3010                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3011                 }
3012             }
3013
3014             ulIndex++;
3015         }
3016         else
3017         {
3018             /* Finished enumerating this key */
3019             if (!bAlternate)
3020             {
3021                 /* Enumerate alternate sorts also */
3022                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3023                 bAlternate = TRUE;
3024                 ulIndex = 0;
3025             }
3026             else
3027                 bContinue = FALSE; /* Finished both keys */
3028         }
3029
3030         if (!bContinue)
3031             break;
3032     }
3033
3034     if (hKey)
3035         NtClose( hKey );
3036
3037     return TRUE;
3038 }
3039
3040 /******************************************************************************
3041  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
3042  *
3043  * Call a users function for every locale in a language group available on the system.
3044  *
3045  * PARAMS
3046  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
3047  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
3048  *  dwFlags            [I] Reserved, set to 0
3049  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
3050  *
3051  * RETURNS
3052  *  Success: TRUE.
3053  *  Failure: FALSE. Use GetLastError() to determine the cause.
3054  */
3055 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3056                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3057 {
3058     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3059
3060     TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3061
3062     callbacks.procA   = pLangGrpLcEnumProc;
3063     callbacks.procW   = NULL;
3064     callbacks.dwFlags = dwFlags;
3065     callbacks.lgrpid  = lgrpid;
3066     callbacks.lParam  = lParam;
3067
3068     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3069 }
3070
3071 /******************************************************************************
3072  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
3073  *
3074  * See EnumLanguageGroupLocalesA.
3075  */
3076 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3077                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3078 {
3079     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3080
3081     TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3082
3083     callbacks.procA   = NULL;
3084     callbacks.procW   = pLangGrpLcEnumProc;
3085     callbacks.dwFlags = dwFlags;
3086     callbacks.lgrpid  = lgrpid;
3087     callbacks.lParam  = lParam;
3088
3089     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3090 }
3091
3092 /******************************************************************************
3093  *           EnumSystemGeoID    (KERNEL32.@)
3094  *
3095  * Call a users function for every location available on the system.
3096  *
3097  * PARAMS
3098  *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3099  *  reserved     [I] Reserved, set to 0
3100  *  pGeoEnumProc [I] Callback function to call for each location
3101  *
3102  * RETURNS
3103  *  Success: TRUE.
3104  *  Failure: FALSE. Use GetLastError() to determine the cause.
3105  */
3106 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3107 {
3108     static const WCHAR szCountryCodeValueName[] = {
3109       'C','o','u','n','t','r','y','C','o','d','e','\0'
3110     };
3111     WCHAR szNumber[10];
3112     HKEY hKey;
3113     ULONG ulIndex = 0;
3114
3115     TRACE("(0x%08lX,0x%08lX,%p)\n", geoclass, reserved, pGeoEnumProc);
3116
3117     if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3118     {
3119         SetLastError(ERROR_INVALID_PARAMETER);
3120         return FALSE;
3121     }
3122
3123     hKey = NLS_RegOpenKey( 0, szCountryListName );
3124
3125     while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3126     {
3127         BOOL bContinue = TRUE;
3128         DWORD dwGeoId;
3129         HKEY hSubKey = NLS_RegOpenKey( hKey, szNumber );
3130
3131         if (hSubKey)
3132         {
3133             if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3134             {
3135                 TRACE("Got geoid %ld\n", dwGeoId);
3136
3137                 if (!pGeoEnumProc( dwGeoId ))
3138                     bContinue = FALSE;
3139             }
3140
3141             NtClose( hSubKey );
3142         }
3143
3144         if (!bContinue)
3145             break;
3146
3147         ulIndex++;
3148     }
3149
3150     if (hKey)
3151         NtClose( hKey );
3152
3153     return TRUE;
3154 }
3155
3156 /******************************************************************************
3157  *           InvalidateNLSCache           (KERNEL32.@)
3158  *
3159  * Invalidate the cache of NLS values.
3160  *
3161  * PARAMS
3162  *  None.
3163  *
3164  * RETURNS
3165  *  Success: TRUE.
3166  *  Failure: FALSE.
3167  */
3168 BOOL WINAPI InvalidateNLSCache(void)
3169 {
3170   FIXME("() stub\n");
3171   return FALSE;
3172 }
3173
3174 /******************************************************************************
3175  *           GetUserGeoID (KERNEL32.@)
3176  */
3177 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3178 {
3179     FIXME("%ld\n",GeoClass);
3180     return GEOID_NOT_AVAILABLE;
3181 }
3182
3183 /******************************************************************************
3184  *           SetUserGeoID (KERNEL32.@)
3185  */
3186 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3187 {
3188     FIXME("%ld\n",GeoID);
3189     return FALSE;
3190 }
3191
3192 /******************************************************************************
3193  *           EnumUILanguagesA (KERNEL32.@)
3194  */
3195 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3196 {
3197     static char value[] = "0409";
3198
3199     if(!pUILangEnumProc) {
3200         SetLastError(ERROR_INVALID_PARAMETER);
3201         return FALSE;
3202     }
3203     if(dwFlags) {
3204         SetLastError(ERROR_INVALID_FLAGS);
3205         return FALSE;
3206     }
3207
3208     FIXME("%p, %lx, %lx calling pUILangEnumProc with %s\n",
3209           pUILangEnumProc, dwFlags, lParam, debugstr_a(value));
3210
3211     pUILangEnumProc( value, lParam );
3212     return(TRUE);
3213 }
3214
3215 /******************************************************************************
3216  *           EnumUILanguagesW (KERNEL32.@)
3217  */
3218 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3219 {
3220     static WCHAR value[] = {'0','4','0','9',0};
3221
3222     if(!pUILangEnumProc) {
3223         SetLastError(ERROR_INVALID_PARAMETER);
3224         return FALSE;
3225     }
3226     if(dwFlags) {
3227         SetLastError(ERROR_INVALID_FLAGS);
3228         return FALSE;
3229     }
3230
3231     FIXME("%p, %lx, %lx calling pUILangEnumProc with %s\n",
3232           pUILangEnumProc, dwFlags, lParam, debugstr_w(value));
3233
3234     pUILangEnumProc( value, lParam );
3235     return(TRUE);
3236 }