gdi32: Add a helper function to initialize Bresenham parameters for line drawing.
[wine/multimedia.git] / dlls / kernel32 / locale.c
blob90603f2ff1778785f97021db4f942df0f2c1c22f
1 /*
2 * Locale support
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
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.
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.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFBundle.h>
37 # include <CoreFoundation/CFLocale.h>
38 # include <CoreFoundation/CFString.h>
39 #endif
41 #include "ntstatus.h"
42 #define WIN32_NO_STATUS
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winuser.h" /* for RT_STRINGW */
46 #include "winternl.h"
47 #include "wine/unicode.h"
48 #include "winnls.h"
49 #include "winerror.h"
50 #include "winver.h"
51 #include "kernel_private.h"
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(nls);
56 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
57 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
59 /* current code pages */
60 static const union cptable *ansi_cptable;
61 static const union cptable *oem_cptable;
62 static const union cptable *mac_cptable;
63 static const union cptable *unix_cptable; /* NULL if UTF8 */
65 static const WCHAR szLocaleKeyName[] = {
66 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
67 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
68 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
71 static const WCHAR szLangGroupsKeyName[] = {
72 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
73 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
74 'C','o','n','t','r','o','l','\\','N','l','s','\\',
75 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
78 /* Charset to codepage map, sorted by name. */
79 static const struct charset_entry
81 const char *charset_name;
82 UINT codepage;
83 } charset_names[] =
85 { "BIG5", 950 },
86 { "CP1250", 1250 },
87 { "CP1251", 1251 },
88 { "CP1252", 1252 },
89 { "CP1253", 1253 },
90 { "CP1254", 1254 },
91 { "CP1255", 1255 },
92 { "CP1256", 1256 },
93 { "CP1257", 1257 },
94 { "CP1258", 1258 },
95 { "CP932", 932 },
96 { "CP936", 936 },
97 { "CP949", 949 },
98 { "CP950", 950 },
99 { "EUCJP", 20932 },
100 { "GB2312", 936 },
101 { "IBM037", 37 },
102 { "IBM1026", 1026 },
103 { "IBM424", 424 },
104 { "IBM437", 437 },
105 { "IBM500", 500 },
106 { "IBM850", 850 },
107 { "IBM852", 852 },
108 { "IBM855", 855 },
109 { "IBM857", 857 },
110 { "IBM860", 860 },
111 { "IBM861", 861 },
112 { "IBM862", 862 },
113 { "IBM863", 863 },
114 { "IBM864", 864 },
115 { "IBM865", 865 },
116 { "IBM866", 866 },
117 { "IBM869", 869 },
118 { "IBM874", 874 },
119 { "IBM875", 875 },
120 { "ISO88591", 28591 },
121 { "ISO885910", 28600 },
122 { "ISO885913", 28603 },
123 { "ISO885914", 28604 },
124 { "ISO885915", 28605 },
125 { "ISO885916", 28606 },
126 { "ISO88592", 28592 },
127 { "ISO88593", 28593 },
128 { "ISO88594", 28594 },
129 { "ISO88595", 28595 },
130 { "ISO88596", 28596 },
131 { "ISO88597", 28597 },
132 { "ISO88598", 28598 },
133 { "ISO88599", 28599 },
134 { "KOI8R", 20866 },
135 { "KOI8U", 21866 },
136 { "UTF8", CP_UTF8 }
140 struct locale_name
142 WCHAR win_name[128]; /* Windows name ("en-US") */
143 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
144 WCHAR *country; /* country ("US") */
145 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
146 WCHAR *script; /* script ("Latn") for Windows format only */
147 WCHAR *modifier; /* modifier or sort order */
148 LCID lcid; /* corresponding LCID */
149 int matches; /* number of elements matching LCID (0..4) */
150 UINT codepage; /* codepage corresponding to charset */
153 /* locale ids corresponding to the various Unix locale parameters */
154 static LCID lcid_LC_COLLATE;
155 static LCID lcid_LC_CTYPE;
156 static LCID lcid_LC_MESSAGES;
157 static LCID lcid_LC_MONETARY;
158 static LCID lcid_LC_NUMERIC;
159 static LCID lcid_LC_TIME;
160 static LCID lcid_LC_PAPER;
161 static LCID lcid_LC_MEASUREMENT;
162 static LCID lcid_LC_TELEPHONE;
164 /* Copy Ascii string to Unicode without using codepages */
165 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
167 while (n > 1 && *src)
169 *dst++ = (unsigned char)*src++;
170 n--;
172 if (n) *dst = 0;
175 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
177 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
180 /***********************************************************************
181 * get_lcid_codepage
183 * Retrieve the ANSI codepage for a given locale.
185 static inline UINT get_lcid_codepage( LCID lcid )
187 UINT ret;
188 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
189 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
190 return ret;
194 /***********************************************************************
195 * get_codepage_table
197 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
199 static const union cptable *get_codepage_table( unsigned int codepage )
201 const union cptable *ret = NULL;
203 assert( ansi_cptable ); /* init must have been done already */
205 switch(codepage)
207 case CP_ACP:
208 return ansi_cptable;
209 case CP_OEMCP:
210 return oem_cptable;
211 case CP_MACCP:
212 return mac_cptable;
213 case CP_UTF7:
214 case CP_UTF8:
215 break;
216 case CP_THREAD_ACP:
217 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
218 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
219 /* fall through */
220 default:
221 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
222 if (codepage == oem_cptable->info.codepage) return oem_cptable;
223 if (codepage == mac_cptable->info.codepage) return mac_cptable;
224 ret = wine_cp_get_table( codepage );
225 break;
227 return ret;
231 /***********************************************************************
232 * charset_cmp (internal)
234 static int charset_cmp( const void *name, const void *entry )
236 const struct charset_entry *charset = entry;
237 return strcasecmp( name, charset->charset_name );
240 /***********************************************************************
241 * find_charset
243 static UINT find_charset( const WCHAR *name )
245 const struct charset_entry *entry;
246 char charset_name[16];
247 size_t i, j;
249 /* remove punctuation characters from charset name */
250 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
251 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
252 charset_name[j] = 0;
254 entry = bsearch( charset_name, charset_names,
255 sizeof(charset_names)/sizeof(charset_names[0]),
256 sizeof(charset_names[0]), charset_cmp );
257 if (entry) return entry->codepage;
258 return 0;
262 /***********************************************************************
263 * find_locale_id_callback
265 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
266 LPCWSTR name, WORD LangID, LPARAM lParam )
268 struct locale_name *data = (struct locale_name *)lParam;
269 WCHAR buffer[128];
270 int matches = 0;
271 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
273 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
275 /* first check exact name */
276 if (data->win_name[0] &&
277 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
278 buffer, sizeof(buffer)/sizeof(WCHAR) ))
280 if (!strcmpW( data->win_name, buffer ))
282 matches = 4; /* everything matches */
283 goto done;
287 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
288 buffer, sizeof(buffer)/sizeof(WCHAR) ))
289 return TRUE;
290 if (strcmpW( buffer, data->lang )) return TRUE;
291 matches++; /* language name matched */
293 if (data->country)
295 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
296 buffer, sizeof(buffer)/sizeof(WCHAR) ))
298 if (strcmpW( buffer, data->country )) goto done;
299 matches++; /* country name matched */
302 else /* match default language */
304 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
307 if (data->codepage)
309 UINT unix_cp;
310 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
311 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
313 if (unix_cp == data->codepage) matches++;
317 /* FIXME: check sort order */
319 done:
320 if (matches > data->matches)
322 data->lcid = lcid;
323 data->matches = matches;
325 return (data->matches < 4); /* no need to continue for perfect match */
329 /***********************************************************************
330 * parse_locale_name
332 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
333 * Unix format is: lang[_country][.charset][@modifier]
334 * Windows format is: lang[-script][-country][_modifier]
336 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
338 static const WCHAR sepW[] = {'-','_','.','@',0};
339 static const WCHAR winsepW[] = {'-','_',0};
340 static const WCHAR posixW[] = {'P','O','S','I','X',0};
341 static const WCHAR cW[] = {'C',0};
342 static const WCHAR latinW[] = {'l','a','t','i','n',0};
343 static const WCHAR latnW[] = {'-','L','a','t','n',0};
344 WCHAR *p;
346 TRACE("%s\n", debugstr_w(str));
348 name->country = name->charset = name->script = name->modifier = NULL;
349 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
350 name->matches = 0;
351 name->codepage = 0;
352 name->win_name[0] = 0;
353 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
355 if (!(p = strpbrkW( name->lang, sepW )))
357 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
359 name->matches = 4; /* perfect match for default English lcid */
360 return;
362 strcpyW( name->win_name, name->lang );
364 else if (*p == '-') /* Windows format */
366 strcpyW( name->win_name, name->lang );
367 *p++ = 0;
368 name->country = p;
369 if (!(p = strpbrkW( p, winsepW ))) goto done;
370 if (*p == '-')
372 *p++ = 0;
373 name->script = name->country;
374 name->country = p;
375 if (!(p = strpbrkW( p, winsepW ))) goto done;
377 *p++ = 0;
378 name->modifier = p;
380 else /* Unix format */
382 if (*p == '_')
384 *p++ = 0;
385 name->country = p;
386 p = strpbrkW( p, sepW + 2 );
388 if (p && *p == '.')
390 *p++ = 0;
391 name->charset = p;
392 p = strchrW( p, '@' );
394 if (p)
396 *p++ = 0;
397 name->modifier = p;
400 if (name->charset)
401 name->codepage = find_charset( name->charset );
403 /* rebuild a Windows name if possible */
405 if (name->charset) goto done; /* can't specify charset in Windows format */
406 if (name->modifier && strcmpW( name->modifier, latinW ))
407 goto done; /* only Latn script supported for now */
408 strcpyW( name->win_name, name->lang );
409 if (name->modifier) strcatW( name->win_name, latnW );
410 if (name->country)
412 p = name->win_name + strlenW(name->win_name);
413 *p++ = '-';
414 strcpyW( p, name->country );
417 done:
418 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
419 find_locale_id_callback, (LPARAM)name );
423 /***********************************************************************
424 * convert_default_lcid
426 * Get the default LCID to use for a given lctype in GetLocaleInfo.
428 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
430 if (lcid == LOCALE_SYSTEM_DEFAULT ||
431 lcid == LOCALE_USER_DEFAULT ||
432 lcid == LOCALE_NEUTRAL)
434 LCID default_id = 0;
436 switch(lctype & 0xffff)
438 case LOCALE_SSORTNAME:
439 default_id = lcid_LC_COLLATE;
440 break;
442 case LOCALE_FONTSIGNATURE:
443 case LOCALE_IDEFAULTANSICODEPAGE:
444 case LOCALE_IDEFAULTCODEPAGE:
445 case LOCALE_IDEFAULTEBCDICCODEPAGE:
446 case LOCALE_IDEFAULTMACCODEPAGE:
447 case LOCALE_IDEFAULTUNIXCODEPAGE:
448 default_id = lcid_LC_CTYPE;
449 break;
451 case LOCALE_ICURRDIGITS:
452 case LOCALE_ICURRENCY:
453 case LOCALE_IINTLCURRDIGITS:
454 case LOCALE_INEGCURR:
455 case LOCALE_INEGSEPBYSPACE:
456 case LOCALE_INEGSIGNPOSN:
457 case LOCALE_INEGSYMPRECEDES:
458 case LOCALE_IPOSSEPBYSPACE:
459 case LOCALE_IPOSSIGNPOSN:
460 case LOCALE_IPOSSYMPRECEDES:
461 case LOCALE_SCURRENCY:
462 case LOCALE_SINTLSYMBOL:
463 case LOCALE_SMONDECIMALSEP:
464 case LOCALE_SMONGROUPING:
465 case LOCALE_SMONTHOUSANDSEP:
466 case LOCALE_SNATIVECURRNAME:
467 default_id = lcid_LC_MONETARY;
468 break;
470 case LOCALE_IDIGITS:
471 case LOCALE_IDIGITSUBSTITUTION:
472 case LOCALE_ILZERO:
473 case LOCALE_INEGNUMBER:
474 case LOCALE_SDECIMAL:
475 case LOCALE_SGROUPING:
476 case LOCALE_SNAN:
477 case LOCALE_SNATIVEDIGITS:
478 case LOCALE_SNEGATIVESIGN:
479 case LOCALE_SNEGINFINITY:
480 case LOCALE_SPOSINFINITY:
481 case LOCALE_SPOSITIVESIGN:
482 case LOCALE_STHOUSAND:
483 default_id = lcid_LC_NUMERIC;
484 break;
486 case LOCALE_ICALENDARTYPE:
487 case LOCALE_ICENTURY:
488 case LOCALE_IDATE:
489 case LOCALE_IDAYLZERO:
490 case LOCALE_IFIRSTDAYOFWEEK:
491 case LOCALE_IFIRSTWEEKOFYEAR:
492 case LOCALE_ILDATE:
493 case LOCALE_IMONLZERO:
494 case LOCALE_IOPTIONALCALENDAR:
495 case LOCALE_ITIME:
496 case LOCALE_ITIMEMARKPOSN:
497 case LOCALE_ITLZERO:
498 case LOCALE_S1159:
499 case LOCALE_S2359:
500 case LOCALE_SABBREVDAYNAME1:
501 case LOCALE_SABBREVDAYNAME2:
502 case LOCALE_SABBREVDAYNAME3:
503 case LOCALE_SABBREVDAYNAME4:
504 case LOCALE_SABBREVDAYNAME5:
505 case LOCALE_SABBREVDAYNAME6:
506 case LOCALE_SABBREVDAYNAME7:
507 case LOCALE_SABBREVMONTHNAME1:
508 case LOCALE_SABBREVMONTHNAME2:
509 case LOCALE_SABBREVMONTHNAME3:
510 case LOCALE_SABBREVMONTHNAME4:
511 case LOCALE_SABBREVMONTHNAME5:
512 case LOCALE_SABBREVMONTHNAME6:
513 case LOCALE_SABBREVMONTHNAME7:
514 case LOCALE_SABBREVMONTHNAME8:
515 case LOCALE_SABBREVMONTHNAME9:
516 case LOCALE_SABBREVMONTHNAME10:
517 case LOCALE_SABBREVMONTHNAME11:
518 case LOCALE_SABBREVMONTHNAME12:
519 case LOCALE_SABBREVMONTHNAME13:
520 case LOCALE_SDATE:
521 case LOCALE_SDAYNAME1:
522 case LOCALE_SDAYNAME2:
523 case LOCALE_SDAYNAME3:
524 case LOCALE_SDAYNAME4:
525 case LOCALE_SDAYNAME5:
526 case LOCALE_SDAYNAME6:
527 case LOCALE_SDAYNAME7:
528 case LOCALE_SDURATION:
529 case LOCALE_SLONGDATE:
530 case LOCALE_SMONTHNAME1:
531 case LOCALE_SMONTHNAME2:
532 case LOCALE_SMONTHNAME3:
533 case LOCALE_SMONTHNAME4:
534 case LOCALE_SMONTHNAME5:
535 case LOCALE_SMONTHNAME6:
536 case LOCALE_SMONTHNAME7:
537 case LOCALE_SMONTHNAME8:
538 case LOCALE_SMONTHNAME9:
539 case LOCALE_SMONTHNAME10:
540 case LOCALE_SMONTHNAME11:
541 case LOCALE_SMONTHNAME12:
542 case LOCALE_SMONTHNAME13:
543 case LOCALE_SSHORTDATE:
544 case LOCALE_SSHORTESTDAYNAME1:
545 case LOCALE_SSHORTESTDAYNAME2:
546 case LOCALE_SSHORTESTDAYNAME3:
547 case LOCALE_SSHORTESTDAYNAME4:
548 case LOCALE_SSHORTESTDAYNAME5:
549 case LOCALE_SSHORTESTDAYNAME6:
550 case LOCALE_SSHORTESTDAYNAME7:
551 case LOCALE_STIME:
552 case LOCALE_STIMEFORMAT:
553 case LOCALE_SYEARMONTH:
554 default_id = lcid_LC_TIME;
555 break;
557 case LOCALE_IPAPERSIZE:
558 default_id = lcid_LC_PAPER;
559 break;
561 case LOCALE_IMEASURE:
562 default_id = lcid_LC_MEASUREMENT;
563 break;
565 case LOCALE_ICOUNTRY:
566 default_id = lcid_LC_TELEPHONE;
567 break;
569 if (default_id) lcid = default_id;
571 return ConvertDefaultLocale( lcid );
574 /***********************************************************************
575 * is_genitive_name_supported
577 * Determine could LCTYPE basically support genitive name form or not.
579 static BOOL is_genitive_name_supported( LCTYPE lctype )
581 switch(lctype & 0xffff)
583 case LOCALE_SMONTHNAME1:
584 case LOCALE_SMONTHNAME2:
585 case LOCALE_SMONTHNAME3:
586 case LOCALE_SMONTHNAME4:
587 case LOCALE_SMONTHNAME5:
588 case LOCALE_SMONTHNAME6:
589 case LOCALE_SMONTHNAME7:
590 case LOCALE_SMONTHNAME8:
591 case LOCALE_SMONTHNAME9:
592 case LOCALE_SMONTHNAME10:
593 case LOCALE_SMONTHNAME11:
594 case LOCALE_SMONTHNAME12:
595 case LOCALE_SMONTHNAME13:
596 return TRUE;
597 default:
598 return FALSE;
602 /***********************************************************************
603 * create_registry_key
605 * Create the Control Panel\\International registry key.
607 static inline HANDLE create_registry_key(void)
609 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
610 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
611 OBJECT_ATTRIBUTES attr;
612 UNICODE_STRING nameW;
613 HANDLE cpl_key, hkey = 0;
615 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
617 attr.Length = sizeof(attr);
618 attr.RootDirectory = hkey;
619 attr.ObjectName = &nameW;
620 attr.Attributes = 0;
621 attr.SecurityDescriptor = NULL;
622 attr.SecurityQualityOfService = NULL;
623 RtlInitUnicodeString( &nameW, cplW );
625 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
627 NtClose( attr.RootDirectory );
628 attr.RootDirectory = cpl_key;
629 RtlInitUnicodeString( &nameW, intlW );
630 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
632 NtClose( attr.RootDirectory );
633 return hkey;
637 /* update the registry settings for a given locale parameter */
638 /* return TRUE if an update was needed */
639 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
640 const LCTYPE *values, UINT nb_values )
642 static const WCHAR formatW[] = { '%','0','8','x',0 };
643 WCHAR bufferW[40];
644 UNICODE_STRING nameW;
645 DWORD count, i;
647 RtlInitUnicodeString( &nameW, name );
648 count = sizeof(bufferW);
649 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
651 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
652 LPCWSTR text = (LPCWSTR)info->Data;
654 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
655 TRACE( "updating registry, locale %s changed %s -> %08x\n",
656 debugstr_w(name), debugstr_w(text), lcid );
658 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
659 sprintfW( bufferW, formatW, lcid );
660 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
662 for (i = 0; i < nb_values; i++)
664 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
665 sizeof(bufferW)/sizeof(WCHAR) );
666 SetLocaleInfoW( lcid, values[i], bufferW );
668 return TRUE;
672 /***********************************************************************
673 * LOCALE_InitRegistry
675 * Update registry contents on startup if the user locale has changed.
676 * This simulates the action of the Windows control panel.
678 void LOCALE_InitRegistry(void)
680 static const WCHAR acpW[] = {'A','C','P',0};
681 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
682 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
683 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
684 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
685 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
686 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
687 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
688 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
689 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
690 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
691 static const struct
693 LPCWSTR name;
694 USHORT value;
695 } update_cp_values[] = {
696 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
697 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
698 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
700 static const LCTYPE lc_messages_values[] = {
701 LOCALE_SABBREVLANGNAME,
702 LOCALE_SCOUNTRY,
703 LOCALE_SLIST };
704 static const LCTYPE lc_monetary_values[] = {
705 LOCALE_SCURRENCY,
706 LOCALE_ICURRENCY,
707 LOCALE_INEGCURR,
708 LOCALE_ICURRDIGITS,
709 LOCALE_ILZERO,
710 LOCALE_SMONDECIMALSEP,
711 LOCALE_SMONGROUPING,
712 LOCALE_SMONTHOUSANDSEP };
713 static const LCTYPE lc_numeric_values[] = {
714 LOCALE_SDECIMAL,
715 LOCALE_STHOUSAND,
716 LOCALE_IDIGITS,
717 LOCALE_IDIGITSUBSTITUTION,
718 LOCALE_SNATIVEDIGITS,
719 LOCALE_INEGNUMBER,
720 LOCALE_SNEGATIVESIGN,
721 LOCALE_SPOSITIVESIGN,
722 LOCALE_SGROUPING };
723 static const LCTYPE lc_time_values[] = {
724 LOCALE_S1159,
725 LOCALE_S2359,
726 LOCALE_STIME,
727 LOCALE_ITIME,
728 LOCALE_ITLZERO,
729 LOCALE_SSHORTDATE,
730 LOCALE_SLONGDATE,
731 LOCALE_SDATE,
732 LOCALE_ITIMEMARKPOSN,
733 LOCALE_ICALENDARTYPE,
734 LOCALE_IFIRSTDAYOFWEEK,
735 LOCALE_IFIRSTWEEKOFYEAR,
736 LOCALE_STIMEFORMAT,
737 LOCALE_SYEARMONTH,
738 LOCALE_IDATE };
739 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
740 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
741 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
743 UNICODE_STRING nameW;
744 WCHAR bufferW[80];
745 DWORD count, i;
746 HANDLE hkey;
747 LCID lcid = GetUserDefaultLCID();
749 if (!(hkey = create_registry_key()))
750 return; /* don't do anything if we can't create the registry key */
752 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
753 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
754 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
755 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
756 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
757 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
758 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
759 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
760 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
761 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
762 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
763 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
764 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
765 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
767 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
769 static const WCHAR codepageW[] =
770 {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
771 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
772 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
774 OBJECT_ATTRIBUTES attr;
775 HANDLE nls_key;
776 DWORD len = 14;
778 RtlInitUnicodeString( &nameW, codepageW );
779 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
780 while (codepageW[len])
782 nameW.Length = len * sizeof(WCHAR);
783 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
784 NtClose( nls_key );
785 len++;
786 while (codepageW[len] && codepageW[len] != '\\') len++;
788 nameW.Length = len * sizeof(WCHAR);
789 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
791 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
793 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
794 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
795 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
796 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
798 NtClose( nls_key );
802 NtClose( hkey );
806 /***********************************************************************
807 * setup_unix_locales
809 static UINT setup_unix_locales(void)
811 struct locale_name locale_name;
812 WCHAR buffer[128], ctype_buff[128];
813 char *locale;
814 UINT unix_cp = 0;
816 if ((locale = setlocale( LC_CTYPE, NULL )))
818 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
819 parse_locale_name( ctype_buff, &locale_name );
820 lcid_LC_CTYPE = locale_name.lcid;
821 unix_cp = locale_name.codepage;
823 if (!lcid_LC_CTYPE) /* this one needs a default value */
824 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
826 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
827 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
829 #define GET_UNIX_LOCALE(cat) do \
830 if ((locale = setlocale( cat, NULL ))) \
832 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
833 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
834 else { \
835 parse_locale_name( buffer, &locale_name ); \
836 lcid_##cat = locale_name.lcid; \
837 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
838 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
840 } while (0)
842 GET_UNIX_LOCALE( LC_COLLATE );
843 GET_UNIX_LOCALE( LC_MESSAGES );
844 GET_UNIX_LOCALE( LC_MONETARY );
845 GET_UNIX_LOCALE( LC_NUMERIC );
846 GET_UNIX_LOCALE( LC_TIME );
847 #ifdef LC_PAPER
848 GET_UNIX_LOCALE( LC_PAPER );
849 #endif
850 #ifdef LC_MEASUREMENT
851 GET_UNIX_LOCALE( LC_MEASUREMENT );
852 #endif
853 #ifdef LC_TELEPHONE
854 GET_UNIX_LOCALE( LC_TELEPHONE );
855 #endif
857 #undef GET_UNIX_LOCALE
859 return unix_cp;
863 /***********************************************************************
864 * GetUserDefaultLangID (KERNEL32.@)
866 * Get the default language Id for the current user.
868 * PARAMS
869 * None.
871 * RETURNS
872 * The current LANGID of the default language for the current user.
874 LANGID WINAPI GetUserDefaultLangID(void)
876 return LANGIDFROMLCID(GetUserDefaultLCID());
880 /***********************************************************************
881 * GetSystemDefaultLangID (KERNEL32.@)
883 * Get the default language Id for the system.
885 * PARAMS
886 * None.
888 * RETURNS
889 * The current LANGID of the default language for the system.
891 LANGID WINAPI GetSystemDefaultLangID(void)
893 return LANGIDFROMLCID(GetSystemDefaultLCID());
897 /***********************************************************************
898 * GetUserDefaultLCID (KERNEL32.@)
900 * Get the default locale Id for the current user.
902 * PARAMS
903 * None.
905 * RETURNS
906 * The current LCID of the default locale for the current user.
908 LCID WINAPI GetUserDefaultLCID(void)
910 LCID lcid;
911 NtQueryDefaultLocale( TRUE, &lcid );
912 return lcid;
916 /***********************************************************************
917 * GetSystemDefaultLCID (KERNEL32.@)
919 * Get the default locale Id for the system.
921 * PARAMS
922 * None.
924 * RETURNS
925 * The current LCID of the default locale for the system.
927 LCID WINAPI GetSystemDefaultLCID(void)
929 LCID lcid;
930 NtQueryDefaultLocale( FALSE, &lcid );
931 return lcid;
934 /***********************************************************************
935 * GetSystemDefaultLocaleName (KERNEL32.@)
937 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
939 LCID lcid = GetSystemDefaultLCID();
940 return LCIDToLocaleName(lcid, localename, len, 0);
943 /***********************************************************************
944 * GetUserDefaultUILanguage (KERNEL32.@)
946 * Get the default user interface language Id for the current user.
948 * PARAMS
949 * None.
951 * RETURNS
952 * The current LANGID of the default UI language for the current user.
954 LANGID WINAPI GetUserDefaultUILanguage(void)
956 LANGID lang;
957 NtQueryDefaultUILanguage( &lang );
958 return lang;
962 /***********************************************************************
963 * GetSystemDefaultUILanguage (KERNEL32.@)
965 * Get the default user interface language Id for the system.
967 * PARAMS
968 * None.
970 * RETURNS
971 * The current LANGID of the default UI language for the system. This is
972 * typically the same language used during the installation process.
974 LANGID WINAPI GetSystemDefaultUILanguage(void)
976 LANGID lang;
977 NtQueryInstallUILanguage( &lang );
978 return lang;
982 /***********************************************************************
983 * LocaleNameToLCID (KERNEL32.@)
985 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
987 struct locale_name locale_name;
989 if (flags) FIXME( "unsupported flags %x\n", flags );
991 if (name == LOCALE_NAME_USER_DEFAULT)
992 return GetUserDefaultLCID();
994 /* string parsing */
995 parse_locale_name( name, &locale_name );
997 TRACE( "found lcid %x for %s, matches %d\n",
998 locale_name.lcid, debugstr_w(name), locale_name.matches );
1000 if (!locale_name.matches)
1001 WARN( "locale %s not recognized, defaulting to English\n", debugstr_w(name) );
1002 else if (locale_name.matches == 1)
1003 WARN( "locale %s not recognized, defaulting to %s\n",
1004 debugstr_w(name), debugstr_w(locale_name.lang) );
1006 return locale_name.lcid;
1010 /***********************************************************************
1011 * LCIDToLocaleName (KERNEL32.@)
1013 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1015 if (flags) FIXME( "unsupported flags %x\n", flags );
1017 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1021 /******************************************************************************
1022 * get_locale_value_name
1024 * Gets the registry value name for a given lctype.
1026 static const WCHAR *get_locale_value_name( DWORD lctype )
1028 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1029 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1030 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1031 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1032 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1033 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1034 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1035 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1036 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1037 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1038 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1039 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1040 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1041 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1042 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1043 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1044 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1045 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1046 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1047 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1048 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1049 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1050 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1051 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1052 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1053 static const WCHAR sListW[] = {'s','L','i','s','t',0};
1054 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1055 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1056 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1057 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1058 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1059 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1060 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1061 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1062 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1063 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1064 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1065 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1066 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1068 switch (lctype)
1070 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1071 * the values are stored in the registry, confirmed under Windows.
1073 case LOCALE_ICALENDARTYPE: return iCalendarTypeW;
1074 case LOCALE_ICURRDIGITS: return iCurrDigitsW;
1075 case LOCALE_ICURRENCY: return iCurrencyW;
1076 case LOCALE_IDIGITS: return iDigitsW;
1077 case LOCALE_IFIRSTDAYOFWEEK: return iFirstDayOfWeekW;
1078 case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1079 case LOCALE_ILZERO: return iLZeroW;
1080 case LOCALE_IMEASURE: return iMeasureW;
1081 case LOCALE_INEGCURR: return iNegCurrW;
1082 case LOCALE_INEGNUMBER: return iNegNumberW;
1083 case LOCALE_IPAPERSIZE: return iPaperSizeW;
1084 case LOCALE_ITIME: return iTimeW;
1085 case LOCALE_S1159: return s1159W;
1086 case LOCALE_S2359: return s2359W;
1087 case LOCALE_SCURRENCY: return sCurrencyW;
1088 case LOCALE_SDATE: return sDateW;
1089 case LOCALE_SDECIMAL: return sDecimalW;
1090 case LOCALE_SGROUPING: return sGroupingW;
1091 case LOCALE_SLIST: return sListW;
1092 case LOCALE_SLONGDATE: return sLongDateW;
1093 case LOCALE_SMONDECIMALSEP: return sMonDecimalSepW;
1094 case LOCALE_SMONGROUPING: return sMonGroupingW;
1095 case LOCALE_SMONTHOUSANDSEP: return sMonThousandSepW;
1096 case LOCALE_SNEGATIVESIGN: return sNegativeSignW;
1097 case LOCALE_SPOSITIVESIGN: return sPositiveSignW;
1098 case LOCALE_SSHORTDATE: return sShortDateW;
1099 case LOCALE_STHOUSAND: return sThousandW;
1100 case LOCALE_STIME: return sTimeW;
1101 case LOCALE_STIMEFORMAT: return sTimeFormatW;
1102 case LOCALE_SYEARMONTH: return sYearMonthW;
1104 /* The following are not listed under MSDN as supported,
1105 * but seem to be used and also stored in the registry.
1107 case LOCALE_ICOUNTRY: return iCountryW;
1108 case LOCALE_IDATE: return iDateW;
1109 case LOCALE_ILDATE: return iLDateW;
1110 case LOCALE_ITLZERO: return iTLZeroW;
1111 case LOCALE_SCOUNTRY: return sCountryW;
1112 case LOCALE_SABBREVLANGNAME: return sLanguageW;
1114 /* The following are used in XP and later */
1115 case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1116 case LOCALE_SNATIVEDIGITS: return sNativeDigitsW;
1117 case LOCALE_ITIMEMARKPOSN: return iTimePrefixW;
1119 return NULL;
1123 /******************************************************************************
1124 * get_registry_locale_info
1126 * Retrieve user-modified locale info from the registry.
1127 * Return length, 0 on error, -1 if not found.
1129 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1131 DWORD size;
1132 INT ret;
1133 HANDLE hkey;
1134 NTSTATUS status;
1135 UNICODE_STRING nameW;
1136 KEY_VALUE_PARTIAL_INFORMATION *info;
1137 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1139 if (!(hkey = create_registry_key())) return -1;
1141 RtlInitUnicodeString( &nameW, value );
1142 size = info_size + len * sizeof(WCHAR);
1144 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1146 NtClose( hkey );
1147 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1148 return 0;
1151 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1153 if (!status)
1155 ret = (size - info_size) / sizeof(WCHAR);
1156 /* append terminating null if needed */
1157 if (!ret || ((WCHAR *)info->Data)[ret-1])
1159 if (ret < len || !buffer) ret++;
1160 else
1162 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1163 ret = 0;
1166 if (ret && buffer)
1168 memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1169 buffer[ret-1] = 0;
1172 else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1174 ret = (size - info_size) / sizeof(WCHAR) + 1;
1176 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1178 ret = -1;
1180 else
1182 SetLastError( RtlNtStatusToDosError(status) );
1183 ret = 0;
1185 NtClose( hkey );
1186 HeapFree( GetProcessHeap(), 0, info );
1187 return ret;
1191 /******************************************************************************
1192 * GetLocaleInfoA (KERNEL32.@)
1194 * Get information about an aspect of a locale.
1196 * PARAMS
1197 * lcid [I] LCID of the locale
1198 * lctype [I] LCTYPE_ flags from "winnls.h"
1199 * buffer [O] Destination for the information
1200 * len [I] Length of buffer in characters
1202 * RETURNS
1203 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1204 * with the information.
1205 * Failure: 0. Use GetLastError() to determine the cause.
1207 * NOTES
1208 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1209 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1210 * which is a bit string.
1212 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1214 WCHAR *bufferW;
1215 INT lenW, ret;
1217 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1219 if (len < 0 || (len && !buffer))
1221 SetLastError( ERROR_INVALID_PARAMETER );
1222 return 0;
1224 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1226 SetLastError( ERROR_INVALID_FLAGS );
1227 return 0;
1230 if (!len) buffer = NULL;
1232 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1234 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1236 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1237 return 0;
1239 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1241 if ((lctype & LOCALE_RETURN_NUMBER) ||
1242 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1244 /* it's not an ASCII string, just bytes */
1245 ret *= sizeof(WCHAR);
1246 if (buffer)
1248 if (ret <= len) memcpy( buffer, bufferW, ret );
1249 else
1251 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1252 ret = 0;
1256 else
1258 UINT codepage = CP_ACP;
1259 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1260 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1263 HeapFree( GetProcessHeap(), 0, bufferW );
1264 return ret;
1267 static int get_value_base_by_lctype( LCTYPE lctype )
1269 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1272 /******************************************************************************
1273 * GetLocaleInfoW (KERNEL32.@)
1275 * See GetLocaleInfoA.
1277 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1279 LANGID lang_id;
1280 HRSRC hrsrc;
1281 HGLOBAL hmem;
1282 INT ret;
1283 UINT lcflags;
1284 const WCHAR *p;
1285 unsigned int i;
1287 if (len < 0 || (len && !buffer))
1289 SetLastError( ERROR_INVALID_PARAMETER );
1290 return 0;
1292 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1293 !is_genitive_name_supported( lctype ))
1295 SetLastError( ERROR_INVALID_FLAGS );
1296 return 0;
1299 if (!len) buffer = NULL;
1301 lcid = convert_default_lcid( lcid, lctype );
1303 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1304 lctype &= 0xffff;
1306 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1308 /* first check for overrides in the registry */
1310 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1311 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1313 const WCHAR *value = get_locale_value_name(lctype);
1315 if (value)
1317 if (lcflags & LOCALE_RETURN_NUMBER)
1319 WCHAR tmp[16];
1320 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1321 if (ret > 0)
1323 WCHAR *end;
1324 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1325 if (*end) /* invalid number */
1327 SetLastError( ERROR_INVALID_FLAGS );
1328 return 0;
1330 ret = sizeof(UINT)/sizeof(WCHAR);
1331 if (!buffer) return ret;
1332 if (ret > len)
1334 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1335 return 0;
1337 memcpy( buffer, &number, sizeof(number) );
1340 else ret = get_registry_locale_info( value, buffer, len );
1342 if (ret != -1) return ret;
1346 /* now load it from kernel resources */
1348 lang_id = LANGIDFROMLCID( lcid );
1350 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1351 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1352 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1354 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1355 ULongToPtr((lctype >> 4) + 1), lang_id )))
1357 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1358 return 0;
1360 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1361 return 0;
1363 p = LockResource( hmem );
1364 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1366 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1367 else if (is_genitive_name_supported( lctype ) && *p)
1369 /* genitive form's stored after a null separator from a nominative */
1370 for (i = 1; i <= *p; i++) if (!p[i]) break;
1372 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1374 ret = *p - i + 1;
1375 p += i;
1377 else ret = i;
1379 else
1380 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1382 if (!buffer) return ret;
1384 if (ret > len)
1386 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1387 return 0;
1390 if (lcflags & LOCALE_RETURN_NUMBER)
1392 UINT number;
1393 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1394 if (!tmp) return 0;
1395 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1396 tmp[*p] = 0;
1397 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1398 if (!*end)
1399 memcpy( buffer, &number, sizeof(number) );
1400 else /* invalid number */
1402 SetLastError( ERROR_INVALID_FLAGS );
1403 ret = 0;
1405 HeapFree( GetProcessHeap(), 0, tmp );
1407 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1408 lcid, lctype, buffer, len, number );
1410 else
1412 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1413 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1415 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1416 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1418 return ret;
1421 /******************************************************************************
1422 * GetLocaleInfoEx (KERNEL32.@)
1424 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1426 return GetLocaleInfoW(LocaleNameToLCID(locale, 0), info, buffer, len);
1429 /******************************************************************************
1430 * SetLocaleInfoA [KERNEL32.@]
1432 * Set information about an aspect of a locale.
1434 * PARAMS
1435 * lcid [I] LCID of the locale
1436 * lctype [I] LCTYPE_ flags from "winnls.h"
1437 * data [I] Information to set
1439 * RETURNS
1440 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1441 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1442 * Failure: FALSE. Use GetLastError() to determine the cause.
1444 * NOTES
1445 * - Values are only be set for the current user locale; the system locale
1446 * settings cannot be changed.
1447 * - Any settings changed by this call are lost when the locale is changed by
1448 * the control panel (in Wine, this happens every time you change LANG).
1449 * - The native implementation of this function does not check that lcid matches
1450 * the current user locale, and simply sets the new values. Wine warns you in
1451 * this case, but behaves the same.
1453 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1455 UINT codepage = CP_ACP;
1456 WCHAR *strW;
1457 DWORD len;
1458 BOOL ret;
1460 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1462 if (!data)
1464 SetLastError( ERROR_INVALID_PARAMETER );
1465 return FALSE;
1467 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1468 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1470 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1471 return FALSE;
1473 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1474 ret = SetLocaleInfoW( lcid, lctype, strW );
1475 HeapFree( GetProcessHeap(), 0, strW );
1476 return ret;
1480 /******************************************************************************
1481 * SetLocaleInfoW (KERNEL32.@)
1483 * See SetLocaleInfoA.
1485 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1487 const WCHAR *value;
1488 static const WCHAR intlW[] = {'i','n','t','l',0 };
1489 UNICODE_STRING valueW;
1490 NTSTATUS status;
1491 HANDLE hkey;
1493 lctype &= 0xffff;
1494 value = get_locale_value_name( lctype );
1496 if (!data || !value)
1498 SetLastError( ERROR_INVALID_PARAMETER );
1499 return FALSE;
1502 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1504 SetLastError( ERROR_INVALID_FLAGS );
1505 return FALSE;
1508 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1510 /* FIXME: should check that data to set is sane */
1512 /* FIXME: profile functions should map to registry */
1513 WriteProfileStringW( intlW, value, data );
1515 if (!(hkey = create_registry_key())) return FALSE;
1516 RtlInitUnicodeString( &valueW, value );
1517 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1519 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1521 /* Set I-value from S value */
1522 WCHAR *lpD, *lpM, *lpY;
1523 WCHAR szBuff[2];
1525 lpD = strrchrW(data, 'd');
1526 lpM = strrchrW(data, 'M');
1527 lpY = strrchrW(data, 'y');
1529 if (lpD <= lpM)
1531 szBuff[0] = '1'; /* D-M-Y */
1533 else
1535 if (lpY <= lpM)
1536 szBuff[0] = '2'; /* Y-M-D */
1537 else
1538 szBuff[0] = '0'; /* M-D-Y */
1541 szBuff[1] = '\0';
1543 if (lctype == LOCALE_SSHORTDATE)
1544 lctype = LOCALE_IDATE;
1545 else
1546 lctype = LOCALE_ILDATE;
1548 value = get_locale_value_name( lctype );
1550 WriteProfileStringW( intlW, value, szBuff );
1552 RtlInitUnicodeString( &valueW, value );
1553 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1556 NtClose( hkey );
1558 if (status) SetLastError( RtlNtStatusToDosError(status) );
1559 return !status;
1563 /******************************************************************************
1564 * GetACP (KERNEL32.@)
1566 * Get the current Ansi code page Id for the system.
1568 * PARAMS
1569 * None.
1571 * RETURNS
1572 * The current Ansi code page identifier for the system.
1574 UINT WINAPI GetACP(void)
1576 assert( ansi_cptable );
1577 return ansi_cptable->info.codepage;
1581 /******************************************************************************
1582 * SetCPGlobal (KERNEL32.@)
1584 * Set the current Ansi code page Id for the system.
1586 * PARAMS
1587 * acp [I] code page ID to be the new ACP.
1589 * RETURNS
1590 * The previous ACP.
1592 UINT WINAPI SetCPGlobal( UINT acp )
1594 UINT ret = GetACP();
1595 const union cptable *new_cptable = wine_cp_get_table( acp );
1597 if (new_cptable) ansi_cptable = new_cptable;
1598 return ret;
1602 /***********************************************************************
1603 * GetOEMCP (KERNEL32.@)
1605 * Get the current OEM code page Id for the system.
1607 * PARAMS
1608 * None.
1610 * RETURNS
1611 * The current OEM code page identifier for the system.
1613 UINT WINAPI GetOEMCP(void)
1615 assert( oem_cptable );
1616 return oem_cptable->info.codepage;
1620 /***********************************************************************
1621 * IsValidCodePage (KERNEL32.@)
1623 * Determine if a given code page identifier is valid.
1625 * PARAMS
1626 * codepage [I] Code page Id to verify.
1628 * RETURNS
1629 * TRUE, If codepage is valid and available on the system,
1630 * FALSE otherwise.
1632 BOOL WINAPI IsValidCodePage( UINT codepage )
1634 switch(codepage) {
1635 case CP_UTF7:
1636 case CP_UTF8:
1637 return TRUE;
1638 default:
1639 return wine_cp_get_table( codepage ) != NULL;
1644 /***********************************************************************
1645 * IsDBCSLeadByteEx (KERNEL32.@)
1647 * Determine if a character is a lead byte in a given code page.
1649 * PARAMS
1650 * codepage [I] Code page for the test.
1651 * testchar [I] Character to test
1653 * RETURNS
1654 * TRUE, if testchar is a lead byte in codepage,
1655 * FALSE otherwise.
1657 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1659 const union cptable *table = get_codepage_table( codepage );
1660 return table && wine_is_dbcs_leadbyte( table, testchar );
1664 /***********************************************************************
1665 * IsDBCSLeadByte (KERNEL32.@)
1666 * IsDBCSLeadByte (KERNEL.207)
1668 * Determine if a character is a lead byte.
1670 * PARAMS
1671 * testchar [I] Character to test
1673 * RETURNS
1674 * TRUE, if testchar is a lead byte in the ANSI code page,
1675 * FALSE otherwise.
1677 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1679 if (!ansi_cptable) return FALSE;
1680 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1684 /***********************************************************************
1685 * GetCPInfo (KERNEL32.@)
1687 * Get information about a code page.
1689 * PARAMS
1690 * codepage [I] Code page number
1691 * cpinfo [O] Destination for code page information
1693 * RETURNS
1694 * Success: TRUE. cpinfo is updated with the information about codepage.
1695 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1697 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1699 const union cptable *table;
1701 if (!cpinfo)
1703 SetLastError( ERROR_INVALID_PARAMETER );
1704 return FALSE;
1707 if (!(table = get_codepage_table( codepage )))
1709 switch(codepage)
1711 case CP_UTF7:
1712 case CP_UTF8:
1713 cpinfo->DefaultChar[0] = 0x3f;
1714 cpinfo->DefaultChar[1] = 0;
1715 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1716 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1717 return TRUE;
1720 SetLastError( ERROR_INVALID_PARAMETER );
1721 return FALSE;
1723 if (table->info.def_char & 0xff00)
1725 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1726 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1728 else
1730 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1731 cpinfo->DefaultChar[1] = 0;
1733 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1734 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1735 else
1736 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1738 return TRUE;
1741 /***********************************************************************
1742 * GetCPInfoExA (KERNEL32.@)
1744 * Get extended information about a code page.
1746 * PARAMS
1747 * codepage [I] Code page number
1748 * dwFlags [I] Reserved, must to 0.
1749 * cpinfo [O] Destination for code page information
1751 * RETURNS
1752 * Success: TRUE. cpinfo is updated with the information about codepage.
1753 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1755 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1757 CPINFOEXW cpinfoW;
1759 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1760 return FALSE;
1762 /* the layout is the same except for CodePageName */
1763 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1764 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1765 return TRUE;
1768 /***********************************************************************
1769 * GetCPInfoExW (KERNEL32.@)
1771 * Unicode version of GetCPInfoExA.
1773 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1775 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1776 return FALSE;
1778 switch(codepage)
1780 case CP_UTF7:
1782 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1784 cpinfo->CodePage = CP_UTF7;
1785 cpinfo->UnicodeDefaultChar = 0x3f;
1786 strcpyW(cpinfo->CodePageName, utf7);
1787 break;
1790 case CP_UTF8:
1792 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1794 cpinfo->CodePage = CP_UTF8;
1795 cpinfo->UnicodeDefaultChar = 0x3f;
1796 strcpyW(cpinfo->CodePageName, utf8);
1797 break;
1800 default:
1802 const union cptable *table = get_codepage_table( codepage );
1804 cpinfo->CodePage = table->info.codepage;
1805 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1806 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1807 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1808 break;
1811 return TRUE;
1814 /***********************************************************************
1815 * EnumSystemCodePagesA (KERNEL32.@)
1817 * Call a user defined function for every code page installed on the system.
1819 * PARAMS
1820 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1821 * flags [I] Reserved, set to 0.
1823 * RETURNS
1824 * TRUE, If all code pages have been enumerated, or
1825 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1827 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1829 const union cptable *table;
1830 char buffer[10];
1831 int index = 0;
1833 for (;;)
1835 if (!(table = wine_cp_enum_table( index++ ))) break;
1836 sprintf( buffer, "%d", table->info.codepage );
1837 if (!lpfnCodePageEnum( buffer )) break;
1839 return TRUE;
1843 /***********************************************************************
1844 * EnumSystemCodePagesW (KERNEL32.@)
1846 * See EnumSystemCodePagesA.
1848 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1850 const union cptable *table;
1851 WCHAR buffer[10], *p;
1852 int page, index = 0;
1854 for (;;)
1856 if (!(table = wine_cp_enum_table( index++ ))) break;
1857 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1858 *--p = 0;
1859 page = table->info.codepage;
1862 *--p = '0' + (page % 10);
1863 page /= 10;
1864 } while( page );
1865 if (!lpfnCodePageEnum( p )) break;
1867 return TRUE;
1871 /***********************************************************************
1872 * MultiByteToWideChar (KERNEL32.@)
1874 * Convert a multibyte character string into a Unicode string.
1876 * PARAMS
1877 * page [I] Codepage character set to convert from
1878 * flags [I] Character mapping flags
1879 * src [I] Source string buffer
1880 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1881 * dst [O] Destination buffer
1882 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1884 * RETURNS
1885 * Success: If dstlen > 0, the number of characters written to dst.
1886 * If dstlen == 0, the number of characters needed to perform the
1887 * conversion. In both cases the count includes the terminating NUL.
1888 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1889 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1890 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1891 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1892 * possible for src.
1894 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1895 LPWSTR dst, INT dstlen )
1897 const union cptable *table;
1898 int ret;
1900 if (!src || !srclen || (!dst && dstlen))
1902 SetLastError( ERROR_INVALID_PARAMETER );
1903 return 0;
1906 if (srclen < 0) srclen = strlen(src) + 1;
1908 switch(page)
1910 case CP_SYMBOL:
1911 if (flags)
1913 SetLastError( ERROR_INVALID_FLAGS );
1914 return 0;
1916 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1917 break;
1918 case CP_UTF7:
1919 if (flags)
1921 SetLastError( ERROR_INVALID_FLAGS );
1922 return 0;
1924 FIXME("UTF-7 not supported\n");
1925 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1926 return 0;
1927 case CP_UNIXCP:
1928 if (unix_cptable)
1930 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1931 break;
1933 #ifdef __APPLE__
1934 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1935 #endif
1936 /* fall through */
1937 case CP_UTF8:
1938 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1939 break;
1940 default:
1941 if (!(table = get_codepage_table( page )))
1943 SetLastError( ERROR_INVALID_PARAMETER );
1944 return 0;
1946 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1947 break;
1950 if (ret < 0)
1952 switch(ret)
1954 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1955 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1957 ret = 0;
1959 TRACE("cp %d %s -> %s, ret = %d\n",
1960 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1961 return ret;
1965 /***********************************************************************
1966 * WideCharToMultiByte (KERNEL32.@)
1968 * Convert a Unicode character string into a multibyte string.
1970 * PARAMS
1971 * page [I] Code page character set to convert to
1972 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
1973 * src [I] Source string buffer
1974 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1975 * dst [O] Destination buffer
1976 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
1977 * defchar [I] Default character to use for conversion if no exact
1978 * conversion can be made
1979 * used [O] Set if default character was used in the conversion
1981 * RETURNS
1982 * Success: If dstlen > 0, the number of characters written to dst.
1983 * If dstlen == 0, number of characters needed to perform the
1984 * conversion. In both cases the count includes the terminating NUL.
1985 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1986 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1987 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1988 * parameter was given.
1990 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1991 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1993 const union cptable *table;
1994 int ret, used_tmp;
1996 if (!src || !srclen || (!dst && dstlen))
1998 SetLastError( ERROR_INVALID_PARAMETER );
1999 return 0;
2002 if (srclen < 0) srclen = strlenW(src) + 1;
2004 switch(page)
2006 case CP_SYMBOL:
2007 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2008 if (flags)
2010 SetLastError( ERROR_INVALID_FLAGS );
2011 return 0;
2013 if (defchar || used)
2015 SetLastError( ERROR_INVALID_PARAMETER );
2016 return 0;
2018 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2019 break;
2020 case CP_UTF7:
2021 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2022 if (defchar || used)
2024 SetLastError( ERROR_INVALID_PARAMETER );
2025 return 0;
2027 if (flags)
2029 SetLastError( ERROR_INVALID_FLAGS );
2030 return 0;
2032 FIXME("UTF-7 not supported\n");
2033 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2034 return 0;
2035 case CP_UNIXCP:
2036 if (unix_cptable)
2038 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2039 defchar, used ? &used_tmp : NULL );
2040 break;
2042 /* fall through */
2043 case CP_UTF8:
2044 if (defchar || used)
2046 SetLastError( ERROR_INVALID_PARAMETER );
2047 return 0;
2049 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2050 break;
2051 default:
2052 if (!(table = get_codepage_table( page )))
2054 SetLastError( ERROR_INVALID_PARAMETER );
2055 return 0;
2057 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2058 defchar, used ? &used_tmp : NULL );
2059 if (used) *used = used_tmp;
2060 break;
2063 if (ret < 0)
2065 switch(ret)
2067 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2068 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2070 ret = 0;
2072 TRACE("cp %d %s -> %s, ret = %d\n",
2073 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2074 return ret;
2078 /***********************************************************************
2079 * GetThreadLocale (KERNEL32.@)
2081 * Get the current threads locale.
2083 * PARAMS
2084 * None.
2086 * RETURNS
2087 * The LCID currently associated with the calling thread.
2089 LCID WINAPI GetThreadLocale(void)
2091 LCID ret = NtCurrentTeb()->CurrentLocale;
2092 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2093 return ret;
2096 /**********************************************************************
2097 * SetThreadLocale (KERNEL32.@)
2099 * Set the current threads locale.
2101 * PARAMS
2102 * lcid [I] LCID of the locale to set
2104 * RETURNS
2105 * Success: TRUE. The threads locale is set to lcid.
2106 * Failure: FALSE. Use GetLastError() to determine the cause.
2108 BOOL WINAPI SetThreadLocale( LCID lcid )
2110 TRACE("(0x%04X)\n", lcid);
2112 lcid = ConvertDefaultLocale(lcid);
2114 if (lcid != GetThreadLocale())
2116 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2118 SetLastError(ERROR_INVALID_PARAMETER);
2119 return FALSE;
2122 NtCurrentTeb()->CurrentLocale = lcid;
2124 return TRUE;
2127 /**********************************************************************
2128 * SetThreadUILanguage (KERNEL32.@)
2130 * Set the current threads UI language.
2132 * PARAMS
2133 * langid [I] LANGID of the language to set, or 0 to use
2134 * the available language which is best supported
2135 * for console applications
2137 * RETURNS
2138 * Success: The return value is the same as the input value.
2139 * Failure: The return value differs from the input value.
2140 * Use GetLastError() to determine the cause.
2142 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2144 TRACE("(0x%04x) stub - returning success\n", langid);
2145 return langid;
2148 /******************************************************************************
2149 * ConvertDefaultLocale (KERNEL32.@)
2151 * Convert a default locale identifier into a real identifier.
2153 * PARAMS
2154 * lcid [I] LCID identifier of the locale to convert
2156 * RETURNS
2157 * lcid unchanged, if not a default locale or its sublanguage is
2158 * not SUBLANG_NEUTRAL.
2159 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2160 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2161 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2163 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2165 LANGID langid;
2167 switch (lcid)
2169 case LOCALE_SYSTEM_DEFAULT:
2170 lcid = GetSystemDefaultLCID();
2171 break;
2172 case LOCALE_USER_DEFAULT:
2173 case LOCALE_NEUTRAL:
2174 lcid = GetUserDefaultLCID();
2175 break;
2176 default:
2177 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2178 langid = LANGIDFROMLCID(lcid);
2179 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2181 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2182 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2185 return lcid;
2189 /******************************************************************************
2190 * IsValidLocale (KERNEL32.@)
2192 * Determine if a locale is valid.
2194 * PARAMS
2195 * lcid [I] LCID of the locale to check
2196 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2198 * RETURNS
2199 * TRUE, if lcid is valid,
2200 * FALSE, otherwise.
2202 * NOTES
2203 * Wine does not currently make the distinction between supported and installed. All
2204 * languages supported are installed by default.
2206 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2208 /* check if language is registered in the kernel32 resources */
2209 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2210 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2214 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2215 LPCSTR name, WORD LangID, LONG_PTR lParam )
2217 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2218 char buf[20];
2220 sprintf(buf, "%08x", (UINT)LangID);
2221 return lpfnLocaleEnum( buf );
2224 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2225 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2227 static const WCHAR formatW[] = {'%','0','8','x',0};
2228 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2229 WCHAR buf[20];
2230 sprintfW( buf, formatW, (UINT)LangID );
2231 return lpfnLocaleEnum( buf );
2234 /******************************************************************************
2235 * EnumSystemLocalesA (KERNEL32.@)
2237 * Call a users function for each locale available on the system.
2239 * PARAMS
2240 * lpfnLocaleEnum [I] Callback function to call for each locale
2241 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2243 * RETURNS
2244 * Success: TRUE.
2245 * Failure: FALSE. Use GetLastError() to determine the cause.
2247 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2249 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2250 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2251 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2252 (LONG_PTR)lpfnLocaleEnum);
2253 return TRUE;
2257 /******************************************************************************
2258 * EnumSystemLocalesW (KERNEL32.@)
2260 * See EnumSystemLocalesA.
2262 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2264 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2265 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2266 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2267 (LONG_PTR)lpfnLocaleEnum);
2268 return TRUE;
2272 struct enum_locale_ex_data
2274 LOCALE_ENUMPROCEX proc;
2275 DWORD flags;
2276 LPARAM lparam;
2279 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2280 LPCWSTR name, WORD lang, LONG_PTR lparam )
2282 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2283 WCHAR buffer[256];
2284 DWORD neutral;
2285 unsigned int flags;
2287 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2288 buffer, sizeof(buffer) / sizeof(WCHAR) );
2289 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2290 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2291 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2292 neutral = 0;
2293 flags = LOCALE_WINDOWS;
2294 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2295 if (data->flags && ~(data->flags & flags)) return TRUE;
2296 return data->proc( buffer, flags, data->lparam );
2299 /******************************************************************************
2300 * EnumSystemLocalesEx (KERNEL32.@)
2302 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2304 struct enum_locale_ex_data data;
2306 if (reserved)
2308 SetLastError( ERROR_INVALID_PARAMETER );
2309 return FALSE;
2311 data.proc = proc;
2312 data.flags = flags;
2313 data.lparam = lparam;
2314 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2315 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2316 enum_locale_ex_proc, (LONG_PTR)&data );
2317 return TRUE;
2321 /***********************************************************************
2322 * VerLanguageNameA (KERNEL32.@)
2324 * Get the name of a language.
2326 * PARAMS
2327 * wLang [I] LANGID of the language
2328 * szLang [O] Destination for the language name
2330 * RETURNS
2331 * Success: The size of the language name. If szLang is non-NULL, it is filled
2332 * with the name.
2333 * Failure: 0. Use GetLastError() to determine the cause.
2336 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2338 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2342 /***********************************************************************
2343 * VerLanguageNameW (KERNEL32.@)
2345 * See VerLanguageNameA.
2347 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2349 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2353 /******************************************************************************
2354 * GetStringTypeW (KERNEL32.@)
2356 * See GetStringTypeA.
2358 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2360 static const unsigned char type2_map[16] =
2362 C2_NOTAPPLICABLE, /* unassigned */
2363 C2_LEFTTORIGHT, /* L */
2364 C2_RIGHTTOLEFT, /* R */
2365 C2_EUROPENUMBER, /* EN */
2366 C2_EUROPESEPARATOR, /* ES */
2367 C2_EUROPETERMINATOR, /* ET */
2368 C2_ARABICNUMBER, /* AN */
2369 C2_COMMONSEPARATOR, /* CS */
2370 C2_BLOCKSEPARATOR, /* B */
2371 C2_SEGMENTSEPARATOR, /* S */
2372 C2_WHITESPACE, /* WS */
2373 C2_OTHERNEUTRAL, /* ON */
2374 C2_RIGHTTOLEFT, /* AL */
2375 C2_NOTAPPLICABLE, /* NSM */
2376 C2_NOTAPPLICABLE, /* BN */
2377 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2380 if (count == -1) count = strlenW(src) + 1;
2381 switch(type)
2383 case CT_CTYPE1:
2384 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2385 break;
2386 case CT_CTYPE2:
2387 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2388 break;
2389 case CT_CTYPE3:
2391 WARN("CT_CTYPE3: semi-stub.\n");
2392 while (count--)
2394 int c = *src;
2395 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2397 type1 = get_char_typeW( *src++ ) & 0xfff;
2398 /* try to construct type3 from type1 */
2399 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2400 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2401 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2402 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2403 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2404 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2405 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2407 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2408 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2409 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2410 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2411 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2412 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2413 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2414 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2416 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2417 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2418 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2419 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2420 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2421 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2422 *chartype++ = type3;
2424 break;
2426 default:
2427 SetLastError( ERROR_INVALID_PARAMETER );
2428 return FALSE;
2430 return TRUE;
2434 /******************************************************************************
2435 * GetStringTypeExW (KERNEL32.@)
2437 * See GetStringTypeExA.
2439 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2441 /* locale is ignored for Unicode */
2442 return GetStringTypeW( type, src, count, chartype );
2446 /******************************************************************************
2447 * GetStringTypeA (KERNEL32.@)
2449 * Get characteristics of the characters making up a string.
2451 * PARAMS
2452 * locale [I] Locale Id for the string
2453 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2454 * src [I] String to analyse
2455 * count [I] Length of src in chars, or -1 if src is NUL terminated
2456 * chartype [O] Destination for the calculated characteristics
2458 * RETURNS
2459 * Success: TRUE. chartype is filled with the requested characteristics of each char
2460 * in src.
2461 * Failure: FALSE. Use GetLastError() to determine the cause.
2463 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2465 UINT cp;
2466 INT countW;
2467 LPWSTR srcW;
2468 BOOL ret = FALSE;
2470 if(count == -1) count = strlen(src) + 1;
2472 if (!(cp = get_lcid_codepage( locale )))
2474 FIXME("For locale %04x using current ANSI code page\n", locale);
2475 cp = GetACP();
2478 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2479 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2481 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2483 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2484 * string, with multibyte characters there maybe be more bytes in count
2485 * than character space in the buffer!
2487 ret = GetStringTypeW(type, srcW, countW, chartype);
2488 HeapFree(GetProcessHeap(), 0, srcW);
2490 return ret;
2493 /******************************************************************************
2494 * GetStringTypeExA (KERNEL32.@)
2496 * Get characteristics of the characters making up a string.
2498 * PARAMS
2499 * locale [I] Locale Id for the string
2500 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2501 * src [I] String to analyse
2502 * count [I] Length of src in chars, or -1 if src is NUL terminated
2503 * chartype [O] Destination for the calculated characteristics
2505 * RETURNS
2506 * Success: TRUE. chartype is filled with the requested characteristics of each char
2507 * in src.
2508 * Failure: FALSE. Use GetLastError() to determine the cause.
2510 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2512 return GetStringTypeA(locale, type, src, count, chartype);
2515 /*************************************************************************
2516 * LCMapStringEx (KERNEL32.@)
2518 * Map characters in a locale sensitive string.
2520 * PARAMS
2521 * name [I] Locale name for the conversion.
2522 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2523 * src [I] String to map
2524 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2525 * dst [O] Destination for mapped string
2526 * dstlen [I] Length of dst in characters
2527 * version [I] reserved, must be NULL
2528 * reserved [I] reserved, must be NULL
2529 * lparam [I] reserved, must be 0
2531 * RETURNS
2532 * Success: The length of the mapped string in dst, including the NUL terminator.
2533 * Failure: 0. Use GetLastError() to determine the cause.
2535 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2536 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2538 LPWSTR dst_ptr;
2540 if (version) FIXME("unsupported version structure %p\n", version);
2541 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2542 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2544 if (!src || !srclen || dstlen < 0)
2546 SetLastError(ERROR_INVALID_PARAMETER);
2547 return 0;
2550 /* mutually exclusive flags */
2551 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2552 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2553 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2554 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2556 SetLastError(ERROR_INVALID_FLAGS);
2557 return 0;
2560 if (!dstlen) dst = NULL;
2562 if (flags & LCMAP_SORTKEY)
2564 INT ret;
2565 if (src == dst)
2567 SetLastError(ERROR_INVALID_FLAGS);
2568 return 0;
2571 if (srclen < 0) srclen = strlenW(src);
2573 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2574 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2576 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2577 if (ret == 0)
2578 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2579 else
2580 ret++;
2581 return ret;
2584 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2585 if (flags & SORT_STRINGSORT)
2587 SetLastError(ERROR_INVALID_FLAGS);
2588 return 0;
2591 if (srclen < 0) srclen = strlenW(src) + 1;
2593 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2594 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2596 if (!dst) /* return required string length */
2598 INT len;
2600 for (len = 0; srclen; src++, srclen--)
2602 WCHAR wch = *src;
2603 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2604 * and skips white space and punctuation characters for
2605 * NORM_IGNORESYMBOLS.
2607 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2608 continue;
2609 len++;
2611 return len;
2614 if (flags & LCMAP_UPPERCASE)
2616 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2618 WCHAR wch = *src;
2619 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2620 continue;
2621 *dst_ptr++ = toupperW(wch);
2622 dstlen--;
2625 else if (flags & LCMAP_LOWERCASE)
2627 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2629 WCHAR wch = *src;
2630 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2631 continue;
2632 *dst_ptr++ = tolowerW(wch);
2633 dstlen--;
2636 else
2638 if (src == dst)
2640 SetLastError(ERROR_INVALID_FLAGS);
2641 return 0;
2643 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2645 WCHAR wch = *src;
2646 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2647 continue;
2648 *dst_ptr++ = wch;
2649 dstlen--;
2653 if (srclen)
2655 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2656 return 0;
2659 return dst_ptr - dst;
2662 /*************************************************************************
2663 * LCMapStringW (KERNEL32.@)
2665 * See LCMapStringA.
2667 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2668 LPWSTR dst, INT dstlen)
2670 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2671 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2673 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2676 /*************************************************************************
2677 * LCMapStringA (KERNEL32.@)
2679 * Map characters in a locale sensitive string.
2681 * PARAMS
2682 * lcid [I] LCID for the conversion.
2683 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2684 * src [I] String to map
2685 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2686 * dst [O] Destination for mapped string
2687 * dstlen [I] Length of dst in characters
2689 * RETURNS
2690 * Success: The length of the mapped string in dst, including the NUL terminator.
2691 * Failure: 0. Use GetLastError() to determine the cause.
2693 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2694 LPSTR dst, INT dstlen)
2696 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2697 LPWSTR srcW, dstW;
2698 INT ret = 0, srclenW, dstlenW;
2699 UINT locale_cp = CP_ACP;
2701 if (!src || !srclen || dstlen < 0)
2703 SetLastError(ERROR_INVALID_PARAMETER);
2704 return 0;
2707 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2709 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2710 if (srclenW)
2711 srcW = bufW;
2712 else
2714 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2715 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2716 if (!srcW)
2718 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2719 return 0;
2721 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2724 if (flags & LCMAP_SORTKEY)
2726 if (src == dst)
2728 SetLastError(ERROR_INVALID_FLAGS);
2729 goto map_string_exit;
2731 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2732 if (ret == 0)
2733 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2734 else
2735 ret++;
2736 goto map_string_exit;
2739 if (flags & SORT_STRINGSORT)
2741 SetLastError(ERROR_INVALID_FLAGS);
2742 goto map_string_exit;
2745 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2746 if (!dstlenW)
2747 goto map_string_exit;
2749 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2750 if (!dstW)
2752 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2753 goto map_string_exit;
2756 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2757 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2758 HeapFree(GetProcessHeap(), 0, dstW);
2760 map_string_exit:
2761 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2762 return ret;
2765 /*************************************************************************
2766 * FoldStringA (KERNEL32.@)
2768 * Map characters in a string.
2770 * PARAMS
2771 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2772 * src [I] String to map
2773 * srclen [I] Length of src, or -1 if src is NUL terminated
2774 * dst [O] Destination for mapped string
2775 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2777 * RETURNS
2778 * Success: The length of the string written to dst, including the terminating NUL. If
2779 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2780 * and dst may be NULL.
2781 * Failure: 0. Use GetLastError() to determine the cause.
2783 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2784 LPSTR dst, INT dstlen)
2786 INT ret = 0, srclenW = 0;
2787 WCHAR *srcW = NULL, *dstW = NULL;
2789 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2791 SetLastError(ERROR_INVALID_PARAMETER);
2792 return 0;
2795 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2796 src, srclen, NULL, 0);
2797 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2799 if (!srcW)
2801 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2802 goto FoldStringA_exit;
2805 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2806 src, srclen, srcW, srclenW);
2808 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2810 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2811 if (ret && dstlen)
2813 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2815 if (!dstW)
2817 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2818 goto FoldStringA_exit;
2821 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2822 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2824 ret = 0;
2825 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2829 HeapFree(GetProcessHeap(), 0, dstW);
2831 FoldStringA_exit:
2832 HeapFree(GetProcessHeap(), 0, srcW);
2833 return ret;
2836 /*************************************************************************
2837 * FoldStringW (KERNEL32.@)
2839 * See FoldStringA.
2841 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2842 LPWSTR dst, INT dstlen)
2844 int ret;
2846 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2848 case 0:
2849 if (dwFlags)
2850 break;
2851 /* Fall through for dwFlags == 0 */
2852 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2853 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2854 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2855 SetLastError(ERROR_INVALID_FLAGS);
2856 return 0;
2859 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2861 SetLastError(ERROR_INVALID_PARAMETER);
2862 return 0;
2865 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2866 if (!ret)
2867 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2868 return ret;
2871 /******************************************************************************
2872 * CompareStringW (KERNEL32.@)
2874 * See CompareStringA.
2876 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2877 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2879 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2882 /******************************************************************************
2883 * CompareStringEx (KERNEL32.@)
2885 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2886 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2888 INT ret;
2890 if (version) FIXME("unexpected version parameter\n");
2891 if (reserved) FIXME("unexpected reserved value\n");
2892 if (lParam) FIXME("unexpected lParam\n");
2894 if (!str1 || !str2)
2896 SetLastError(ERROR_INVALID_PARAMETER);
2897 return 0;
2900 if( flags & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2901 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2903 SetLastError(ERROR_INVALID_FLAGS);
2904 return 0;
2907 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2908 if (flags & 0x10000000)
2909 WARN("Ignoring unknown flags 0x10000000\n");
2911 if (len1 < 0) len1 = strlenW(str1);
2912 if (len2 < 0) len2 = strlenW(str2);
2914 ret = wine_compare_string(flags, str1, len1, str2, len2);
2916 if (ret) /* need to translate result */
2917 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2918 return CSTR_EQUAL;
2921 /******************************************************************************
2922 * CompareStringA (KERNEL32.@)
2924 * Compare two locale sensitive strings.
2926 * PARAMS
2927 * lcid [I] LCID for the comparison
2928 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
2929 * str1 [I] First string to compare
2930 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
2931 * str2 [I] Second string to compare
2932 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
2934 * RETURNS
2935 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2936 * str1 is less than, equal to or greater than str2 respectively.
2937 * Failure: FALSE. Use GetLastError() to determine the cause.
2939 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
2940 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2942 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2943 WCHAR *buf2W = buf1W + 130;
2944 LPWSTR str1W, str2W;
2945 INT len1W, len2W, ret;
2946 UINT locale_cp = CP_ACP;
2948 if (!str1 || !str2)
2950 SetLastError(ERROR_INVALID_PARAMETER);
2951 return 0;
2953 if (len1 < 0) len1 = strlen(str1);
2954 if (len2 < 0) len2 = strlen(str2);
2956 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2958 if (len1)
2960 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2961 if (len1W)
2962 str1W = buf1W;
2963 else
2965 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2966 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2967 if (!str1W)
2969 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2970 return 0;
2972 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2975 else
2977 len1W = 0;
2978 str1W = buf1W;
2981 if (len2)
2983 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2984 if (len2W)
2985 str2W = buf2W;
2986 else
2988 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2989 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2990 if (!str2W)
2992 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2993 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2994 return 0;
2996 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2999 else
3001 len2W = 0;
3002 str2W = buf2W;
3005 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3007 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3008 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3009 return ret;
3012 /*************************************************************************
3013 * lstrcmp (KERNEL32.@)
3014 * lstrcmpA (KERNEL32.@)
3016 * Compare two strings using the current thread locale.
3018 * PARAMS
3019 * str1 [I] First string to compare
3020 * str2 [I] Second string to compare
3022 * RETURNS
3023 * Success: A number less than, equal to or greater than 0 depending on whether
3024 * str1 is less than, equal to or greater than str2 respectively.
3025 * Failure: FALSE. Use GetLastError() to determine the cause.
3027 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3029 int ret;
3031 if ((str1 == NULL) && (str2 == NULL)) return 0;
3032 if (str1 == NULL) return -1;
3033 if (str2 == NULL) return 1;
3035 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3036 if (ret) ret -= 2;
3038 return ret;
3041 /*************************************************************************
3042 * lstrcmpi (KERNEL32.@)
3043 * lstrcmpiA (KERNEL32.@)
3045 * Compare two strings using the current thread locale, ignoring case.
3047 * PARAMS
3048 * str1 [I] First string to compare
3049 * str2 [I] Second string to compare
3051 * RETURNS
3052 * Success: A number less than, equal to or greater than 0 depending on whether
3053 * str2 is less than, equal to or greater than str1 respectively.
3054 * Failure: FALSE. Use GetLastError() to determine the cause.
3056 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3058 int ret;
3060 if ((str1 == NULL) && (str2 == NULL)) return 0;
3061 if (str1 == NULL) return -1;
3062 if (str2 == NULL) return 1;
3064 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3065 if (ret) ret -= 2;
3067 return ret;
3070 /*************************************************************************
3071 * lstrcmpW (KERNEL32.@)
3073 * See lstrcmpA.
3075 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3077 int ret;
3079 if ((str1 == NULL) && (str2 == NULL)) return 0;
3080 if (str1 == NULL) return -1;
3081 if (str2 == NULL) return 1;
3083 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3084 if (ret) ret -= 2;
3086 return ret;
3089 /*************************************************************************
3090 * lstrcmpiW (KERNEL32.@)
3092 * See lstrcmpiA.
3094 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3096 int ret;
3098 if ((str1 == NULL) && (str2 == NULL)) return 0;
3099 if (str1 == NULL) return -1;
3100 if (str2 == NULL) return 1;
3102 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3103 if (ret) ret -= 2;
3105 return ret;
3108 /******************************************************************************
3109 * LOCALE_Init
3111 void LOCALE_Init(void)
3113 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3114 const union cptable *unix_cp );
3116 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3118 #ifdef __APPLE__
3119 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3120 char user_locale[50];
3122 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3123 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3124 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3125 CFStringRef user_locale_string_ref;
3127 if (user_locale_country_ref)
3129 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3130 user_locale_lang_ref, user_locale_country_ref);
3132 else
3134 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3135 user_locale_lang_ref);
3138 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3140 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3141 setenv( "LANG", user_locale, 0 );
3142 TRACE( "setting locale to '%s'\n", user_locale );
3143 #endif /* __APPLE__ */
3145 setlocale( LC_ALL, "" );
3147 unix_cp = setup_unix_locales();
3148 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3150 #ifdef __APPLE__
3151 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3152 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3154 /* Retrieve the preferred language as chosen in System Preferences. */
3155 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3156 leave things be. */
3157 CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3158 CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3159 CFStringRef user_language_string_ref;
3160 if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3161 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3162 !CFEqual(user_language_string_ref, user_locale_lang_ref))
3164 struct locale_name locale_name;
3165 WCHAR buffer[128];
3166 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3167 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3168 parse_locale_name( buffer, &locale_name );
3169 lcid_LC_MESSAGES = locale_name.lcid;
3170 TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3172 CFRelease( all_locales );
3173 if (preferred_locales)
3174 CFRelease( preferred_locales );
3177 CFRelease( user_locale_ref );
3178 CFRelease( user_locale_string_ref );
3179 #endif
3181 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3182 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3183 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3185 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3186 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3187 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3188 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3189 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3190 if (!unix_cp)
3191 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3192 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3194 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3195 ansi_cptable = wine_cp_get_table( 1252 );
3196 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3197 oem_cptable = wine_cp_get_table( 437 );
3198 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3199 mac_cptable = wine_cp_get_table( 10000 );
3200 if (unix_cp != CP_UTF8)
3202 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3203 unix_cptable = wine_cp_get_table( 28591 );
3206 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3208 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3209 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3210 mac_cptable->info.codepage, unix_cp );
3212 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3215 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3217 UNICODE_STRING keyName;
3218 OBJECT_ATTRIBUTES attr;
3219 HANDLE hkey;
3221 RtlInitUnicodeString( &keyName, szKeyName );
3222 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3224 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3225 hkey = 0;
3227 return hkey;
3230 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3231 ULONG keyNameSize)
3233 BYTE buffer[80];
3234 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3235 DWORD dwLen;
3237 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3238 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3239 info->NameLength > keyNameSize)
3241 return FALSE;
3244 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3246 memcpy( szKeyName, info->Name, info->NameLength);
3247 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3249 TRACE("returning %s\n", debugstr_w(szKeyName));
3250 return TRUE;
3253 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3254 LPWSTR szValueName, ULONG valueNameSize,
3255 LPWSTR szValueData, ULONG valueDataSize)
3257 BYTE buffer[80];
3258 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3259 DWORD dwLen;
3261 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3262 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3263 info->NameLength > valueNameSize ||
3264 info->DataLength > valueDataSize)
3266 return FALSE;
3269 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3271 memcpy( szValueName, info->Name, info->NameLength);
3272 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3273 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3274 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3276 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3277 return TRUE;
3280 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3282 BYTE buffer[128];
3283 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3284 DWORD dwSize = sizeof(buffer);
3285 UNICODE_STRING valueName;
3287 RtlInitUnicodeString( &valueName, szValueName );
3289 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3290 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3291 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3292 info->DataLength == sizeof(DWORD))
3294 memcpy(lpVal, info->Data, sizeof(DWORD));
3295 return TRUE;
3298 return FALSE;
3301 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3303 LANGID langId;
3304 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3305 HRSRC hResource;
3306 BOOL bRet = FALSE;
3308 /* FIXME: Is it correct to use the system default langid? */
3309 langId = GetSystemDefaultLangID();
3311 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3312 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3314 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3316 if (hResource)
3318 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3320 if (hResDir)
3322 ULONG iResourceIndex = lgrpid & 0xf;
3323 LPCWSTR lpResEntry = LockResource( hResDir );
3324 ULONG i;
3326 for (i = 0; i < iResourceIndex; i++)
3327 lpResEntry += *lpResEntry + 1;
3329 if (*lpResEntry < nameSize)
3331 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3332 szName[*lpResEntry] = '\0';
3333 bRet = TRUE;
3337 FreeResource( hResource );
3339 return bRet;
3342 /* Registry keys for NLS related information */
3344 static const WCHAR szCountryListName[] = {
3345 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3346 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3347 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3348 'T','e','l','e','p','h','o','n','y','\\',
3349 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3353 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3354 typedef struct
3356 LANGUAGEGROUP_ENUMPROCA procA;
3357 LANGUAGEGROUP_ENUMPROCW procW;
3358 DWORD dwFlags;
3359 LONG_PTR lParam;
3360 } ENUMLANGUAGEGROUP_CALLBACKS;
3362 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3363 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3365 WCHAR szNumber[10], szValue[4];
3366 HANDLE hKey;
3367 BOOL bContinue = TRUE;
3368 ULONG ulIndex = 0;
3370 if (!lpProcs)
3372 SetLastError(ERROR_INVALID_PARAMETER);
3373 return FALSE;
3376 switch (lpProcs->dwFlags)
3378 case 0:
3379 /* Default to LGRPID_INSTALLED */
3380 lpProcs->dwFlags = LGRPID_INSTALLED;
3381 /* Fall through... */
3382 case LGRPID_INSTALLED:
3383 case LGRPID_SUPPORTED:
3384 break;
3385 default:
3386 SetLastError(ERROR_INVALID_FLAGS);
3387 return FALSE;
3390 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3392 if (!hKey)
3393 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3395 while (bContinue)
3397 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3398 szValue, sizeof(szValue) ))
3400 BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
3401 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3403 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3404 bInstalled ? "" : "not ");
3406 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3408 WCHAR szGrpName[48];
3410 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3411 szGrpName[0] = '\0';
3413 if (lpProcs->procW)
3414 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3415 lpProcs->lParam );
3416 else
3418 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3419 char szGrpNameA[48];
3421 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3422 * or whether the language names are ever localised. Assume CP_ACP.
3425 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3426 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3428 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3429 lpProcs->lParam );
3433 ulIndex++;
3435 else
3436 bContinue = FALSE;
3438 if (!bContinue)
3439 break;
3442 if (hKey)
3443 NtClose( hKey );
3445 return TRUE;
3448 /******************************************************************************
3449 * EnumSystemLanguageGroupsA (KERNEL32.@)
3451 * Call a users function for each language group available on the system.
3453 * PARAMS
3454 * pLangGrpEnumProc [I] Callback function to call for each language group
3455 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3456 * lParam [I] User parameter to pass to pLangGrpEnumProc
3458 * RETURNS
3459 * Success: TRUE.
3460 * Failure: FALSE. Use GetLastError() to determine the cause.
3462 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3463 DWORD dwFlags, LONG_PTR lParam)
3465 ENUMLANGUAGEGROUP_CALLBACKS procs;
3467 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3469 procs.procA = pLangGrpEnumProc;
3470 procs.procW = NULL;
3471 procs.dwFlags = dwFlags;
3472 procs.lParam = lParam;
3474 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3477 /******************************************************************************
3478 * EnumSystemLanguageGroupsW (KERNEL32.@)
3480 * See EnumSystemLanguageGroupsA.
3482 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3483 DWORD dwFlags, LONG_PTR lParam)
3485 ENUMLANGUAGEGROUP_CALLBACKS procs;
3487 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3489 procs.procA = NULL;
3490 procs.procW = pLangGrpEnumProc;
3491 procs.dwFlags = dwFlags;
3492 procs.lParam = lParam;
3494 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3497 /******************************************************************************
3498 * IsValidLanguageGroup (KERNEL32.@)
3500 * Determine if a language group is supported and/or installed.
3502 * PARAMS
3503 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3504 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3506 * RETURNS
3507 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3508 * FALSE otherwise.
3510 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3512 static const WCHAR szFormat[] = { '%','x','\0' };
3513 WCHAR szValueName[16], szValue[2];
3514 BOOL bSupported = FALSE, bInstalled = FALSE;
3515 HANDLE hKey;
3518 switch (dwFlags)
3520 case LGRPID_INSTALLED:
3521 case LGRPID_SUPPORTED:
3523 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3525 sprintfW( szValueName, szFormat, lgrpid );
3527 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3529 bSupported = TRUE;
3531 if (szValue[0] == '1')
3532 bInstalled = TRUE;
3535 if (hKey)
3536 NtClose( hKey );
3538 break;
3541 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3542 (dwFlags == LGRPID_INSTALLED && bInstalled))
3543 return TRUE;
3545 return FALSE;
3548 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3549 typedef struct
3551 LANGGROUPLOCALE_ENUMPROCA procA;
3552 LANGGROUPLOCALE_ENUMPROCW procW;
3553 DWORD dwFlags;
3554 LGRPID lgrpid;
3555 LONG_PTR lParam;
3556 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3558 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3559 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3561 static const WCHAR szAlternateSortsKeyName[] = {
3562 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3564 WCHAR szNumber[10], szValue[4];
3565 HANDLE hKey;
3566 BOOL bContinue = TRUE, bAlternate = FALSE;
3567 LGRPID lgrpid;
3568 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3570 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3572 SetLastError(ERROR_INVALID_PARAMETER);
3573 return FALSE;
3576 if (lpProcs->dwFlags)
3578 SetLastError(ERROR_INVALID_FLAGS);
3579 return FALSE;
3582 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3584 if (!hKey)
3585 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3587 while (bContinue)
3589 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3590 szValue, sizeof(szValue) ))
3592 lgrpid = strtoulW( szValue, NULL, 16 );
3594 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3595 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3597 if (lgrpid == lpProcs->lgrpid)
3599 LCID lcid;
3601 lcid = strtoulW( szNumber, NULL, 16 );
3603 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3604 * '00000437 ;Georgian'
3605 * At present we only pass the LCID string.
3608 if (lpProcs->procW)
3609 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3610 else
3612 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3614 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3616 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3620 ulIndex++;
3622 else
3624 /* Finished enumerating this key */
3625 if (!bAlternate)
3627 /* Enumerate alternate sorts also */
3628 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3629 bAlternate = TRUE;
3630 ulIndex = 0;
3632 else
3633 bContinue = FALSE; /* Finished both keys */
3636 if (!bContinue)
3637 break;
3640 if (hKey)
3641 NtClose( hKey );
3643 return TRUE;
3646 /******************************************************************************
3647 * EnumLanguageGroupLocalesA (KERNEL32.@)
3649 * Call a users function for every locale in a language group available on the system.
3651 * PARAMS
3652 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3653 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3654 * dwFlags [I] Reserved, set to 0
3655 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3657 * RETURNS
3658 * Success: TRUE.
3659 * Failure: FALSE. Use GetLastError() to determine the cause.
3661 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3662 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3664 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3666 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3668 callbacks.procA = pLangGrpLcEnumProc;
3669 callbacks.procW = NULL;
3670 callbacks.dwFlags = dwFlags;
3671 callbacks.lgrpid = lgrpid;
3672 callbacks.lParam = lParam;
3674 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3677 /******************************************************************************
3678 * EnumLanguageGroupLocalesW (KERNEL32.@)
3680 * See EnumLanguageGroupLocalesA.
3682 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3683 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3685 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3687 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3689 callbacks.procA = NULL;
3690 callbacks.procW = pLangGrpLcEnumProc;
3691 callbacks.dwFlags = dwFlags;
3692 callbacks.lgrpid = lgrpid;
3693 callbacks.lParam = lParam;
3695 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3698 /******************************************************************************
3699 * EnumSystemGeoID (KERNEL32.@)
3701 * Call a users function for every location available on the system.
3703 * PARAMS
3704 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3705 * reserved [I] Reserved, set to 0
3706 * pGeoEnumProc [I] Callback function to call for each location
3708 * RETURNS
3709 * Success: TRUE.
3710 * Failure: FALSE. Use GetLastError() to determine the cause.
3712 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3714 static const WCHAR szCountryCodeValueName[] = {
3715 'C','o','u','n','t','r','y','C','o','d','e','\0'
3717 WCHAR szNumber[10];
3718 HANDLE hKey;
3719 ULONG ulIndex = 0;
3721 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3723 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3725 SetLastError(ERROR_INVALID_PARAMETER);
3726 return FALSE;
3729 hKey = NLS_RegOpenKey( 0, szCountryListName );
3731 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3733 BOOL bContinue = TRUE;
3734 DWORD dwGeoId;
3735 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3737 if (hSubKey)
3739 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3741 TRACE("Got geoid %d\n", dwGeoId);
3743 if (!pGeoEnumProc( dwGeoId ))
3744 bContinue = FALSE;
3747 NtClose( hSubKey );
3750 if (!bContinue)
3751 break;
3753 ulIndex++;
3756 if (hKey)
3757 NtClose( hKey );
3759 return TRUE;
3762 /******************************************************************************
3763 * InvalidateNLSCache (KERNEL32.@)
3765 * Invalidate the cache of NLS values.
3767 * PARAMS
3768 * None.
3770 * RETURNS
3771 * Success: TRUE.
3772 * Failure: FALSE.
3774 BOOL WINAPI InvalidateNLSCache(void)
3776 FIXME("() stub\n");
3777 return FALSE;
3780 /******************************************************************************
3781 * GetUserGeoID (KERNEL32.@)
3783 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3785 GEOID ret = GEOID_NOT_AVAILABLE;
3786 static const WCHAR geoW[] = {'G','e','o',0};
3787 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3788 WCHAR bufferW[40], *end;
3789 DWORD count;
3790 HANDLE hkey, hSubkey = 0;
3791 UNICODE_STRING keyW;
3792 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3793 RtlInitUnicodeString( &keyW, nationW );
3794 count = sizeof(bufferW);
3796 if(!(hkey = create_registry_key())) return ret;
3798 switch( GeoClass ){
3799 case GEOCLASS_NATION:
3800 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3802 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3803 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3804 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3806 break;
3807 case GEOCLASS_REGION:
3808 FIXME("GEOCLASS_REGION not handled yet\n");
3809 break;
3812 NtClose(hkey);
3813 if (hSubkey) NtClose(hSubkey);
3814 return ret;
3817 /******************************************************************************
3818 * SetUserGeoID (KERNEL32.@)
3820 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3822 static const WCHAR geoW[] = {'G','e','o',0};
3823 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3824 static const WCHAR formatW[] = {'%','i',0};
3825 UNICODE_STRING nameW,keyW;
3826 WCHAR bufferW[10];
3827 OBJECT_ATTRIBUTES attr;
3828 HANDLE hkey;
3830 if(!(hkey = create_registry_key())) return FALSE;
3832 attr.Length = sizeof(attr);
3833 attr.RootDirectory = hkey;
3834 attr.ObjectName = &nameW;
3835 attr.Attributes = 0;
3836 attr.SecurityDescriptor = NULL;
3837 attr.SecurityQualityOfService = NULL;
3838 RtlInitUnicodeString( &nameW, geoW );
3839 RtlInitUnicodeString( &keyW, nationW );
3841 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3844 NtClose(attr.RootDirectory);
3845 return FALSE;
3848 sprintfW(bufferW, formatW, GeoID);
3849 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3850 NtClose(attr.RootDirectory);
3851 NtClose(hkey);
3852 return TRUE;
3855 typedef struct
3857 union
3859 UILANGUAGE_ENUMPROCA procA;
3860 UILANGUAGE_ENUMPROCW procW;
3861 } u;
3862 DWORD flags;
3863 LONG_PTR param;
3864 } ENUM_UILANG_CALLBACK;
3866 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3867 LPCSTR name, WORD LangID, LONG_PTR lParam )
3869 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3870 char buf[20];
3872 sprintf(buf, "%08x", (UINT)LangID);
3873 return enum_uilang->u.procA( buf, enum_uilang->param );
3876 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3877 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3879 static const WCHAR formatW[] = {'%','0','8','x',0};
3880 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3881 WCHAR buf[20];
3883 sprintfW( buf, formatW, (UINT)LangID );
3884 return enum_uilang->u.procW( buf, enum_uilang->param );
3887 /******************************************************************************
3888 * EnumUILanguagesA (KERNEL32.@)
3890 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3892 ENUM_UILANG_CALLBACK enum_uilang;
3894 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3896 if(!pUILangEnumProc) {
3897 SetLastError(ERROR_INVALID_PARAMETER);
3898 return FALSE;
3900 if(dwFlags) {
3901 SetLastError(ERROR_INVALID_FLAGS);
3902 return FALSE;
3905 enum_uilang.u.procA = pUILangEnumProc;
3906 enum_uilang.flags = dwFlags;
3907 enum_uilang.param = lParam;
3909 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3910 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3911 (LONG_PTR)&enum_uilang);
3912 return TRUE;
3915 /******************************************************************************
3916 * EnumUILanguagesW (KERNEL32.@)
3918 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3920 ENUM_UILANG_CALLBACK enum_uilang;
3922 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3925 if(!pUILangEnumProc) {
3926 SetLastError(ERROR_INVALID_PARAMETER);
3927 return FALSE;
3929 if(dwFlags) {
3930 SetLastError(ERROR_INVALID_FLAGS);
3931 return FALSE;
3934 enum_uilang.u.procW = pUILangEnumProc;
3935 enum_uilang.flags = dwFlags;
3936 enum_uilang.param = lParam;
3938 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3939 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3940 (LONG_PTR)&enum_uilang);
3941 return TRUE;
3944 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
3945 int cchData, LANGID language)
3947 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3948 return 0;
3951 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
3952 int cchData, LANGID language)
3954 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3955 return 0;
3958 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3960 LCID userlcid;
3962 TRACE("%p, %d\n", localename, buffersize);
3964 userlcid = GetUserDefaultLCID();
3965 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3968 /******************************************************************************
3969 * NormalizeString (KERNEL32.@)
3971 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3972 LPWSTR lpDstString, INT cwDstLength)
3974 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3975 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3976 return 0;
3979 /******************************************************************************
3980 * IsNormalizedString (KERNEL32.@)
3982 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
3984 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
3985 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3986 return FALSE;
3989 enum {
3990 BASE = 36,
3991 TMIN = 1,
3992 TMAX = 26,
3993 SKEW = 38,
3994 DAMP = 700,
3995 INIT_BIAS = 72,
3996 INIT_N = 128
3999 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4001 INT k;
4003 delta /= (firsttime ? DAMP : 2);
4004 delta += delta/numpoints;
4006 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4007 delta /= BASE-TMIN;
4008 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4011 /******************************************************************************
4012 * IdnToAscii (KERNEL32.@)
4013 * Implementation of Punycode based on RFC 3492.
4015 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4016 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4018 static const WCHAR prefixW[] = {'x','n','-','-'};
4020 WCHAR *norm_str;
4021 INT i, label_start, label_end, norm_len, out_label, out = 0;
4023 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4024 lpASCIICharStr, cchASCIIChar);
4026 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4027 if(!norm_len)
4028 return 0;
4029 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4030 if(!norm_str) {
4031 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4032 return 0;
4034 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4035 cchUnicodeChar, norm_str, norm_len);
4036 if(!norm_len) {
4037 HeapFree(GetProcessHeap(), 0, norm_str);
4038 return 0;
4041 for(label_start=0; label_start<norm_len;) {
4042 INT n = INIT_N, bias = INIT_BIAS;
4043 INT delta = 0, b = 0, h;
4045 out_label = out;
4046 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4047 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4048 if(norm_str[i] < 0x80)
4049 b++;
4050 label_end = i;
4052 if(b == label_end-label_start) {
4053 if(label_end < norm_len)
4054 b++;
4055 if(!lpASCIICharStr) {
4056 out += b;
4057 }else if(out+b <= cchASCIIChar) {
4058 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4059 out += b;
4060 }else {
4061 HeapFree(GetProcessHeap(), 0, norm_str);
4062 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4063 return 0;
4065 label_start = label_end+1;
4066 continue;
4069 if(!lpASCIICharStr) {
4070 out += 5+b; /* strlen(xn--...-) */
4071 }else if(out+5+b <= cchASCIIChar) {
4072 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4073 out += 4;
4074 for(i=label_start; i<label_end; i++)
4075 if(norm_str[i] < 0x80)
4076 lpASCIICharStr[out++] = norm_str[i];
4077 lpASCIICharStr[out++] = '-';
4078 }else {
4079 HeapFree(GetProcessHeap(), 0, norm_str);
4080 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4081 return 0;
4083 if(!b)
4084 out--;
4086 for(h=b; h<label_end-label_start;) {
4087 INT m = 0xffff, q, k;
4089 for(i=label_start; i<label_end; i++) {
4090 if(norm_str[i]>=n && m>norm_str[i])
4091 m = norm_str[i];
4093 delta += (m-n)*(h+1);
4094 n = m;
4096 for(i=label_start; i<label_end; i++) {
4097 if(norm_str[i] < n) {
4098 delta++;
4099 }else if(norm_str[i] == n) {
4100 for(q=delta, k=BASE; ; k+=BASE) {
4101 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4102 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4103 if(!lpASCIICharStr) {
4104 out++;
4105 }else if(out+1 <= cchASCIIChar) {
4106 lpASCIICharStr[out++] = disp<='z'-'a' ?
4107 'a'+disp : '0'+disp-'z'+'a'-1;
4108 }else {
4109 HeapFree(GetProcessHeap(), 0, norm_str);
4110 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4111 return 0;
4113 if(q < t)
4114 break;
4115 q = (q-t)/(BASE-t);
4117 bias = adapt(delta, h+1, h==b);
4118 delta = 0;
4119 h++;
4122 delta++;
4123 n++;
4126 if(out-out_label > 63) {
4127 HeapFree(GetProcessHeap(), 0, norm_str);
4128 SetLastError(ERROR_INVALID_NAME);
4129 return 0;
4132 if(label_end < norm_len) {
4133 if(!lpASCIICharStr) {
4134 out++;
4135 }else if(out+1 <= cchASCIIChar) {
4136 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4137 }else {
4138 HeapFree(GetProcessHeap(), 0, norm_str);
4139 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4140 return 0;
4143 label_start = label_end+1;
4146 HeapFree(GetProcessHeap(), 0, norm_str);
4147 return out;
4150 /******************************************************************************
4151 * IdnToNameprepUnicode (KERNEL32.@)
4153 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4154 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4156 enum {
4157 UNASSIGNED = 0x1,
4158 PROHIBITED = 0x2,
4159 BIDI_RAL = 0x4,
4160 BIDI_L = 0x8
4163 extern const unsigned short nameprep_char_type[];
4164 extern const WCHAR nameprep_mapping[];
4165 const WCHAR *ptr;
4166 WORD flags;
4167 WCHAR buf[64], *map_str, norm_str[64], ch;
4168 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4169 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4171 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4172 lpNameprepCharStr, cchNameprepChar);
4174 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4175 SetLastError(ERROR_INVALID_FLAGS);
4176 return 0;
4179 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4180 SetLastError(ERROR_INVALID_PARAMETER);
4181 return 0;
4184 if(cchUnicodeChar == -1)
4185 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4186 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4187 SetLastError(ERROR_INVALID_NAME);
4188 return 0;
4191 for(label_start=0; label_start<cchUnicodeChar;) {
4192 ascii_only = TRUE;
4193 for(i=label_start; i<cchUnicodeChar; i++) {
4194 ch = lpUnicodeCharStr[i];
4196 if(i!=cchUnicodeChar-1 && !ch) {
4197 SetLastError(ERROR_INVALID_NAME);
4198 return 0;
4200 /* check if ch is one of label separators defined in RFC3490 */
4201 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4202 break;
4204 if(ch > 0x7f) {
4205 ascii_only = FALSE;
4206 continue;
4209 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4210 continue;
4211 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4212 || (ch>='0' && ch<='9') || ch=='-')
4213 continue;
4215 SetLastError(ERROR_INVALID_NAME);
4216 return 0;
4218 label_end = i;
4219 /* last label may be empty */
4220 if(label_start==label_end && ch) {
4221 SetLastError(ERROR_INVALID_NAME);
4222 return 0;
4225 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4226 lpUnicodeCharStr[label_end-1]=='-')) {
4227 SetLastError(ERROR_INVALID_NAME);
4228 return 0;
4231 if(ascii_only) {
4232 /* maximal label length is 63 characters */
4233 if(label_end-label_start > 63) {
4234 SetLastError(ERROR_INVALID_NAME);
4235 return 0;
4237 if(label_end < cchUnicodeChar)
4238 label_end++;
4240 if(!lpNameprepCharStr) {
4241 out += label_end-label_start;
4242 }else if(out+label_end-label_start <= cchNameprepChar) {
4243 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4244 (label_end-label_start)*sizeof(WCHAR));
4245 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4246 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4247 out += label_end-label_start;
4248 }else {
4249 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4250 return 0;
4253 label_start = label_end;
4254 continue;
4257 map_len = 0;
4258 for(i=label_start; i<label_end; i++) {
4259 ch = lpUnicodeCharStr[i];
4260 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4261 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4263 if(!ptr[0]) map_len++;
4264 else if(!ptr[1]) map_len++;
4265 else if(!ptr[2]) map_len += 2;
4266 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4268 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4269 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4270 if(!map_str) {
4271 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4272 return 0;
4274 }else {
4275 map_str = buf;
4277 map_len = 0;
4278 for(i=label_start; i<label_end; i++) {
4279 ch = lpUnicodeCharStr[i];
4280 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4281 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4283 if(!ptr[0]) {
4284 map_str[map_len++] = ch;
4285 }else if(!ptr[1]) {
4286 map_str[map_len++] = ptr[0];
4287 }else if(!ptr[2]) {
4288 map_str[map_len++] = ptr[0];
4289 map_str[map_len++] = ptr[1];
4290 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4291 map_str[map_len++] = ptr[0];
4292 map_str[map_len++] = ptr[1];
4293 map_str[map_len++] = ptr[2];
4297 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4298 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4299 if(map_str != buf)
4300 HeapFree(GetProcessHeap(), 0, map_str);
4301 if(!norm_len) {
4302 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4303 SetLastError(ERROR_INVALID_NAME);
4304 return 0;
4307 if(label_end < cchUnicodeChar) {
4308 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4309 label_end++;
4312 if(!lpNameprepCharStr) {
4313 out += norm_len;
4314 }else if(out+norm_len <= cchNameprepChar) {
4315 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4316 out += norm_len;
4317 }else {
4318 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4319 return 0;
4322 have_bidi_ral = prohibit_bidi_ral = FALSE;
4323 mask = PROHIBITED;
4324 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4325 mask |= UNASSIGNED;
4326 for(i=0; i<norm_len; i++) {
4327 ch = norm_str[i];
4328 flags = get_table_entry( nameprep_char_type, ch );
4330 if(flags & mask) {
4331 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4332 : ERROR_NO_UNICODE_TRANSLATION);
4333 return 0;
4336 if(flags & BIDI_RAL)
4337 have_bidi_ral = TRUE;
4338 if(flags & BIDI_L)
4339 prohibit_bidi_ral = TRUE;
4342 if(have_bidi_ral) {
4343 ch = norm_str[0];
4344 flags = get_table_entry( nameprep_char_type, ch );
4345 if((flags & BIDI_RAL) == 0)
4346 prohibit_bidi_ral = TRUE;
4348 ch = norm_str[norm_len-1];
4349 flags = get_table_entry( nameprep_char_type, ch );
4350 if((flags & BIDI_RAL) == 0)
4351 prohibit_bidi_ral = TRUE;
4354 if(have_bidi_ral && prohibit_bidi_ral) {
4355 SetLastError(ERROR_INVALID_NAME);
4356 return 0;
4359 label_start = label_end;
4362 return out;
4365 /******************************************************************************
4366 * IdnToUnicode (KERNEL32.@)
4368 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4369 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4371 extern const unsigned short nameprep_char_type[];
4373 INT i, label_start, label_end, out_label, out = 0;
4374 WCHAR ch;
4376 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4377 lpUnicodeCharStr, cchUnicodeChar);
4379 for(label_start=0; label_start<cchASCIIChar;) {
4380 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4382 out_label = out;
4383 for(i=label_start; i<cchASCIIChar; i++) {
4384 ch = lpASCIICharStr[i];
4386 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4387 SetLastError(ERROR_INVALID_NAME);
4388 return 0;
4391 if(!ch || ch=='.')
4392 break;
4393 if(ch == '-')
4394 delim = i;
4396 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4397 continue;
4398 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4399 || (ch>='0' && ch<='9') || ch=='-')
4400 continue;
4402 SetLastError(ERROR_INVALID_NAME);
4403 return 0;
4405 label_end = i;
4406 /* last label may be empty */
4407 if(label_start==label_end && ch) {
4408 SetLastError(ERROR_INVALID_NAME);
4409 return 0;
4412 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4413 lpUnicodeCharStr[label_end-1]=='-')) {
4414 SetLastError(ERROR_INVALID_NAME);
4415 return 0;
4417 if(label_end-label_start > 63) {
4418 SetLastError(ERROR_INVALID_NAME);
4419 return 0;
4422 if(label_end-label_start<4 ||
4423 tolowerW(lpASCIICharStr[label_start])!='x' ||
4424 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4425 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4426 if(label_end < cchUnicodeChar)
4427 label_end++;
4429 if(!lpUnicodeCharStr) {
4430 out += label_end-label_start;
4431 }else if(out+label_end-label_start <= cchUnicodeChar) {
4432 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4433 (label_end-label_start)*sizeof(WCHAR));
4434 out += label_end-label_start;
4435 }else {
4436 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4437 return 0;
4440 label_start = label_end;
4441 continue;
4444 if(delim == label_start+3)
4445 delim++;
4446 if(!lpUnicodeCharStr) {
4447 out += delim-label_start-4;
4448 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4449 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4450 (delim-label_start-4)*sizeof(WCHAR));
4451 out += delim-label_start-4;
4452 }else {
4453 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4454 return 0;
4456 if(out != out_label)
4457 delim++;
4459 for(i=delim; i<label_end;) {
4460 old_pos = pos;
4461 w = 1;
4462 for(k=BASE; ; k+=BASE) {
4463 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4464 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4465 SetLastError(ERROR_INVALID_NAME);
4466 return 0;
4468 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4469 pos += digit*w;
4470 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4471 if(digit < t)
4472 break;
4473 w *= BASE-t;
4475 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4476 n += pos/(out-out_label+1);
4477 pos %= out-out_label+1;
4479 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4480 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4481 SetLastError(ERROR_INVALID_NAME);
4482 return 0;
4484 if(!lpUnicodeCharStr) {
4485 out++;
4486 }else if(out+1 <= cchASCIIChar) {
4487 memmove(lpUnicodeCharStr+out_label+pos+1,
4488 lpUnicodeCharStr+out_label+pos,
4489 (out-out_label-pos)*sizeof(WCHAR));
4490 lpUnicodeCharStr[out_label+pos] = n;
4491 out++;
4492 }else {
4493 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4494 return 0;
4496 pos++;
4499 if(out-out_label > 63) {
4500 SetLastError(ERROR_INVALID_NAME);
4501 return 0;
4504 if(label_end < cchASCIIChar) {
4505 if(!lpUnicodeCharStr) {
4506 out++;
4507 }else if(out+1 <= cchUnicodeChar) {
4508 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4509 }else {
4510 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4511 return 0;
4514 label_start = label_end+1;
4517 return out;