Fix direct sound capabilities to match hardware.
[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  *              GetOEMCP   (KERNEL32.@)
1210  *
1211  * Get the current OEM code page Id for the system.
1212  *
1213  * PARAMS
1214  *  None.
1215  *
1216  * RETURNS
1217  *    The current OEM code page identifier for the system.
1218  */
1219 UINT WINAPI GetOEMCP(void)
1220 {
1221     assert( oem_cptable );
1222     return oem_cptable->info.codepage;
1223 }
1224
1225
1226 /***********************************************************************
1227  *           IsValidCodePage   (KERNEL32.@)
1228  *
1229  * Determine if a given code page identifier is valid.
1230  *
1231  * PARAMS
1232  *  codepage [I] Code page Id to verify.
1233  *
1234  * RETURNS
1235  *  TRUE, If codepage is valid and available on the system,
1236  *  FALSE otherwise.
1237  */
1238 BOOL WINAPI IsValidCodePage( UINT codepage )
1239 {
1240     switch(codepage) {
1241     case CP_UTF7:
1242     case CP_UTF8:
1243         return TRUE;
1244     default:
1245         return wine_cp_get_table( codepage ) != NULL;
1246     }
1247 }
1248
1249
1250 /***********************************************************************
1251  *           IsDBCSLeadByteEx   (KERNEL32.@)
1252  *
1253  * Determine if a character is a lead byte in a given code page.
1254  *
1255  * PARAMS
1256  *  codepage [I] Code page for the test.
1257  *  testchar [I] Character to test
1258  *
1259  * RETURNS
1260  *  TRUE, if testchar is a lead byte in codepage,
1261  *  FALSE otherwise.
1262  */
1263 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1264 {
1265     const union cptable *table = get_codepage_table( codepage );
1266     return table && is_dbcs_leadbyte( table, testchar );
1267 }
1268
1269
1270 /***********************************************************************
1271  *           IsDBCSLeadByte   (KERNEL32.@)
1272  *           IsDBCSLeadByte   (KERNEL.207)
1273  *
1274  * Determine if a character is a lead byte.
1275  *
1276  * PARAMS
1277  *  testchar [I] Character to test
1278  *
1279  * RETURNS
1280  *  TRUE, if testchar is a lead byte in the Ansii code page,
1281  *  FALSE otherwise.
1282  */
1283 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1284 {
1285     if (!ansi_cptable) return FALSE;
1286     return is_dbcs_leadbyte( ansi_cptable, testchar );
1287 }
1288
1289
1290 /***********************************************************************
1291  *           GetCPInfo   (KERNEL32.@)
1292  *
1293  * Get information about a code page.
1294  *
1295  * PARAMS
1296  *  codepage [I] Code page number
1297  *  cpinfo   [O] Destination for code page information
1298  *
1299  * RETURNS
1300  *  Success: TRUE. cpinfo is updated with the information about codepage.
1301  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1302  */
1303 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1304 {
1305     const union cptable *table;
1306
1307     if (!cpinfo)
1308     {
1309         SetLastError( ERROR_INVALID_PARAMETER );
1310         return FALSE;
1311     }
1312
1313     if (!(table = get_codepage_table( codepage )))
1314     {
1315         switch(codepage)
1316         {
1317             case CP_UTF7:
1318             case CP_UTF8:
1319                 cpinfo->DefaultChar[0] = 0x3f;
1320                 cpinfo->DefaultChar[1] = 0;
1321                 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1322                 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1323                 return TRUE;
1324         }
1325
1326         SetLastError( ERROR_INVALID_PARAMETER );
1327         return FALSE;
1328     }
1329     if (table->info.def_char & 0xff00)
1330     {
1331         cpinfo->DefaultChar[0] = table->info.def_char & 0xff00;
1332         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1333     }
1334     else
1335     {
1336         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1337         cpinfo->DefaultChar[1] = 0;
1338     }
1339     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1340         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1341     else
1342         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1343
1344     return TRUE;
1345 }
1346
1347 /***********************************************************************
1348  *           GetCPInfoExA   (KERNEL32.@)
1349  *
1350  * Get extended information about a code page.
1351  *
1352  * PARAMS
1353  *  codepage [I] Code page number
1354  *  dwFlags  [I] Reserved, must to 0.
1355  *  cpinfo   [O] Destination for code page information
1356  *
1357  * RETURNS
1358  *  Success: TRUE. cpinfo is updated with the information about codepage.
1359  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1360  */
1361 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1362 {
1363     CPINFOEXW cpinfoW;
1364
1365     if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1366       return FALSE;
1367
1368     /* the layout is the same except for CodePageName */
1369     memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1370     WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1371     return TRUE;
1372 }
1373
1374 /***********************************************************************
1375  *           GetCPInfoExW   (KERNEL32.@)
1376  *
1377  * Unicode version of GetCPInfoExA.
1378  */
1379 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1380 {
1381     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1382       return FALSE;
1383
1384     switch(codepage)
1385     {
1386         case CP_UTF7:
1387         {
1388             static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1389
1390             cpinfo->CodePage = CP_UTF7;
1391             cpinfo->UnicodeDefaultChar = 0x3f;
1392             strcpyW(cpinfo->CodePageName, utf7);
1393             break;
1394         }
1395
1396         case CP_UTF8:
1397         {
1398             static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1399
1400             cpinfo->CodePage = CP_UTF8;
1401             cpinfo->UnicodeDefaultChar = 0x3f;
1402             strcpyW(cpinfo->CodePageName, utf8);
1403             break;
1404         }
1405
1406         default:
1407         {
1408             const union cptable *table = get_codepage_table( codepage );
1409
1410             cpinfo->CodePage = table->info.codepage;
1411             cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1412             MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1413                                  sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1414             break;
1415         }
1416     }
1417     return TRUE;
1418 }
1419
1420 /***********************************************************************
1421  *              EnumSystemCodePagesA   (KERNEL32.@)
1422  *
1423  * Call a user defined function for every code page installed on the system.
1424  *
1425  * PARAMS
1426  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1427  *   flags            [I] Reserved, set to 0.
1428  *
1429  * RETURNS
1430  *  TRUE, If all code pages have been enumerated, or
1431  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1432  */
1433 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1434 {
1435     const union cptable *table;
1436     char buffer[10];
1437     int index = 0;
1438
1439     for (;;)
1440     {
1441         if (!(table = wine_cp_enum_table( index++ ))) break;
1442         sprintf( buffer, "%d", table->info.codepage );
1443         if (!lpfnCodePageEnum( buffer )) break;
1444     }
1445     return TRUE;
1446 }
1447
1448
1449 /***********************************************************************
1450  *              EnumSystemCodePagesW   (KERNEL32.@)
1451  *
1452  * See EnumSystemCodePagesA.
1453  */
1454 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1455 {
1456     const union cptable *table;
1457     WCHAR buffer[10], *p;
1458     int page, index = 0;
1459
1460     for (;;)
1461     {
1462         if (!(table = wine_cp_enum_table( index++ ))) break;
1463         p = buffer + sizeof(buffer)/sizeof(WCHAR);
1464         *--p = 0;
1465         page = table->info.codepage;
1466         do
1467         {
1468             *--p = '0' + (page % 10);
1469             page /= 10;
1470         } while( page );
1471         if (!lpfnCodePageEnum( p )) break;
1472     }
1473     return TRUE;
1474 }
1475
1476
1477 /***********************************************************************
1478  *              MultiByteToWideChar   (KERNEL32.@)
1479  *
1480  * Convert a multibyte character string into a Unicode string.
1481  *
1482  * PARAMS
1483  *   page   [I] Codepage character set to convert from
1484  *   flags  [I] Character mapping flags
1485  *   src    [I] Source string buffer
1486  *   srclen [I] Length of src, or -1 if src is NUL terminated
1487  *   dst    [O] Destination buffer
1488  *   dstlen [I] Length of dst, or 0 to compute the required length
1489  *
1490  * RETURNS
1491  *   Success: If dstlen > 0, the number of characters written to dst.
1492  *            If dstlen == 0, the number of characters needed to perform the
1493  *            conversion. In both cases the count includes the terminating NUL.
1494  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1495  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1496  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
1497  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1498  *            possible for src.
1499  */
1500 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1501                                 LPWSTR dst, INT dstlen )
1502 {
1503     const union cptable *table;
1504     int ret;
1505
1506     if (!src || (!dst && dstlen))
1507     {
1508         SetLastError( ERROR_INVALID_PARAMETER );
1509         return 0;
1510     }
1511
1512     if (srclen < 0) srclen = strlen(src) + 1;
1513
1514     if (flags & MB_USEGLYPHCHARS) FIXME("MB_USEGLYPHCHARS not supported\n");
1515
1516     switch(page)
1517     {
1518     case CP_SYMBOL:
1519         if( flags)
1520         {
1521             SetLastError( ERROR_INVALID_PARAMETER );
1522             return 0;
1523         }
1524         ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1525         break;
1526     case CP_UTF7:
1527         FIXME("UTF-7 not supported\n");
1528         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1529         return 0;
1530     case CP_UNIXCP:
1531         if (unix_cptable)
1532         {
1533             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1534             break;
1535         }
1536         /* fall through */
1537     case CP_UTF8:
1538         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1539         break;
1540     default:
1541         if (!(table = get_codepage_table( page )))
1542         {
1543             SetLastError( ERROR_INVALID_PARAMETER );
1544             return 0;
1545         }
1546         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1547         break;
1548     }
1549
1550     if (ret < 0)
1551     {
1552         switch(ret)
1553         {
1554         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1555         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1556         }
1557         ret = 0;
1558     }
1559     return ret;
1560 }
1561
1562
1563 /***********************************************************************
1564  *              WideCharToMultiByte   (KERNEL32.@)
1565  *
1566  * Convert a Unicode character string into a multibyte string.
1567  *
1568  * PARAMS
1569  *   page    [I] Code page character set to convert to
1570  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
1571  *   src     [I] Source string buffer
1572  *   srclen  [I] Length of src, or -1 if src is NUL terminated
1573  *   dst     [O] Destination buffer
1574  *   dstlen  [I] Length of dst, or 0 to compute the required length
1575  *   defchar [I] Default character to use for conversion if no exact
1576  *                  conversion can be made
1577  *   used    [O] Set if default character was used in the conversion
1578  *
1579  * RETURNS
1580  *   Success: If dstlen > 0, the number of characters written to dst.
1581  *            If dstlen == 0, number of characters needed to perform the
1582  *            conversion. In both cases the count includes the terminating NUL.
1583  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1584  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1585  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1586  *            parameter was given.
1587  */
1588 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1589                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1590 {
1591     const union cptable *table;
1592     int ret, used_tmp;
1593
1594     if (!src || (!dst && dstlen))
1595     {
1596         SetLastError( ERROR_INVALID_PARAMETER );
1597         return 0;
1598     }
1599
1600     if (srclen < 0) srclen = strlenW(src) + 1;
1601
1602     switch(page)
1603     {
1604     case CP_SYMBOL:
1605         if( flags || defchar || used)
1606         {
1607             SetLastError( ERROR_INVALID_PARAMETER );
1608             return 0;
1609         }
1610         ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
1611         break;
1612     case CP_UTF7:
1613         FIXME("UTF-7 not supported\n");
1614         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1615         return 0;
1616     case CP_UNIXCP:
1617         if (unix_cptable)
1618         {
1619             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
1620                                     defchar, used ? &used_tmp : NULL );
1621             break;
1622         }
1623         /* fall through */
1624     case CP_UTF8:
1625         if (used) *used = FALSE;  /* all chars are valid for UTF-8 */
1626         ret = wine_utf8_wcstombs( src, srclen, dst, dstlen );
1627         break;
1628     default:
1629         if (!(table = get_codepage_table( page )))
1630         {
1631             SetLastError( ERROR_INVALID_PARAMETER );
1632             return 0;
1633         }
1634         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
1635                                 defchar, used ? &used_tmp : NULL );
1636         if (used) *used = used_tmp;
1637         break;
1638     }
1639
1640     if (ret < 0)
1641     {
1642         switch(ret)
1643         {
1644         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1645         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1646         }
1647         ret = 0;
1648     }
1649     TRACE("cp %d %s -> %s\n", page, debugstr_w(src), debugstr_a(dst));
1650     return ret;
1651 }
1652
1653
1654 /***********************************************************************
1655  *           GetThreadLocale    (KERNEL32.@)
1656  *
1657  * Get the current threads locale.
1658  *
1659  * PARAMS
1660  *  None.
1661  *
1662  * RETURNS
1663  *  The LCID currently assocated with the calling thread.
1664  */
1665 LCID WINAPI GetThreadLocale(void)
1666 {
1667     LCID ret = NtCurrentTeb()->CurrentLocale;
1668     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
1669     return ret;
1670 }
1671
1672 /**********************************************************************
1673  *           SetThreadLocale    (KERNEL32.@)
1674  *
1675  * Set the current threads locale.
1676  *
1677  * PARAMS
1678  *  lcid [I] LCID of the locale to set
1679  *
1680  * RETURNS
1681  *  Success: TRUE. The threads locale is set to lcid.
1682  *  Failure: FALSE. Use GetLastError() to determine the cause.
1683  */
1684 BOOL WINAPI SetThreadLocale( LCID lcid )
1685 {
1686     TRACE("(0x%04lX)\n", lcid);
1687
1688     lcid = ConvertDefaultLocale(lcid);
1689
1690     if (lcid != GetThreadLocale())
1691     {
1692         if (!IsValidLocale(lcid, LCID_SUPPORTED))
1693         {
1694             SetLastError(ERROR_INVALID_PARAMETER);
1695             return FALSE;
1696         }
1697
1698         NtCurrentTeb()->CurrentLocale = lcid;
1699         NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
1700     }
1701     return TRUE;
1702 }
1703
1704 /******************************************************************************
1705  *              ConvertDefaultLocale (KERNEL32.@)
1706  *
1707  * Convert a default locale identifier into a real identifier.
1708  *
1709  * PARAMS
1710  *  lcid [I] LCID identifier of the locale to convert
1711  *
1712  * RETURNS
1713  *  lcid unchanged, if not a default locale or its sublanguage is
1714  *   not SUBLANG_NEUTRAL.
1715  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
1716  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
1717  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
1718  */
1719 LCID WINAPI ConvertDefaultLocale( LCID lcid )
1720 {
1721     LANGID langid;
1722
1723     switch (lcid)
1724     {
1725     case LOCALE_SYSTEM_DEFAULT:
1726         lcid = GetSystemDefaultLCID();
1727         break;
1728     case LOCALE_USER_DEFAULT:
1729     case LOCALE_NEUTRAL:
1730         lcid = GetUserDefaultLCID();
1731         break;
1732     default:
1733         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
1734         langid = LANGIDFROMLCID(lcid);
1735         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
1736         {
1737           langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
1738           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
1739         }
1740     }
1741     return lcid;
1742 }
1743
1744
1745 /******************************************************************************
1746  *           IsValidLocale   (KERNEL32.@)
1747  *
1748  * Determine if a locale is valid.
1749  *
1750  * PARAMS
1751  *  lcid  [I] LCID of the locale to check
1752  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
1753  *
1754  * RETURN
1755  *  TRUE,  if lcid is valid,
1756  *  FALSE, otherwise.
1757  *
1758  * NOTES
1759  *  Wine does not currently make the distinction between supported and installed. All
1760  *  languages supported are installed by default.
1761  */
1762 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
1763 {
1764     /* check if language is registered in the kernel32 resources */
1765     return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1766                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
1767 }
1768
1769
1770 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
1771                                        LPCSTR name, WORD LangID, LONG lParam )
1772 {
1773     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
1774     char buf[20];
1775
1776     sprintf(buf, "%08x", (UINT)LangID);
1777     return lpfnLocaleEnum( buf );
1778 }
1779
1780 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
1781                                        LPCWSTR name, WORD LangID, LONG lParam )
1782 {
1783     static const WCHAR formatW[] = {'%','0','8','x',0};
1784     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
1785     WCHAR buf[20];
1786     sprintfW( buf, formatW, (UINT)LangID );
1787     return lpfnLocaleEnum( buf );
1788 }
1789
1790 /******************************************************************************
1791  *           EnumSystemLocalesA  (KERNEL32.@)
1792  *
1793  * Call a users function for each locale available on the system.
1794  *
1795  * PARAMS
1796  *  lpfnLocaleEnum [I] Callback function to call for each locale
1797  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
1798  *
1799  * RETURNS
1800  *  Success: TRUE.
1801  *  Failure: FALSE. Use GetLastError() to determine the cause.
1802  */
1803 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
1804 {
1805     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1806     EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
1807                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
1808                             (LONG)lpfnLocaleEnum);
1809     return TRUE;
1810 }
1811
1812
1813 /******************************************************************************
1814  *           EnumSystemLocalesW  (KERNEL32.@)
1815  *
1816  * See EnumSystemLocalesA.
1817  */
1818 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
1819 {
1820     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1821     EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
1822                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
1823                             (LONG)lpfnLocaleEnum);
1824     return TRUE;
1825 }
1826
1827
1828 /***********************************************************************
1829  *           VerLanguageNameA  (KERNEL32.@)
1830  *
1831  * Get the name of a language.
1832  *
1833  * PARAMS
1834  *  wLang  [I] LANGID of the language
1835  *  szLang [O] Destination for the language name
1836  *
1837  * RETURNS
1838  *  Success: The size of the language name. If szLang is non-NULL, it is filled
1839  *           with the name.
1840  *  Failure: 0. Use GetLastError() to determine the cause.
1841  *
1842  */
1843 DWORD WINAPI VerLanguageNameA( UINT wLang, LPSTR szLang, UINT nSize )
1844 {
1845     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1846 }
1847
1848
1849 /***********************************************************************
1850  *           VerLanguageNameW  (KERNEL32.@)
1851  *
1852  * See VerLanguageNameA.
1853  */
1854 DWORD WINAPI VerLanguageNameW( UINT wLang, LPWSTR szLang, UINT nSize )
1855 {
1856     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1857 }
1858
1859
1860 /******************************************************************************
1861  *           GetStringTypeW    (KERNEL32.@)
1862  *
1863  * See GetStringTypeA.
1864  */
1865 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1866 {
1867     if (count == -1) count = strlenW(src) + 1;
1868     switch(type)
1869     {
1870     case CT_CTYPE1:
1871         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
1872         break;
1873     case CT_CTYPE2:
1874         while (count--) *chartype++ = get_char_typeW( *src++ ) >> 12;
1875         break;
1876     case CT_CTYPE3:
1877     {
1878         WARN("CT_CTYPE3: semi-stub.\n");
1879         while (count--)
1880         {
1881             int c = *src;
1882             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
1883
1884             type1 = get_char_typeW( *src++ ) & 0xfff;
1885             /* try to construct type3 from type1 */
1886             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
1887             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
1888             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
1889             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
1890             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
1891             if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
1892             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
1893
1894             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
1895             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
1896             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
1897             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
1898             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
1899             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
1900             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
1901             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
1902
1903             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
1904             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
1905             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
1906             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
1907             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
1908             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
1909             *chartype++ = type3;
1910         }
1911         break;
1912     }
1913     default:
1914         SetLastError( ERROR_INVALID_PARAMETER );
1915         return FALSE;
1916     }
1917     return TRUE;
1918 }
1919
1920
1921 /******************************************************************************
1922  *           GetStringTypeExW    (KERNEL32.@)
1923  *
1924  * See GetStringTypeExA.
1925  */
1926 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1927 {
1928     /* locale is ignored for Unicode */
1929     return GetStringTypeW( type, src, count, chartype );
1930 }
1931
1932
1933 /******************************************************************************
1934  *           GetStringTypeA    (KERNEL32.@)
1935  *
1936  * Get characteristics of the characters making up a string.
1937  *
1938  * PARAMS
1939  *  locale   [I] Locale Id for the string
1940  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
1941  *  src      [I] String to analyse
1942  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
1943  *  chartype [O] Destination for the calculated characteristics
1944  *
1945  * RETURNS
1946  *  Success: TRUE. chartype is filled with the requested characteristics of each char
1947  *           in src.
1948  *  Failure: FALSE. Use GetLastError() to determine the cause.
1949  */
1950 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
1951 {
1952     UINT cp;
1953     INT countW;
1954     LPWSTR srcW;
1955     BOOL ret = FALSE;
1956
1957     if(count == -1) count = strlen(src) + 1;
1958
1959     if (!(cp = get_lcid_codepage( locale )))
1960     {
1961         FIXME("For locale %04lx using current ANSI code page\n", locale);
1962         cp = GetACP();
1963     }
1964
1965     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
1966     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
1967     {
1968         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
1969     /*
1970      * NOTE: the target buffer has 1 word for each CHARACTER in the source
1971      * string, with multibyte characters there maybe be more bytes in count
1972      * than character space in the buffer!
1973      */
1974         ret = GetStringTypeW(type, srcW, countW, chartype);
1975         HeapFree(GetProcessHeap(), 0, srcW);
1976     }
1977     return ret;
1978 }
1979
1980 /******************************************************************************
1981  *           GetStringTypeExA    (KERNEL32.@)
1982  *
1983  * Get characteristics of the characters making up a string.
1984  *
1985  * PARAMS
1986  *  locale   [I] Locale Id for the string
1987  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
1988  *  src      [I] String to analyse
1989  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
1990  *  chartype [O] Destination for the calculated characteristics
1991  *
1992  * RETURNS
1993  *  Success: TRUE. chartype is filled with the requested characteristics of each char
1994  *           in src.
1995  *  Failure: FALSE. Use GetLastError() to determine the cause.
1996  */
1997 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
1998 {
1999     return GetStringTypeA(locale, type, src, count, chartype);
2000 }
2001
2002
2003 /*************************************************************************
2004  *           LCMapStringW    (KERNEL32.@)
2005  *
2006  * See LCMapStringA.
2007  */
2008 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2009                         LPWSTR dst, INT dstlen)
2010 {
2011     LPWSTR dst_ptr;
2012
2013     if (!src || !srclen || dstlen < 0)
2014     {
2015         SetLastError(ERROR_INVALID_PARAMETER);
2016         return 0;
2017     }
2018
2019     /* mutually exclusive flags */
2020     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2021         (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2022         (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2023         (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2024     {
2025         SetLastError(ERROR_INVALID_FLAGS);
2026         return 0;
2027     }
2028
2029     if (!dstlen) dst = NULL;
2030
2031     lcid = ConvertDefaultLocale(lcid);
2032
2033     if (flags & LCMAP_SORTKEY)
2034     {
2035         if (src == dst)
2036         {
2037             SetLastError(ERROR_INVALID_FLAGS);
2038             return 0;
2039         }
2040
2041         if (srclen < 0) srclen = strlenW(src);
2042
2043         TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
2044               lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2045
2046         return wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2047     }
2048
2049     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2050     if (flags & SORT_STRINGSORT)
2051     {
2052         SetLastError(ERROR_INVALID_FLAGS);
2053         return 0;
2054     }
2055
2056     if (srclen < 0) srclen = strlenW(src) + 1;
2057
2058     TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
2059           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2060
2061     if (!dst) /* return required string length */
2062     {
2063         INT len;
2064
2065         for (len = 0; srclen; src++, srclen--)
2066         {
2067             WCHAR wch = *src;
2068             /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2069              * and skips white space and punctuation characters for
2070              * NORM_IGNORESYMBOLS.
2071              */
2072             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2073                 continue;
2074             len++;
2075         }
2076         return len;
2077     }
2078
2079     if (flags & LCMAP_UPPERCASE)
2080     {
2081         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2082         {
2083             WCHAR wch = *src;
2084             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2085                 continue;
2086             *dst_ptr++ = toupperW(wch);
2087             dstlen--;
2088         }
2089     }
2090     else if (flags & LCMAP_LOWERCASE)
2091     {
2092         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2093         {
2094             WCHAR wch = *src;
2095             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2096                 continue;
2097             *dst_ptr++ = tolowerW(wch);
2098             dstlen--;
2099         }
2100     }
2101     else
2102     {
2103         if (src == dst)
2104         {
2105             SetLastError(ERROR_INVALID_FLAGS);
2106             return 0;
2107         }
2108         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2109         {
2110             WCHAR wch = *src;
2111             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2112                 continue;
2113             *dst_ptr++ = wch;
2114             dstlen--;
2115         }
2116     }
2117
2118     if (srclen)
2119     {
2120         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2121         return 0;
2122     }
2123
2124     return dst_ptr - dst;
2125 }
2126
2127 /*************************************************************************
2128  *           LCMapStringA    (KERNEL32.@)
2129  *
2130  * Map characters in a locale sensitive string.
2131  *
2132  * PARAMS
2133  *  lcid   [I] LCID for the conversion.
2134  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2135  *  src    [I] String to map
2136  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
2137  *  dst    [O] Destination for mapped string
2138  *  dstlen [I] Length of dst in characters
2139  *
2140  * RETURNS
2141  *  Success: The length of the mapped string in dst, including the NUL terminator.
2142  *  Failure: 0. Use GetLastError() to determine the cause.
2143  */
2144 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2145                         LPSTR dst, INT dstlen)
2146 {
2147     WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2148     LPWSTR srcW, dstW;
2149     INT ret = 0, srclenW, dstlenW;
2150     UINT locale_cp = CP_ACP;
2151
2152     if (!src || !srclen || dstlen < 0)
2153     {
2154         SetLastError(ERROR_INVALID_PARAMETER);
2155         return 0;
2156     }
2157
2158     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2159
2160     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2161     if (srclenW)
2162         srcW = bufW;
2163     else
2164     {
2165         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2166         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2167         if (!srcW)
2168         {
2169             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2170             return 0;
2171         }
2172         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2173     }
2174
2175     if (flags & LCMAP_SORTKEY)
2176     {
2177         if (src == dst)
2178         {
2179             SetLastError(ERROR_INVALID_FLAGS);
2180             goto map_string_exit;
2181         }
2182         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2183         goto map_string_exit;
2184     }
2185
2186     if (flags & SORT_STRINGSORT)
2187     {
2188         SetLastError(ERROR_INVALID_FLAGS);
2189         goto map_string_exit;
2190     }
2191
2192     dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
2193     if (!dstlenW)
2194         goto map_string_exit;
2195
2196     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2197     if (!dstW)
2198     {
2199         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2200         goto map_string_exit;
2201     }
2202
2203     LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
2204     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2205     HeapFree(GetProcessHeap(), 0, dstW);
2206
2207 map_string_exit:
2208     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2209     return ret;
2210 }
2211
2212 /*************************************************************************
2213  *           FoldStringA    (KERNEL32.@)
2214  *
2215  * Map characters in a string.
2216  *
2217  * PARAMS
2218  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2219  *  src     [I] String to map
2220  *  srclen  [I] Length of src, or -1 if src is NUL terminated
2221  *  dst     [O] Destination for mapped string
2222  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
2223  *
2224  * RETURNS
2225  *  Success: The length of the string written to dst, including the terminating NUL. If
2226  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
2227  *           and dst may be NULL.
2228  *  Failure: 0. Use GetLastError() to determine the cause.
2229  */
2230 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2231                        LPSTR dst, INT dstlen)
2232 {
2233     INT ret = 0, srclenW = 0;
2234     WCHAR *srcW = NULL, *dstW = NULL;
2235
2236     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2237     {
2238         SetLastError(ERROR_INVALID_PARAMETER);
2239         return 0;
2240     }
2241
2242     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2243                                   src, srclen, NULL, 0);
2244     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2245
2246     if (!srcW)
2247     {
2248         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2249         goto FoldStringA_exit;
2250     }
2251
2252     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2253                         src, srclen, srcW, srclenW);
2254
2255     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2256
2257     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2258     if (ret && dstlen)
2259     {
2260         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2261
2262         if (!dstW)
2263         {
2264             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2265             goto FoldStringA_exit;
2266         }
2267
2268         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2269         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2270         {
2271             ret = 0;
2272             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2273         }
2274     }
2275
2276     HeapFree(GetProcessHeap(), 0, dstW);
2277
2278 FoldStringA_exit:
2279     HeapFree(GetProcessHeap(), 0, srcW);
2280     return ret;
2281 }
2282
2283 /*************************************************************************
2284  *           FoldStringW    (KERNEL32.@)
2285  *
2286  * See FoldStringA.
2287  */
2288 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2289                        LPWSTR dst, INT dstlen)
2290 {
2291     int ret;
2292
2293     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2294     {
2295     case 0:
2296         if (dwFlags)
2297           break;
2298         /* Fall through for dwFlags == 0 */
2299     case MAP_PRECOMPOSED|MAP_COMPOSITE:
2300     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2301     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2302         SetLastError(ERROR_INVALID_FLAGS);
2303         return 0;
2304     }
2305
2306     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2307     {
2308         SetLastError(ERROR_INVALID_PARAMETER);
2309         return 0;
2310     }
2311
2312     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2313     if (!ret)
2314         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2315     return ret;
2316 }
2317
2318 /******************************************************************************
2319  *           CompareStringW    (KERNEL32.@)
2320  *
2321  * See CompareStringA.
2322  */
2323 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2324                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2325 {
2326     INT ret;
2327
2328     if (!str1 || !str2)
2329     {
2330         SetLastError(ERROR_INVALID_PARAMETER);
2331         return 0;
2332     }
2333
2334     if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2335         SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2336     {
2337         SetLastError(ERROR_INVALID_FLAGS);
2338         return 0;
2339     }
2340
2341     if (style & 0x10000000)
2342         FIXME("Ignoring unknown style 0x10000000\n");
2343
2344     if (len1 < 0) len1 = strlenW(str1);
2345     if (len2 < 0) len2 = strlenW(str2);
2346
2347     ret = wine_compare_string(style, str1, len1, str2, len2);
2348
2349     if (ret) /* need to translate result */
2350         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2351     return CSTR_EQUAL;
2352 }
2353
2354 /******************************************************************************
2355  *           CompareStringA    (KERNEL32.@)
2356  *
2357  * Compare two locale sensitive strings.
2358  *
2359  * PARAMS
2360  *  lcid  [I] LCID for the comparison
2361  *  style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2362  *  str1  [I] First string to compare
2363  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
2364  *  str2  [I] Second string to compare
2365  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
2366  *
2367  * RETURNS
2368  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2369  *           str2 is less than, equal to or greater than str1 respectively.
2370  *  Failure: FALSE. Use GetLastError() to determine the cause.
2371  */
2372 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2373                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2374 {
2375     WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2376     WCHAR *buf2W = buf1W + 130;
2377     LPWSTR str1W, str2W;
2378     INT len1W, len2W, ret;
2379     UINT locale_cp = CP_ACP;
2380
2381     if (!str1 || !str2)
2382     {
2383         SetLastError(ERROR_INVALID_PARAMETER);
2384         return 0;
2385     }
2386     if (len1 < 0) len1 = strlen(str1);
2387     if (len2 < 0) len2 = strlen(str2);
2388
2389     if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2390
2391     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2392     if (len1W)
2393         str1W = buf1W;
2394     else
2395     {
2396         len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2397         str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2398         if (!str1W)
2399         {
2400             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2401             return 0;
2402         }
2403         MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2404     }
2405     len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2406     if (len2W)
2407         str2W = buf2W;
2408     else
2409     {
2410         len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2411         str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2412         if (!str2W)
2413         {
2414             if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2415             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2416             return 0;
2417         }
2418         MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2419     }
2420
2421     ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2422
2423     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2424     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2425     return ret;
2426 }
2427
2428 /*************************************************************************
2429  *           lstrcmp     (KERNEL32.@)
2430  *           lstrcmpA    (KERNEL32.@)
2431  *
2432  * Compare two strings using the current thread locale.
2433  *
2434  * PARAMS
2435  *  str1  [I] First string to compare
2436  *  str2  [I] Second string to compare
2437  *
2438  * RETURNS
2439  *  Success: A number less than, equal to or greater than 0 depending on whether
2440  *           str2 is less than, equal to or greater than str1 respectively.
2441  *  Failure: FALSE. Use GetLastError() to determine the cause.
2442  */
2443 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2444 {
2445     int ret;
2446     
2447     if ((str1 == NULL) && (str2 == NULL)) return 0;
2448     if (str1 == NULL) return -1;
2449     if (str2 == NULL) return 1;
2450
2451     ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2452     if (ret) ret -= 2;
2453     
2454     return ret;
2455 }
2456
2457 /*************************************************************************
2458  *           lstrcmpi     (KERNEL32.@)
2459  *           lstrcmpiA    (KERNEL32.@)
2460  *
2461  * Compare two strings using the current thread locale, ignoring case.
2462  *
2463  * PARAMS
2464  *  str1  [I] First string to compare
2465  *  str2  [I] Second string to compare
2466  *
2467  * RETURNS
2468  *  Success: A number less than, equal to or greater than 0 depending on whether
2469  *           str2 is less than, equal to or greater than str1 respectively.
2470  *  Failure: FALSE. Use GetLastError() to determine the cause.
2471  */
2472 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
2473 {
2474     int ret;
2475     
2476     if ((str1 == NULL) && (str2 == NULL)) return 0;
2477     if (str1 == NULL) return -1;
2478     if (str2 == NULL) return 1;
2479
2480     ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2481     if (ret) ret -= 2;
2482     
2483     return ret;
2484 }
2485
2486 /*************************************************************************
2487  *           lstrcmpW    (KERNEL32.@)
2488  *
2489  * See lstrcmpA.
2490  */
2491 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
2492 {
2493     int ret;
2494
2495     if ((str1 == NULL) && (str2 == NULL)) return 0;
2496     if (str1 == NULL) return -1;
2497     if (str2 == NULL) return 1;
2498
2499     ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
2500     if (ret) ret -= 2;
2501     
2502     return ret;
2503 }
2504
2505 /*************************************************************************
2506  *           lstrcmpiW    (KERNEL32.@)
2507  *
2508  * See lstrcmpiA.
2509  */
2510 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
2511 {
2512     int ret;
2513     
2514     if ((str1 == NULL) && (str2 == NULL)) return 0;
2515     if (str1 == NULL) return -1;
2516     if (str2 == NULL) return 1;
2517
2518     ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2519     if (ret) ret -= 2;
2520     
2521     return ret;
2522 }
2523
2524 /******************************************************************************
2525  *              LOCALE_Init
2526  */
2527 void LOCALE_Init(void)
2528 {
2529     extern void __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
2530                                        const union cptable *unix_cp );
2531
2532     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp = ~0U;
2533     LCID lcid;
2534
2535     lcid = get_env_lcid( NULL, NULL );
2536     NtSetDefaultLocale( TRUE, lcid );
2537
2538     lcid = get_env_lcid( NULL, "LC_MESSAGES" );
2539     NtSetDefaultUILanguage( LANGIDFROMLCID(lcid) );
2540
2541     lcid = get_env_lcid( &unix_cp, "LC_CTYPE" );
2542     NtSetDefaultLocale( FALSE, lcid );
2543
2544     ansi_cp = get_lcid_codepage(lcid);
2545     GetLocaleInfoW( lcid, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
2546                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
2547     GetLocaleInfoW( lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
2548                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
2549     if (unix_cp == ~0U)
2550         GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
2551                     (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
2552
2553     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
2554         ansi_cptable = wine_cp_get_table( 1252 );
2555     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
2556         oem_cptable  = wine_cp_get_table( 437 );
2557     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
2558         mac_cptable  = wine_cp_get_table( 10000 );
2559     if (unix_cp != CP_UTF8)
2560     {
2561         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
2562             unix_cptable  = wine_cp_get_table( 28591 );
2563     }
2564
2565     __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
2566
2567     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
2568            ansi_cptable->info.codepage, oem_cptable->info.codepage,
2569            mac_cptable->info.codepage, unix_cp );
2570 }
2571
2572 static HKEY NLS_RegOpenKey(HKEY hRootKey, LPCWSTR szKeyName)
2573 {
2574     UNICODE_STRING keyName;
2575     OBJECT_ATTRIBUTES attr;
2576     HKEY hkey;
2577
2578     RtlInitUnicodeString( &keyName, szKeyName );
2579     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
2580
2581     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS)
2582         hkey = 0;
2583
2584     return hkey;
2585 }
2586
2587 static HKEY NLS_RegOpenSubKey(HKEY hRootKey, LPCWSTR szKeyName)
2588 {
2589     HKEY hKey = NLS_RegOpenKey(hRootKey, szKeyName);
2590
2591     if (hRootKey)
2592         NtClose( hRootKey );
2593
2594     return hKey;
2595 }
2596
2597 static BOOL NLS_RegEnumSubKey(HKEY hKey, UINT ulIndex, LPWSTR szKeyName,
2598                               ULONG keyNameSize)
2599 {
2600     BYTE buffer[80];
2601     KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
2602     DWORD dwLen;
2603
2604     if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
2605                         sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
2606         info->NameLength > keyNameSize)
2607     {
2608         return FALSE;
2609     }
2610
2611     TRACE("info->Name %s info->NameLength %ld\n", debugstr_w(info->Name), info->NameLength);
2612
2613     memcpy( szKeyName, info->Name, info->NameLength);
2614     szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
2615
2616     TRACE("returning %s\n", debugstr_w(szKeyName));
2617     return TRUE;
2618 }
2619
2620 static BOOL NLS_RegEnumValue(HKEY hKey, UINT ulIndex,
2621                              LPWSTR szValueName, ULONG valueNameSize,
2622                              LPWSTR szValueData, ULONG valueDataSize)
2623 {
2624     BYTE buffer[80];
2625     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
2626     DWORD dwLen;
2627
2628     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
2629         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
2630         info->NameLength > valueNameSize ||
2631         info->DataLength > valueDataSize)
2632     {
2633         return FALSE;
2634     }
2635
2636     TRACE("info->Name %s info->DataLength %ld\n", debugstr_w(info->Name), info->DataLength);
2637
2638     memcpy( szValueName, info->Name, info->NameLength);
2639     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
2640     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
2641     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
2642
2643     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
2644     return TRUE;
2645 }
2646
2647 static BOOL NLS_RegGetDword(HKEY hKey, LPCWSTR szValueName, DWORD *lpVal)
2648 {
2649     BYTE buffer[128];
2650     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
2651     DWORD dwSize = sizeof(buffer);
2652     UNICODE_STRING valueName;
2653
2654     RtlInitUnicodeString( &valueName, szValueName );
2655
2656     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
2657     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
2658                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
2659         info->DataLength == sizeof(DWORD))
2660     {
2661         memcpy(lpVal, info->Data, sizeof(DWORD));
2662         return TRUE;
2663     }
2664
2665     return FALSE;
2666 }
2667
2668 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
2669 {
2670     LANGID  langId;
2671     LPCWSTR szResourceName = (LPCWSTR)(((lgrpid + 0x2000) >> 4) + 1);
2672     HRSRC   hResource;
2673     BOOL    bRet = FALSE;
2674
2675     /* FIXME: Is it correct to use the system default langid? */
2676     langId = GetSystemDefaultLangID();
2677
2678     if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
2679         langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
2680
2681     hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
2682
2683     if (hResource)
2684     {
2685         HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
2686
2687         if (hResDir)
2688         {
2689             ULONG   iResourceIndex = lgrpid & 0xf;
2690             LPCWSTR lpResEntry = LockResource( hResDir );
2691             ULONG   i;
2692
2693             for (i = 0; i < iResourceIndex; i++)
2694                 lpResEntry += *lpResEntry + 1;
2695
2696             if (*lpResEntry < nameSize)
2697             {
2698                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
2699                 szName[*lpResEntry] = '\0';
2700                 bRet = TRUE;
2701             }
2702
2703         }
2704         FreeResource( hResource );
2705     }
2706     return bRet;
2707 }
2708
2709 /* Registry keys for NLS related information */
2710 static const WCHAR szLangGroupsKeyName[] = {
2711     'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s','\0'
2712 };
2713
2714 static const WCHAR szCountryListName[] = {
2715     'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
2716     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
2717     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2718     'T','e','l','e','p','h','o','n','y','\\',
2719     'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
2720 };
2721
2722
2723 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
2724 typedef struct
2725 {
2726   LANGUAGEGROUP_ENUMPROCA procA;
2727   LANGUAGEGROUP_ENUMPROCW procW;
2728   DWORD    dwFlags;
2729   LONG_PTR lParam;
2730 } ENUMLANGUAGEGROUP_CALLBACKS;
2731
2732 /* Internal implementation of EnumSystemLanguageGroupsA/W */
2733 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
2734 {
2735     WCHAR szNumber[10], szValue[4];
2736     HKEY hKey;
2737     BOOL bContinue = TRUE;
2738     ULONG ulIndex = 0;
2739
2740     if (!lpProcs)
2741     {
2742         SetLastError(ERROR_INVALID_PARAMETER);
2743         return FALSE;
2744     }
2745
2746     switch (lpProcs->dwFlags)
2747     {
2748     case 0:
2749         /* Default to LGRPID_INSTALLED */
2750         lpProcs->dwFlags = LGRPID_INSTALLED;
2751         /* Fall through... */
2752     case LGRPID_INSTALLED:
2753     case LGRPID_SUPPORTED:
2754         break;
2755     default:
2756         SetLastError(ERROR_INVALID_FLAGS);
2757         return FALSE;
2758     }
2759
2760     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2761
2762     if (!hKey)
2763         FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
2764
2765     while (bContinue)
2766     {
2767         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2768                               szValue, sizeof(szValue) ))
2769         {
2770             BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
2771             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
2772
2773             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
2774                    bInstalled ? "" : "not ");
2775
2776             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
2777             {
2778                 WCHAR szGrpName[48];
2779
2780                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
2781                     szGrpName[0] = '\0';
2782
2783                 if (lpProcs->procW)
2784                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
2785                                                 lpProcs->lParam );
2786                 else
2787                 {
2788                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
2789                     char szGrpNameA[48];
2790
2791                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
2792                      *        or whether the language names are ever localised. Assume CP_ACP.
2793                      */
2794
2795                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
2796                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
2797
2798                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
2799                                                 lpProcs->lParam );
2800                 }
2801             }
2802
2803             ulIndex++;
2804         }
2805         else
2806             bContinue = FALSE;
2807
2808         if (!bContinue)
2809             break;
2810     }
2811
2812     if (hKey)
2813         NtClose( hKey );
2814
2815     return TRUE;
2816 }
2817
2818 /******************************************************************************
2819  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
2820  *
2821  * Call a users function for each language group available on the system.
2822  *
2823  * PARAMS
2824  *  pLangGrpEnumProc [I] Callback function to call for each language group
2825  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
2826  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
2827  *
2828  * RETURNS
2829  *  Success: TRUE.
2830  *  Failure: FALSE. Use GetLastError() to determine the cause.
2831  */
2832 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
2833                                       DWORD dwFlags, LONG_PTR lParam)
2834 {
2835     ENUMLANGUAGEGROUP_CALLBACKS procs;
2836
2837     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2838
2839     procs.procA = pLangGrpEnumProc;
2840     procs.procW = NULL;
2841     procs.dwFlags = dwFlags;
2842     procs.lParam = lParam;
2843
2844     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2845 }
2846
2847 /******************************************************************************
2848  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
2849  *
2850  * See EnumSystemLanguageGroupsA.
2851  */
2852 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
2853                                       DWORD dwFlags, LONG_PTR lParam)
2854 {
2855     ENUMLANGUAGEGROUP_CALLBACKS procs;
2856
2857     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2858
2859     procs.procA = NULL;
2860     procs.procW = pLangGrpEnumProc;
2861     procs.dwFlags = dwFlags;
2862     procs.lParam = lParam;
2863
2864     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2865 }
2866
2867 /******************************************************************************
2868  *           IsValidLanguageGroup    (KERNEL32.@)
2869  *
2870  * Determine if a language group is supported and/or installed.
2871  *
2872  * PARAMS
2873  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
2874  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
2875  *
2876  * RETURNS
2877  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
2878  *  FALSE otherwise.
2879  */
2880 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
2881 {
2882     static const WCHAR szFormat[] = { '%','x','\0' };
2883     WCHAR szValueName[16], szValue[2];
2884     BOOL bSupported = FALSE, bInstalled = FALSE;
2885     HKEY hKey;
2886
2887
2888     switch (dwFlags)
2889     {
2890     case LGRPID_INSTALLED:
2891     case LGRPID_SUPPORTED:
2892
2893         hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2894
2895         sprintfW( szValueName, szFormat, lgrpid );
2896
2897         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)&szValue ))
2898         {
2899             bSupported = TRUE;
2900
2901             if (szValue[0] == '1')
2902                 bInstalled = TRUE;
2903         }
2904
2905         if (hKey)
2906             NtClose( hKey );
2907
2908         break;
2909     }
2910
2911     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
2912         (dwFlags == LGRPID_INSTALLED && bInstalled))
2913         return TRUE;
2914
2915     return FALSE;
2916 }
2917
2918 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
2919 typedef struct
2920 {
2921   LANGGROUPLOCALE_ENUMPROCA procA;
2922   LANGGROUPLOCALE_ENUMPROCW procW;
2923   DWORD    dwFlags;
2924   LGRPID   lgrpid;
2925   LONG_PTR lParam;
2926 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
2927
2928 /* Internal implementation of EnumLanguageGrouplocalesA/W */
2929 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
2930 {
2931     static const WCHAR szLocaleKeyName[] = {
2932       'L','o','c','a','l','e','\0'
2933     };
2934     static const WCHAR szAlternateSortsKeyName[] = {
2935       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
2936     };
2937     WCHAR szNumber[10], szValue[4];
2938     HKEY hKey;
2939     BOOL bContinue = TRUE, bAlternate = FALSE;
2940     LGRPID lgrpid;
2941     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
2942
2943     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
2944     {
2945         SetLastError(ERROR_INVALID_PARAMETER);
2946         return FALSE;
2947     }
2948
2949     if (lpProcs->dwFlags)
2950     {
2951         SetLastError(ERROR_INVALID_FLAGS);
2952         return FALSE;
2953     }
2954
2955     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLocaleKeyName );
2956
2957     if (!hKey)
2958         WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
2959
2960     while (bContinue)
2961     {
2962         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2963                               szValue, sizeof(szValue) ))
2964         {
2965             lgrpid = strtoulW( szValue, NULL, 16 );
2966
2967             TRACE("lcid %s, grpid %ld (%smatched)\n", debugstr_w(szNumber),
2968                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
2969
2970             if (lgrpid == lpProcs->lgrpid)
2971             {
2972                 LCID lcid;
2973
2974                 lcid = strtoulW( szNumber, NULL, 16 );
2975
2976                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
2977                  * '00000437          ;Georgian'
2978                  * At present we only pass the LCID string.
2979                  */
2980
2981                 if (lpProcs->procW)
2982                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
2983                 else
2984                 {
2985                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
2986
2987                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
2988
2989                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
2990                 }
2991             }
2992
2993             ulIndex++;
2994         }
2995         else
2996         {
2997             /* Finished enumerating this key */
2998             if (!bAlternate)
2999             {
3000                 /* Enumerate alternate sorts also */
3001                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3002                 bAlternate = TRUE;
3003                 ulIndex = 0;
3004             }
3005             else
3006                 bContinue = FALSE; /* Finished both keys */
3007         }
3008
3009         if (!bContinue)
3010             break;
3011     }
3012
3013     if (hKey)
3014         NtClose( hKey );
3015
3016     return TRUE;
3017 }
3018
3019 /******************************************************************************
3020  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
3021  *
3022  * Call a users function for every locale in a language group available on the system.
3023  *
3024  * PARAMS
3025  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
3026  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
3027  *  dwFlags            [I] Reserved, set to 0
3028  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
3029  *
3030  * RETURNS
3031  *  Success: TRUE.
3032  *  Failure: FALSE. Use GetLastError() to determine the cause.
3033  */
3034 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3035                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3036 {
3037     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3038
3039     TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3040
3041     callbacks.procA   = pLangGrpLcEnumProc;
3042     callbacks.procW   = NULL;
3043     callbacks.dwFlags = dwFlags;
3044     callbacks.lgrpid  = lgrpid;
3045     callbacks.lParam  = lParam;
3046
3047     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3048 }
3049
3050 /******************************************************************************
3051  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
3052  *
3053  * See EnumLanguageGroupLocalesA.
3054  */
3055 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW 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   = NULL;
3063     callbacks.procW   = pLangGrpLcEnumProc;
3064     callbacks.dwFlags = dwFlags;
3065     callbacks.lgrpid  = lgrpid;
3066     callbacks.lParam  = lParam;
3067
3068     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3069 }
3070
3071 /******************************************************************************
3072  *           EnumSystemGeoID    (KERNEL32.@)
3073  *
3074  * Call a users function for every location available on the system.
3075  *
3076  * PARAMS
3077  *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3078  *  reserved     [I] Reserved, set to 0
3079  *  pGeoEnumProc [I] Callback function to call for each location
3080  *
3081  * RETURNS
3082  *  Success: TRUE.
3083  *  Failure: FALSE. Use GetLastError() to determine the cause.
3084  */
3085 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3086 {
3087     static const WCHAR szCountryCodeValueName[] = {
3088       'C','o','u','n','t','r','y','C','o','d','e','\0'
3089     };
3090     WCHAR szNumber[10];
3091     HKEY hKey;
3092     ULONG ulIndex = 0;
3093
3094     TRACE("(0x%08lX,0x%08lX,%p)\n", geoclass, reserved, pGeoEnumProc);
3095
3096     if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3097     {
3098         SetLastError(ERROR_INVALID_PARAMETER);
3099         return FALSE;
3100     }
3101
3102     hKey = NLS_RegOpenKey( 0, szCountryListName );
3103
3104     while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3105     {
3106         BOOL bContinue = TRUE;
3107         DWORD dwGeoId;
3108         HKEY hSubKey = NLS_RegOpenKey( hKey, szNumber );
3109
3110         if (hSubKey)
3111         {
3112             if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3113             {
3114                 TRACE("Got geoid %ld\n", dwGeoId);
3115
3116                 if (!pGeoEnumProc( dwGeoId ))
3117                     bContinue = FALSE;
3118             }
3119
3120             NtClose( hSubKey );
3121         }
3122
3123         if (!bContinue)
3124             break;
3125
3126         ulIndex++;
3127     }
3128
3129     if (hKey)
3130         NtClose( hKey );
3131
3132     return TRUE;
3133 }
3134
3135 /******************************************************************************
3136  *           InvalidateNLSCache           (KERNEL32.@)
3137  *
3138  * Invalidate the cache of NLS values.
3139  *
3140  * PARAMS
3141  *  None.
3142  *
3143  * RETURNS
3144  *  Success: TRUE.
3145  *  Failure: FALSE.
3146  */
3147 BOOL WINAPI InvalidateNLSCache(void)
3148 {
3149   FIXME("() stub\n");
3150   return FALSE;
3151 }
3152
3153 /******************************************************************************
3154  *           GetUserGeoID (KERNEL32.@)
3155  */
3156 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3157 {
3158     FIXME("%ld\n",GeoClass);
3159     return GEOID_NOT_AVAILABLE;
3160 }
3161
3162 /******************************************************************************
3163  *           SetUserGeoID (KERNEL32.@)
3164  */
3165 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3166 {
3167     FIXME("%ld\n",GeoID);
3168     return FALSE;
3169 }
3170
3171 /******************************************************************************
3172  *           EnumUILanguagesA (KERNEL32.@)
3173  */
3174 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3175 {
3176     static char value[] = "0409";
3177
3178     if(!pUILangEnumProc) {
3179         SetLastError(ERROR_INVALID_PARAMETER);
3180         return FALSE;
3181     }
3182     if(dwFlags) {
3183         SetLastError(ERROR_INVALID_FLAGS);
3184         return FALSE;
3185     }
3186
3187     FIXME("%p, %lx, %lx calling pUILangEnumProc with %s\n",
3188           pUILangEnumProc, dwFlags, lParam, debugstr_a(value));
3189
3190     pUILangEnumProc( value, lParam );
3191     return(TRUE);
3192 }
3193
3194 /******************************************************************************
3195  *           EnumUILanguagesW (KERNEL32.@)
3196  */
3197 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3198 {
3199     static WCHAR value[] = {'0','4','0','9',0};
3200
3201     if(!pUILangEnumProc) {
3202         SetLastError(ERROR_INVALID_PARAMETER);
3203         return FALSE;
3204     }
3205     if(dwFlags) {
3206         SetLastError(ERROR_INVALID_FLAGS);
3207         return FALSE;
3208     }
3209
3210     FIXME("%p, %lx, %lx calling pUILangEnumProc with %s\n",
3211           pUILangEnumProc, dwFlags, lParam, debugstr_w(value));
3212
3213     pUILangEnumProc( value, lParam );
3214     return(TRUE);
3215 }