winemac: Implement GetDeviceCaps().
[wine/testsucceed.git] / dlls / kernel32 / locale.c
blob57daab1ce1878d79bf6d2c38ae04d39e26945609
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)
1002 SetLastError(ERROR_INVALID_PARAMETER);
1003 return 0;
1006 if (locale_name.matches == 1)
1007 WARN( "locale %s not recognized, defaulting to %s\n",
1008 debugstr_w(name), debugstr_w(locale_name.lang) );
1010 return locale_name.lcid;
1014 /***********************************************************************
1015 * LCIDToLocaleName (KERNEL32.@)
1017 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1019 if (flags) FIXME( "unsupported flags %x\n", flags );
1021 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1025 /******************************************************************************
1026 * get_locale_value_name
1028 * Gets the registry value name for a given lctype.
1030 static const WCHAR *get_locale_value_name( DWORD lctype )
1032 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1033 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1034 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1035 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1036 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1037 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1038 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1039 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1040 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1041 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1042 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1043 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1044 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1045 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1046 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1047 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1048 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1049 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1050 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1051 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1052 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1053 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1054 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1055 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1056 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1057 static const WCHAR sListW[] = {'s','L','i','s','t',0};
1058 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1059 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1060 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1061 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1062 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1063 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1064 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1065 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1066 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1067 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1068 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1069 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1070 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1072 switch (lctype)
1074 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1075 * the values are stored in the registry, confirmed under Windows.
1077 case LOCALE_ICALENDARTYPE: return iCalendarTypeW;
1078 case LOCALE_ICURRDIGITS: return iCurrDigitsW;
1079 case LOCALE_ICURRENCY: return iCurrencyW;
1080 case LOCALE_IDIGITS: return iDigitsW;
1081 case LOCALE_IFIRSTDAYOFWEEK: return iFirstDayOfWeekW;
1082 case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1083 case LOCALE_ILZERO: return iLZeroW;
1084 case LOCALE_IMEASURE: return iMeasureW;
1085 case LOCALE_INEGCURR: return iNegCurrW;
1086 case LOCALE_INEGNUMBER: return iNegNumberW;
1087 case LOCALE_IPAPERSIZE: return iPaperSizeW;
1088 case LOCALE_ITIME: return iTimeW;
1089 case LOCALE_S1159: return s1159W;
1090 case LOCALE_S2359: return s2359W;
1091 case LOCALE_SCURRENCY: return sCurrencyW;
1092 case LOCALE_SDATE: return sDateW;
1093 case LOCALE_SDECIMAL: return sDecimalW;
1094 case LOCALE_SGROUPING: return sGroupingW;
1095 case LOCALE_SLIST: return sListW;
1096 case LOCALE_SLONGDATE: return sLongDateW;
1097 case LOCALE_SMONDECIMALSEP: return sMonDecimalSepW;
1098 case LOCALE_SMONGROUPING: return sMonGroupingW;
1099 case LOCALE_SMONTHOUSANDSEP: return sMonThousandSepW;
1100 case LOCALE_SNEGATIVESIGN: return sNegativeSignW;
1101 case LOCALE_SPOSITIVESIGN: return sPositiveSignW;
1102 case LOCALE_SSHORTDATE: return sShortDateW;
1103 case LOCALE_STHOUSAND: return sThousandW;
1104 case LOCALE_STIME: return sTimeW;
1105 case LOCALE_STIMEFORMAT: return sTimeFormatW;
1106 case LOCALE_SYEARMONTH: return sYearMonthW;
1108 /* The following are not listed under MSDN as supported,
1109 * but seem to be used and also stored in the registry.
1111 case LOCALE_ICOUNTRY: return iCountryW;
1112 case LOCALE_IDATE: return iDateW;
1113 case LOCALE_ILDATE: return iLDateW;
1114 case LOCALE_ITLZERO: return iTLZeroW;
1115 case LOCALE_SCOUNTRY: return sCountryW;
1116 case LOCALE_SABBREVLANGNAME: return sLanguageW;
1118 /* The following are used in XP and later */
1119 case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1120 case LOCALE_SNATIVEDIGITS: return sNativeDigitsW;
1121 case LOCALE_ITIMEMARKPOSN: return iTimePrefixW;
1123 return NULL;
1127 /******************************************************************************
1128 * get_registry_locale_info
1130 * Retrieve user-modified locale info from the registry.
1131 * Return length, 0 on error, -1 if not found.
1133 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1135 DWORD size;
1136 INT ret;
1137 HANDLE hkey;
1138 NTSTATUS status;
1139 UNICODE_STRING nameW;
1140 KEY_VALUE_PARTIAL_INFORMATION *info;
1141 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1143 if (!(hkey = create_registry_key())) return -1;
1145 RtlInitUnicodeString( &nameW, value );
1146 size = info_size + len * sizeof(WCHAR);
1148 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1150 NtClose( hkey );
1151 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1152 return 0;
1155 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1157 if (!status)
1159 ret = (size - info_size) / sizeof(WCHAR);
1160 /* append terminating null if needed */
1161 if (!ret || ((WCHAR *)info->Data)[ret-1])
1163 if (ret < len || !buffer) ret++;
1164 else
1166 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1167 ret = 0;
1170 if (ret && buffer)
1172 memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1173 buffer[ret-1] = 0;
1176 else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1178 ret = (size - info_size) / sizeof(WCHAR) + 1;
1180 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1182 ret = -1;
1184 else
1186 SetLastError( RtlNtStatusToDosError(status) );
1187 ret = 0;
1189 NtClose( hkey );
1190 HeapFree( GetProcessHeap(), 0, info );
1191 return ret;
1195 /******************************************************************************
1196 * GetLocaleInfoA (KERNEL32.@)
1198 * Get information about an aspect of a locale.
1200 * PARAMS
1201 * lcid [I] LCID of the locale
1202 * lctype [I] LCTYPE_ flags from "winnls.h"
1203 * buffer [O] Destination for the information
1204 * len [I] Length of buffer in characters
1206 * RETURNS
1207 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1208 * with the information.
1209 * Failure: 0. Use GetLastError() to determine the cause.
1211 * NOTES
1212 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1213 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1214 * which is a bit string.
1216 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1218 WCHAR *bufferW;
1219 INT lenW, ret;
1221 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1223 if (len < 0 || (len && !buffer))
1225 SetLastError( ERROR_INVALID_PARAMETER );
1226 return 0;
1228 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1230 SetLastError( ERROR_INVALID_FLAGS );
1231 return 0;
1234 if (!len) buffer = NULL;
1236 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1238 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1240 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1241 return 0;
1243 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1245 if ((lctype & LOCALE_RETURN_NUMBER) ||
1246 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1248 /* it's not an ASCII string, just bytes */
1249 ret *= sizeof(WCHAR);
1250 if (buffer)
1252 if (ret <= len) memcpy( buffer, bufferW, ret );
1253 else
1255 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1256 ret = 0;
1260 else
1262 UINT codepage = CP_ACP;
1263 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1264 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1267 HeapFree( GetProcessHeap(), 0, bufferW );
1268 return ret;
1271 static int get_value_base_by_lctype( LCTYPE lctype )
1273 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1276 /******************************************************************************
1277 * GetLocaleInfoW (KERNEL32.@)
1279 * See GetLocaleInfoA.
1281 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1283 LANGID lang_id;
1284 HRSRC hrsrc;
1285 HGLOBAL hmem;
1286 INT ret;
1287 UINT lcflags;
1288 const WCHAR *p;
1289 unsigned int i;
1291 if (len < 0 || (len && !buffer))
1293 SetLastError( ERROR_INVALID_PARAMETER );
1294 return 0;
1296 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1297 !is_genitive_name_supported( lctype ))
1299 SetLastError( ERROR_INVALID_FLAGS );
1300 return 0;
1303 if (!len) buffer = NULL;
1305 lcid = convert_default_lcid( lcid, lctype );
1307 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1308 lctype &= 0xffff;
1310 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1312 /* first check for overrides in the registry */
1314 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1315 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1317 const WCHAR *value = get_locale_value_name(lctype);
1319 if (value)
1321 if (lcflags & LOCALE_RETURN_NUMBER)
1323 WCHAR tmp[16];
1324 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1325 if (ret > 0)
1327 WCHAR *end;
1328 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1329 if (*end) /* invalid number */
1331 SetLastError( ERROR_INVALID_FLAGS );
1332 return 0;
1334 ret = sizeof(UINT)/sizeof(WCHAR);
1335 if (!buffer) return ret;
1336 if (ret > len)
1338 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1339 return 0;
1341 memcpy( buffer, &number, sizeof(number) );
1344 else ret = get_registry_locale_info( value, buffer, len );
1346 if (ret != -1) return ret;
1350 /* now load it from kernel resources */
1352 lang_id = LANGIDFROMLCID( lcid );
1354 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1355 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1356 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1358 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1359 ULongToPtr((lctype >> 4) + 1), lang_id )))
1361 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1362 return 0;
1364 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1365 return 0;
1367 p = LockResource( hmem );
1368 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1370 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1371 else if (is_genitive_name_supported( lctype ) && *p)
1373 /* genitive form's stored after a null separator from a nominative */
1374 for (i = 1; i <= *p; i++) if (!p[i]) break;
1376 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1378 ret = *p - i + 1;
1379 p += i;
1381 else ret = i;
1383 else
1384 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1386 if (!buffer) return ret;
1388 if (ret > len)
1390 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1391 return 0;
1394 if (lcflags & LOCALE_RETURN_NUMBER)
1396 UINT number;
1397 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1398 if (!tmp) return 0;
1399 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1400 tmp[*p] = 0;
1401 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1402 if (!*end)
1403 memcpy( buffer, &number, sizeof(number) );
1404 else /* invalid number */
1406 SetLastError( ERROR_INVALID_FLAGS );
1407 ret = 0;
1409 HeapFree( GetProcessHeap(), 0, tmp );
1411 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1412 lcid, lctype, buffer, len, number );
1414 else
1416 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1417 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1419 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1420 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1422 return ret;
1425 /******************************************************************************
1426 * GetLocaleInfoEx (KERNEL32.@)
1428 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1430 LCID lcid = LocaleNameToLCID(locale, 0);
1432 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1434 if (!lcid) return 0;
1436 /* special handling for neutral locale names */
1437 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1439 if (len && len < 3)
1441 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1442 return 0;
1445 if (len) strcpyW(buffer, locale);
1446 return 3;
1449 return GetLocaleInfoW(lcid, info, buffer, len);
1452 /******************************************************************************
1453 * SetLocaleInfoA [KERNEL32.@]
1455 * Set information about an aspect of a locale.
1457 * PARAMS
1458 * lcid [I] LCID of the locale
1459 * lctype [I] LCTYPE_ flags from "winnls.h"
1460 * data [I] Information to set
1462 * RETURNS
1463 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1464 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1465 * Failure: FALSE. Use GetLastError() to determine the cause.
1467 * NOTES
1468 * - Values are only be set for the current user locale; the system locale
1469 * settings cannot be changed.
1470 * - Any settings changed by this call are lost when the locale is changed by
1471 * the control panel (in Wine, this happens every time you change LANG).
1472 * - The native implementation of this function does not check that lcid matches
1473 * the current user locale, and simply sets the new values. Wine warns you in
1474 * this case, but behaves the same.
1476 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1478 UINT codepage = CP_ACP;
1479 WCHAR *strW;
1480 DWORD len;
1481 BOOL ret;
1483 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1485 if (!data)
1487 SetLastError( ERROR_INVALID_PARAMETER );
1488 return FALSE;
1490 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1491 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1493 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1494 return FALSE;
1496 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1497 ret = SetLocaleInfoW( lcid, lctype, strW );
1498 HeapFree( GetProcessHeap(), 0, strW );
1499 return ret;
1503 /******************************************************************************
1504 * SetLocaleInfoW (KERNEL32.@)
1506 * See SetLocaleInfoA.
1508 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1510 const WCHAR *value;
1511 static const WCHAR intlW[] = {'i','n','t','l',0 };
1512 UNICODE_STRING valueW;
1513 NTSTATUS status;
1514 HANDLE hkey;
1516 lctype &= 0xffff;
1517 value = get_locale_value_name( lctype );
1519 if (!data || !value)
1521 SetLastError( ERROR_INVALID_PARAMETER );
1522 return FALSE;
1525 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1527 SetLastError( ERROR_INVALID_FLAGS );
1528 return FALSE;
1531 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1533 /* FIXME: should check that data to set is sane */
1535 /* FIXME: profile functions should map to registry */
1536 WriteProfileStringW( intlW, value, data );
1538 if (!(hkey = create_registry_key())) return FALSE;
1539 RtlInitUnicodeString( &valueW, value );
1540 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1542 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1544 /* Set I-value from S value */
1545 WCHAR *lpD, *lpM, *lpY;
1546 WCHAR szBuff[2];
1548 lpD = strrchrW(data, 'd');
1549 lpM = strrchrW(data, 'M');
1550 lpY = strrchrW(data, 'y');
1552 if (lpD <= lpM)
1554 szBuff[0] = '1'; /* D-M-Y */
1556 else
1558 if (lpY <= lpM)
1559 szBuff[0] = '2'; /* Y-M-D */
1560 else
1561 szBuff[0] = '0'; /* M-D-Y */
1564 szBuff[1] = '\0';
1566 if (lctype == LOCALE_SSHORTDATE)
1567 lctype = LOCALE_IDATE;
1568 else
1569 lctype = LOCALE_ILDATE;
1571 value = get_locale_value_name( lctype );
1573 WriteProfileStringW( intlW, value, szBuff );
1575 RtlInitUnicodeString( &valueW, value );
1576 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1579 NtClose( hkey );
1581 if (status) SetLastError( RtlNtStatusToDosError(status) );
1582 return !status;
1586 /******************************************************************************
1587 * GetACP (KERNEL32.@)
1589 * Get the current Ansi code page Id for the system.
1591 * PARAMS
1592 * None.
1594 * RETURNS
1595 * The current Ansi code page identifier for the system.
1597 UINT WINAPI GetACP(void)
1599 assert( ansi_cptable );
1600 return ansi_cptable->info.codepage;
1604 /******************************************************************************
1605 * SetCPGlobal (KERNEL32.@)
1607 * Set the current Ansi code page Id for the system.
1609 * PARAMS
1610 * acp [I] code page ID to be the new ACP.
1612 * RETURNS
1613 * The previous ACP.
1615 UINT WINAPI SetCPGlobal( UINT acp )
1617 UINT ret = GetACP();
1618 const union cptable *new_cptable = wine_cp_get_table( acp );
1620 if (new_cptable) ansi_cptable = new_cptable;
1621 return ret;
1625 /***********************************************************************
1626 * GetOEMCP (KERNEL32.@)
1628 * Get the current OEM code page Id for the system.
1630 * PARAMS
1631 * None.
1633 * RETURNS
1634 * The current OEM code page identifier for the system.
1636 UINT WINAPI GetOEMCP(void)
1638 assert( oem_cptable );
1639 return oem_cptable->info.codepage;
1643 /***********************************************************************
1644 * IsValidCodePage (KERNEL32.@)
1646 * Determine if a given code page identifier is valid.
1648 * PARAMS
1649 * codepage [I] Code page Id to verify.
1651 * RETURNS
1652 * TRUE, If codepage is valid and available on the system,
1653 * FALSE otherwise.
1655 BOOL WINAPI IsValidCodePage( UINT codepage )
1657 switch(codepage) {
1658 case CP_UTF7:
1659 case CP_UTF8:
1660 return TRUE;
1661 default:
1662 return wine_cp_get_table( codepage ) != NULL;
1667 /***********************************************************************
1668 * IsDBCSLeadByteEx (KERNEL32.@)
1670 * Determine if a character is a lead byte in a given code page.
1672 * PARAMS
1673 * codepage [I] Code page for the test.
1674 * testchar [I] Character to test
1676 * RETURNS
1677 * TRUE, if testchar is a lead byte in codepage,
1678 * FALSE otherwise.
1680 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1682 const union cptable *table = get_codepage_table( codepage );
1683 return table && wine_is_dbcs_leadbyte( table, testchar );
1687 /***********************************************************************
1688 * IsDBCSLeadByte (KERNEL32.@)
1689 * IsDBCSLeadByte (KERNEL.207)
1691 * Determine if a character is a lead byte.
1693 * PARAMS
1694 * testchar [I] Character to test
1696 * RETURNS
1697 * TRUE, if testchar is a lead byte in the ANSI code page,
1698 * FALSE otherwise.
1700 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1702 if (!ansi_cptable) return FALSE;
1703 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1707 /***********************************************************************
1708 * GetCPInfo (KERNEL32.@)
1710 * Get information about a code page.
1712 * PARAMS
1713 * codepage [I] Code page number
1714 * cpinfo [O] Destination for code page information
1716 * RETURNS
1717 * Success: TRUE. cpinfo is updated with the information about codepage.
1718 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1720 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1722 const union cptable *table;
1724 if (!cpinfo)
1726 SetLastError( ERROR_INVALID_PARAMETER );
1727 return FALSE;
1730 if (!(table = get_codepage_table( codepage )))
1732 switch(codepage)
1734 case CP_UTF7:
1735 case CP_UTF8:
1736 cpinfo->DefaultChar[0] = 0x3f;
1737 cpinfo->DefaultChar[1] = 0;
1738 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1739 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1740 return TRUE;
1743 SetLastError( ERROR_INVALID_PARAMETER );
1744 return FALSE;
1746 if (table->info.def_char & 0xff00)
1748 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1749 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1751 else
1753 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1754 cpinfo->DefaultChar[1] = 0;
1756 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1757 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1758 else
1759 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1761 return TRUE;
1764 /***********************************************************************
1765 * GetCPInfoExA (KERNEL32.@)
1767 * Get extended information about a code page.
1769 * PARAMS
1770 * codepage [I] Code page number
1771 * dwFlags [I] Reserved, must to 0.
1772 * cpinfo [O] Destination for code page information
1774 * RETURNS
1775 * Success: TRUE. cpinfo is updated with the information about codepage.
1776 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1778 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1780 CPINFOEXW cpinfoW;
1782 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1783 return FALSE;
1785 /* the layout is the same except for CodePageName */
1786 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1787 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1788 return TRUE;
1791 /***********************************************************************
1792 * GetCPInfoExW (KERNEL32.@)
1794 * Unicode version of GetCPInfoExA.
1796 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1798 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1799 return FALSE;
1801 switch(codepage)
1803 case CP_UTF7:
1805 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1807 cpinfo->CodePage = CP_UTF7;
1808 cpinfo->UnicodeDefaultChar = 0x3f;
1809 strcpyW(cpinfo->CodePageName, utf7);
1810 break;
1813 case CP_UTF8:
1815 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1817 cpinfo->CodePage = CP_UTF8;
1818 cpinfo->UnicodeDefaultChar = 0x3f;
1819 strcpyW(cpinfo->CodePageName, utf8);
1820 break;
1823 default:
1825 const union cptable *table = get_codepage_table( codepage );
1827 cpinfo->CodePage = table->info.codepage;
1828 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1829 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1830 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1831 break;
1834 return TRUE;
1837 /***********************************************************************
1838 * EnumSystemCodePagesA (KERNEL32.@)
1840 * Call a user defined function for every code page installed on the system.
1842 * PARAMS
1843 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1844 * flags [I] Reserved, set to 0.
1846 * RETURNS
1847 * TRUE, If all code pages have been enumerated, or
1848 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1850 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1852 const union cptable *table;
1853 char buffer[10];
1854 int index = 0;
1856 for (;;)
1858 if (!(table = wine_cp_enum_table( index++ ))) break;
1859 sprintf( buffer, "%d", table->info.codepage );
1860 if (!lpfnCodePageEnum( buffer )) break;
1862 return TRUE;
1866 /***********************************************************************
1867 * EnumSystemCodePagesW (KERNEL32.@)
1869 * See EnumSystemCodePagesA.
1871 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1873 const union cptable *table;
1874 WCHAR buffer[10], *p;
1875 int page, index = 0;
1877 for (;;)
1879 if (!(table = wine_cp_enum_table( index++ ))) break;
1880 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1881 *--p = 0;
1882 page = table->info.codepage;
1885 *--p = '0' + (page % 10);
1886 page /= 10;
1887 } while( page );
1888 if (!lpfnCodePageEnum( p )) break;
1890 return TRUE;
1894 /***********************************************************************
1895 * MultiByteToWideChar (KERNEL32.@)
1897 * Convert a multibyte character string into a Unicode string.
1899 * PARAMS
1900 * page [I] Codepage character set to convert from
1901 * flags [I] Character mapping flags
1902 * src [I] Source string buffer
1903 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1904 * dst [O] Destination buffer
1905 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1907 * RETURNS
1908 * Success: If dstlen > 0, the number of characters written to dst.
1909 * If dstlen == 0, the number of characters needed to perform the
1910 * conversion. In both cases the count includes the terminating NUL.
1911 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1912 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1913 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1914 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1915 * possible for src.
1917 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1918 LPWSTR dst, INT dstlen )
1920 const union cptable *table;
1921 int ret;
1923 if (!src || !srclen || (!dst && dstlen))
1925 SetLastError( ERROR_INVALID_PARAMETER );
1926 return 0;
1929 if (srclen < 0) srclen = strlen(src) + 1;
1931 switch(page)
1933 case CP_SYMBOL:
1934 if (flags)
1936 SetLastError( ERROR_INVALID_FLAGS );
1937 return 0;
1939 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1940 break;
1941 case CP_UTF7:
1942 if (flags)
1944 SetLastError( ERROR_INVALID_FLAGS );
1945 return 0;
1947 FIXME("UTF-7 not supported\n");
1948 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1949 return 0;
1950 case CP_UNIXCP:
1951 if (unix_cptable)
1953 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1954 break;
1956 #ifdef __APPLE__
1957 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1958 #endif
1959 /* fall through */
1960 case CP_UTF8:
1961 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1962 break;
1963 default:
1964 if (!(table = get_codepage_table( page )))
1966 SetLastError( ERROR_INVALID_PARAMETER );
1967 return 0;
1969 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1970 break;
1973 if (ret < 0)
1975 switch(ret)
1977 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1978 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1980 ret = 0;
1982 TRACE("cp %d %s -> %s, ret = %d\n",
1983 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1984 return ret;
1988 /***********************************************************************
1989 * WideCharToMultiByte (KERNEL32.@)
1991 * Convert a Unicode character string into a multibyte string.
1993 * PARAMS
1994 * page [I] Code page character set to convert to
1995 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
1996 * src [I] Source string buffer
1997 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1998 * dst [O] Destination buffer
1999 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2000 * defchar [I] Default character to use for conversion if no exact
2001 * conversion can be made
2002 * used [O] Set if default character was used in the conversion
2004 * RETURNS
2005 * Success: If dstlen > 0, the number of characters written to dst.
2006 * If dstlen == 0, number of characters needed to perform the
2007 * conversion. In both cases the count includes the terminating NUL.
2008 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2009 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2010 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2011 * parameter was given.
2013 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2014 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2016 const union cptable *table;
2017 int ret, used_tmp;
2019 if (!src || !srclen || (!dst && dstlen))
2021 SetLastError( ERROR_INVALID_PARAMETER );
2022 return 0;
2025 if (srclen < 0) srclen = strlenW(src) + 1;
2027 switch(page)
2029 case CP_SYMBOL:
2030 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2031 if (flags)
2033 SetLastError( ERROR_INVALID_FLAGS );
2034 return 0;
2036 if (defchar || used)
2038 SetLastError( ERROR_INVALID_PARAMETER );
2039 return 0;
2041 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2042 break;
2043 case CP_UTF7:
2044 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2045 if (defchar || used)
2047 SetLastError( ERROR_INVALID_PARAMETER );
2048 return 0;
2050 if (flags)
2052 SetLastError( ERROR_INVALID_FLAGS );
2053 return 0;
2055 FIXME("UTF-7 not supported\n");
2056 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2057 return 0;
2058 case CP_UNIXCP:
2059 if (unix_cptable)
2061 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2062 defchar, used ? &used_tmp : NULL );
2063 break;
2065 /* fall through */
2066 case CP_UTF8:
2067 if (defchar || used)
2069 SetLastError( ERROR_INVALID_PARAMETER );
2070 return 0;
2072 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2073 break;
2074 default:
2075 if (!(table = get_codepage_table( page )))
2077 SetLastError( ERROR_INVALID_PARAMETER );
2078 return 0;
2080 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2081 defchar, used ? &used_tmp : NULL );
2082 if (used) *used = used_tmp;
2083 break;
2086 if (ret < 0)
2088 switch(ret)
2090 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2091 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2093 ret = 0;
2095 TRACE("cp %d %s -> %s, ret = %d\n",
2096 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2097 return ret;
2101 /***********************************************************************
2102 * GetThreadLocale (KERNEL32.@)
2104 * Get the current threads locale.
2106 * PARAMS
2107 * None.
2109 * RETURNS
2110 * The LCID currently associated with the calling thread.
2112 LCID WINAPI GetThreadLocale(void)
2114 LCID ret = NtCurrentTeb()->CurrentLocale;
2115 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2116 return ret;
2119 /**********************************************************************
2120 * SetThreadLocale (KERNEL32.@)
2122 * Set the current threads locale.
2124 * PARAMS
2125 * lcid [I] LCID of the locale to set
2127 * RETURNS
2128 * Success: TRUE. The threads locale is set to lcid.
2129 * Failure: FALSE. Use GetLastError() to determine the cause.
2131 BOOL WINAPI SetThreadLocale( LCID lcid )
2133 TRACE("(0x%04X)\n", lcid);
2135 lcid = ConvertDefaultLocale(lcid);
2137 if (lcid != GetThreadLocale())
2139 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2141 SetLastError(ERROR_INVALID_PARAMETER);
2142 return FALSE;
2145 NtCurrentTeb()->CurrentLocale = lcid;
2147 return TRUE;
2150 /**********************************************************************
2151 * SetThreadUILanguage (KERNEL32.@)
2153 * Set the current threads UI language.
2155 * PARAMS
2156 * langid [I] LANGID of the language to set, or 0 to use
2157 * the available language which is best supported
2158 * for console applications
2160 * RETURNS
2161 * Success: The return value is the same as the input value.
2162 * Failure: The return value differs from the input value.
2163 * Use GetLastError() to determine the cause.
2165 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2167 TRACE("(0x%04x) stub - returning success\n", langid);
2168 return langid;
2171 /******************************************************************************
2172 * ConvertDefaultLocale (KERNEL32.@)
2174 * Convert a default locale identifier into a real identifier.
2176 * PARAMS
2177 * lcid [I] LCID identifier of the locale to convert
2179 * RETURNS
2180 * lcid unchanged, if not a default locale or its sublanguage is
2181 * not SUBLANG_NEUTRAL.
2182 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2183 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2184 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2186 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2188 LANGID langid;
2190 switch (lcid)
2192 case LOCALE_SYSTEM_DEFAULT:
2193 lcid = GetSystemDefaultLCID();
2194 break;
2195 case LOCALE_USER_DEFAULT:
2196 case LOCALE_NEUTRAL:
2197 lcid = GetUserDefaultLCID();
2198 break;
2199 default:
2200 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2201 langid = LANGIDFROMLCID(lcid);
2202 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2204 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2205 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2208 return lcid;
2212 /******************************************************************************
2213 * IsValidLocale (KERNEL32.@)
2215 * Determine if a locale is valid.
2217 * PARAMS
2218 * lcid [I] LCID of the locale to check
2219 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2221 * RETURNS
2222 * TRUE, if lcid is valid,
2223 * FALSE, otherwise.
2225 * NOTES
2226 * Wine does not currently make the distinction between supported and installed. All
2227 * languages supported are installed by default.
2229 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2231 /* check if language is registered in the kernel32 resources */
2232 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2233 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2236 /******************************************************************************
2237 * IsValidLocaleName (KERNEL32.@)
2239 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2241 struct locale_name locale_name;
2243 /* string parsing */
2244 parse_locale_name( locale, &locale_name );
2246 TRACE( "found lcid %x for %s, matches %d\n",
2247 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2249 return locale_name.matches > 0;
2252 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2253 LPCSTR name, WORD LangID, LONG_PTR lParam )
2255 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2256 char buf[20];
2258 sprintf(buf, "%08x", (UINT)LangID);
2259 return lpfnLocaleEnum( buf );
2262 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2263 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2265 static const WCHAR formatW[] = {'%','0','8','x',0};
2266 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2267 WCHAR buf[20];
2268 sprintfW( buf, formatW, (UINT)LangID );
2269 return lpfnLocaleEnum( buf );
2272 /******************************************************************************
2273 * EnumSystemLocalesA (KERNEL32.@)
2275 * Call a users function for each locale available on the system.
2277 * PARAMS
2278 * lpfnLocaleEnum [I] Callback function to call for each locale
2279 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2281 * RETURNS
2282 * Success: TRUE.
2283 * Failure: FALSE. Use GetLastError() to determine the cause.
2285 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2287 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2288 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2289 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2290 (LONG_PTR)lpfnLocaleEnum);
2291 return TRUE;
2295 /******************************************************************************
2296 * EnumSystemLocalesW (KERNEL32.@)
2298 * See EnumSystemLocalesA.
2300 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2302 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2303 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2304 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2305 (LONG_PTR)lpfnLocaleEnum);
2306 return TRUE;
2310 struct enum_locale_ex_data
2312 LOCALE_ENUMPROCEX proc;
2313 DWORD flags;
2314 LPARAM lparam;
2317 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2318 LPCWSTR name, WORD lang, LONG_PTR lparam )
2320 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2321 WCHAR buffer[256];
2322 DWORD neutral;
2323 unsigned int flags;
2325 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2326 buffer, sizeof(buffer) / sizeof(WCHAR) );
2327 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2328 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2329 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2330 neutral = 0;
2331 flags = LOCALE_WINDOWS;
2332 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2333 if (data->flags && ~(data->flags & flags)) return TRUE;
2334 return data->proc( buffer, flags, data->lparam );
2337 /******************************************************************************
2338 * EnumSystemLocalesEx (KERNEL32.@)
2340 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2342 struct enum_locale_ex_data data;
2344 if (reserved)
2346 SetLastError( ERROR_INVALID_PARAMETER );
2347 return FALSE;
2349 data.proc = proc;
2350 data.flags = flags;
2351 data.lparam = lparam;
2352 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2353 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2354 enum_locale_ex_proc, (LONG_PTR)&data );
2355 return TRUE;
2359 /***********************************************************************
2360 * VerLanguageNameA (KERNEL32.@)
2362 * Get the name of a language.
2364 * PARAMS
2365 * wLang [I] LANGID of the language
2366 * szLang [O] Destination for the language name
2368 * RETURNS
2369 * Success: The size of the language name. If szLang is non-NULL, it is filled
2370 * with the name.
2371 * Failure: 0. Use GetLastError() to determine the cause.
2374 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2376 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2380 /***********************************************************************
2381 * VerLanguageNameW (KERNEL32.@)
2383 * See VerLanguageNameA.
2385 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2387 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2391 /******************************************************************************
2392 * GetStringTypeW (KERNEL32.@)
2394 * See GetStringTypeA.
2396 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2398 static const unsigned char type2_map[16] =
2400 C2_NOTAPPLICABLE, /* unassigned */
2401 C2_LEFTTORIGHT, /* L */
2402 C2_RIGHTTOLEFT, /* R */
2403 C2_EUROPENUMBER, /* EN */
2404 C2_EUROPESEPARATOR, /* ES */
2405 C2_EUROPETERMINATOR, /* ET */
2406 C2_ARABICNUMBER, /* AN */
2407 C2_COMMONSEPARATOR, /* CS */
2408 C2_BLOCKSEPARATOR, /* B */
2409 C2_SEGMENTSEPARATOR, /* S */
2410 C2_WHITESPACE, /* WS */
2411 C2_OTHERNEUTRAL, /* ON */
2412 C2_RIGHTTOLEFT, /* AL */
2413 C2_NOTAPPLICABLE, /* NSM */
2414 C2_NOTAPPLICABLE, /* BN */
2415 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2418 if (count == -1) count = strlenW(src) + 1;
2419 switch(type)
2421 case CT_CTYPE1:
2422 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2423 break;
2424 case CT_CTYPE2:
2425 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2426 break;
2427 case CT_CTYPE3:
2429 WARN("CT_CTYPE3: semi-stub.\n");
2430 while (count--)
2432 int c = *src;
2433 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2435 type1 = get_char_typeW( *src++ ) & 0xfff;
2436 /* try to construct type3 from type1 */
2437 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2438 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2439 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2440 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2441 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2442 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2443 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2445 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2446 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2447 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2448 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2449 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2450 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2451 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2452 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2454 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2455 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2456 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2457 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2458 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2459 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2460 *chartype++ = type3;
2462 break;
2464 default:
2465 SetLastError( ERROR_INVALID_PARAMETER );
2466 return FALSE;
2468 return TRUE;
2472 /******************************************************************************
2473 * GetStringTypeExW (KERNEL32.@)
2475 * See GetStringTypeExA.
2477 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2479 /* locale is ignored for Unicode */
2480 return GetStringTypeW( type, src, count, chartype );
2484 /******************************************************************************
2485 * GetStringTypeA (KERNEL32.@)
2487 * Get characteristics of the characters making up a string.
2489 * PARAMS
2490 * locale [I] Locale Id for the string
2491 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2492 * src [I] String to analyse
2493 * count [I] Length of src in chars, or -1 if src is NUL terminated
2494 * chartype [O] Destination for the calculated characteristics
2496 * RETURNS
2497 * Success: TRUE. chartype is filled with the requested characteristics of each char
2498 * in src.
2499 * Failure: FALSE. Use GetLastError() to determine the cause.
2501 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2503 UINT cp;
2504 INT countW;
2505 LPWSTR srcW;
2506 BOOL ret = FALSE;
2508 if(count == -1) count = strlen(src) + 1;
2510 if (!(cp = get_lcid_codepage( locale )))
2512 FIXME("For locale %04x using current ANSI code page\n", locale);
2513 cp = GetACP();
2516 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2517 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2519 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2521 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2522 * string, with multibyte characters there maybe be more bytes in count
2523 * than character space in the buffer!
2525 ret = GetStringTypeW(type, srcW, countW, chartype);
2526 HeapFree(GetProcessHeap(), 0, srcW);
2528 return ret;
2531 /******************************************************************************
2532 * GetStringTypeExA (KERNEL32.@)
2534 * Get characteristics of the characters making up a string.
2536 * PARAMS
2537 * locale [I] Locale Id for the string
2538 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2539 * src [I] String to analyse
2540 * count [I] Length of src in chars, or -1 if src is NUL terminated
2541 * chartype [O] Destination for the calculated characteristics
2543 * RETURNS
2544 * Success: TRUE. chartype is filled with the requested characteristics of each char
2545 * in src.
2546 * Failure: FALSE. Use GetLastError() to determine the cause.
2548 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2550 return GetStringTypeA(locale, type, src, count, chartype);
2553 /*************************************************************************
2554 * LCMapStringEx (KERNEL32.@)
2556 * Map characters in a locale sensitive string.
2558 * PARAMS
2559 * name [I] Locale name for the conversion.
2560 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2561 * src [I] String to map
2562 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2563 * dst [O] Destination for mapped string
2564 * dstlen [I] Length of dst in characters
2565 * version [I] reserved, must be NULL
2566 * reserved [I] reserved, must be NULL
2567 * lparam [I] reserved, must be 0
2569 * RETURNS
2570 * Success: The length of the mapped string in dst, including the NUL terminator.
2571 * Failure: 0. Use GetLastError() to determine the cause.
2573 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2574 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2576 LPWSTR dst_ptr;
2578 if (version) FIXME("unsupported version structure %p\n", version);
2579 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2580 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2582 if (!src || !srclen || dstlen < 0)
2584 SetLastError(ERROR_INVALID_PARAMETER);
2585 return 0;
2588 /* mutually exclusive flags */
2589 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2590 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2591 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2592 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2594 SetLastError(ERROR_INVALID_FLAGS);
2595 return 0;
2598 if (!dstlen) dst = NULL;
2600 if (flags & LCMAP_SORTKEY)
2602 INT ret;
2603 if (src == dst)
2605 SetLastError(ERROR_INVALID_FLAGS);
2606 return 0;
2609 if (srclen < 0) srclen = strlenW(src);
2611 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2612 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2614 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2615 if (ret == 0)
2616 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2617 else
2618 ret++;
2619 return ret;
2622 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2623 if (flags & SORT_STRINGSORT)
2625 SetLastError(ERROR_INVALID_FLAGS);
2626 return 0;
2629 if (srclen < 0) srclen = strlenW(src) + 1;
2631 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2632 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2634 if (!dst) /* return required string length */
2636 INT len;
2638 for (len = 0; srclen; src++, srclen--)
2640 WCHAR wch = *src;
2641 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2642 * and skips white space and punctuation characters for
2643 * NORM_IGNORESYMBOLS.
2645 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2646 continue;
2647 len++;
2649 return len;
2652 if (flags & LCMAP_UPPERCASE)
2654 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2656 WCHAR wch = *src;
2657 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2658 continue;
2659 *dst_ptr++ = toupperW(wch);
2660 dstlen--;
2663 else if (flags & LCMAP_LOWERCASE)
2665 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2667 WCHAR wch = *src;
2668 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2669 continue;
2670 *dst_ptr++ = tolowerW(wch);
2671 dstlen--;
2674 else
2676 if (src == dst)
2678 SetLastError(ERROR_INVALID_FLAGS);
2679 return 0;
2681 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2683 WCHAR wch = *src;
2684 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2685 continue;
2686 *dst_ptr++ = wch;
2687 dstlen--;
2691 if (srclen)
2693 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2694 return 0;
2697 return dst_ptr - dst;
2700 /*************************************************************************
2701 * LCMapStringW (KERNEL32.@)
2703 * See LCMapStringA.
2705 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2706 LPWSTR dst, INT dstlen)
2708 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2709 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2711 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2714 /*************************************************************************
2715 * LCMapStringA (KERNEL32.@)
2717 * Map characters in a locale sensitive string.
2719 * PARAMS
2720 * lcid [I] LCID for the conversion.
2721 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2722 * src [I] String to map
2723 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2724 * dst [O] Destination for mapped string
2725 * dstlen [I] Length of dst in characters
2727 * RETURNS
2728 * Success: The length of the mapped string in dst, including the NUL terminator.
2729 * Failure: 0. Use GetLastError() to determine the cause.
2731 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2732 LPSTR dst, INT dstlen)
2734 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2735 LPWSTR srcW, dstW;
2736 INT ret = 0, srclenW, dstlenW;
2737 UINT locale_cp = CP_ACP;
2739 if (!src || !srclen || dstlen < 0)
2741 SetLastError(ERROR_INVALID_PARAMETER);
2742 return 0;
2745 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2747 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2748 if (srclenW)
2749 srcW = bufW;
2750 else
2752 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2753 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2754 if (!srcW)
2756 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2757 return 0;
2759 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2762 if (flags & LCMAP_SORTKEY)
2764 if (src == dst)
2766 SetLastError(ERROR_INVALID_FLAGS);
2767 goto map_string_exit;
2769 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2770 if (ret == 0)
2771 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2772 else
2773 ret++;
2774 goto map_string_exit;
2777 if (flags & SORT_STRINGSORT)
2779 SetLastError(ERROR_INVALID_FLAGS);
2780 goto map_string_exit;
2783 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2784 if (!dstlenW)
2785 goto map_string_exit;
2787 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2788 if (!dstW)
2790 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2791 goto map_string_exit;
2794 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2795 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2796 HeapFree(GetProcessHeap(), 0, dstW);
2798 map_string_exit:
2799 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2800 return ret;
2803 /*************************************************************************
2804 * FoldStringA (KERNEL32.@)
2806 * Map characters in a string.
2808 * PARAMS
2809 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2810 * src [I] String to map
2811 * srclen [I] Length of src, or -1 if src is NUL terminated
2812 * dst [O] Destination for mapped string
2813 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2815 * RETURNS
2816 * Success: The length of the string written to dst, including the terminating NUL. If
2817 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2818 * and dst may be NULL.
2819 * Failure: 0. Use GetLastError() to determine the cause.
2821 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2822 LPSTR dst, INT dstlen)
2824 INT ret = 0, srclenW = 0;
2825 WCHAR *srcW = NULL, *dstW = NULL;
2827 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2829 SetLastError(ERROR_INVALID_PARAMETER);
2830 return 0;
2833 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2834 src, srclen, NULL, 0);
2835 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2837 if (!srcW)
2839 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2840 goto FoldStringA_exit;
2843 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2844 src, srclen, srcW, srclenW);
2846 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2848 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2849 if (ret && dstlen)
2851 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2853 if (!dstW)
2855 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2856 goto FoldStringA_exit;
2859 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2860 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2862 ret = 0;
2863 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2867 HeapFree(GetProcessHeap(), 0, dstW);
2869 FoldStringA_exit:
2870 HeapFree(GetProcessHeap(), 0, srcW);
2871 return ret;
2874 /*************************************************************************
2875 * FoldStringW (KERNEL32.@)
2877 * See FoldStringA.
2879 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2880 LPWSTR dst, INT dstlen)
2882 int ret;
2884 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2886 case 0:
2887 if (dwFlags)
2888 break;
2889 /* Fall through for dwFlags == 0 */
2890 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2891 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2892 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2893 SetLastError(ERROR_INVALID_FLAGS);
2894 return 0;
2897 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2899 SetLastError(ERROR_INVALID_PARAMETER);
2900 return 0;
2903 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2904 if (!ret)
2905 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2906 return ret;
2909 /******************************************************************************
2910 * CompareStringW (KERNEL32.@)
2912 * See CompareStringA.
2914 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2915 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2917 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2920 /******************************************************************************
2921 * CompareStringEx (KERNEL32.@)
2923 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2924 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2926 INT ret;
2928 if (version) FIXME("unexpected version parameter\n");
2929 if (reserved) FIXME("unexpected reserved value\n");
2930 if (lParam) FIXME("unexpected lParam\n");
2932 if (!str1 || !str2)
2934 SetLastError(ERROR_INVALID_PARAMETER);
2935 return 0;
2938 if( flags & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2939 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2941 SetLastError(ERROR_INVALID_FLAGS);
2942 return 0;
2945 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2946 if (flags & 0x10000000)
2947 WARN("Ignoring unknown flags 0x10000000\n");
2949 if (len1 < 0) len1 = strlenW(str1);
2950 if (len2 < 0) len2 = strlenW(str2);
2952 ret = wine_compare_string(flags, str1, len1, str2, len2);
2954 if (ret) /* need to translate result */
2955 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2956 return CSTR_EQUAL;
2959 /******************************************************************************
2960 * CompareStringA (KERNEL32.@)
2962 * Compare two locale sensitive strings.
2964 * PARAMS
2965 * lcid [I] LCID for the comparison
2966 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
2967 * str1 [I] First string to compare
2968 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
2969 * str2 [I] Second string to compare
2970 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
2972 * RETURNS
2973 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2974 * str1 is less than, equal to or greater than str2 respectively.
2975 * Failure: FALSE. Use GetLastError() to determine the cause.
2977 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
2978 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2980 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2981 WCHAR *buf2W = buf1W + 130;
2982 LPWSTR str1W, str2W;
2983 INT len1W, len2W, ret;
2984 UINT locale_cp = CP_ACP;
2986 if (!str1 || !str2)
2988 SetLastError(ERROR_INVALID_PARAMETER);
2989 return 0;
2991 if (len1 < 0) len1 = strlen(str1);
2992 if (len2 < 0) len2 = strlen(str2);
2994 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2996 if (len1)
2998 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2999 if (len1W)
3000 str1W = buf1W;
3001 else
3003 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3004 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3005 if (!str1W)
3007 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3008 return 0;
3010 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3013 else
3015 len1W = 0;
3016 str1W = buf1W;
3019 if (len2)
3021 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3022 if (len2W)
3023 str2W = buf2W;
3024 else
3026 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3027 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3028 if (!str2W)
3030 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3031 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3032 return 0;
3034 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3037 else
3039 len2W = 0;
3040 str2W = buf2W;
3043 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3045 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3046 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3047 return ret;
3050 /******************************************************************************
3051 * CompareStringOrdinal (KERNEL32.@)
3053 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3055 int ret, len;
3057 if (!str1 || !str2)
3059 SetLastError(ERROR_INVALID_PARAMETER);
3060 return 0;
3062 if (len1 < 0) len1 = strlenW(str1);
3063 if (len2 < 0) len2 = strlenW(str2);
3065 len = min(len1, len2);
3066 if (ignore_case)
3068 ret = memicmpW(str1, str2, len);
3070 else
3072 ret = 0;
3073 for (; len > 0; len--)
3074 if ((ret = (*str1++ - *str2++))) break;
3076 if (!ret) ret = len1 - len2;
3078 if (ret < 0) return CSTR_LESS_THAN;
3079 if (ret > 0) return CSTR_GREATER_THAN;
3080 return CSTR_EQUAL;
3083 /*************************************************************************
3084 * lstrcmp (KERNEL32.@)
3085 * lstrcmpA (KERNEL32.@)
3087 * Compare two strings using the current thread locale.
3089 * PARAMS
3090 * str1 [I] First string to compare
3091 * str2 [I] Second string to compare
3093 * RETURNS
3094 * Success: A number less than, equal to or greater than 0 depending on whether
3095 * str1 is less than, equal to or greater than str2 respectively.
3096 * Failure: FALSE. Use GetLastError() to determine the cause.
3098 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3100 int ret;
3102 if ((str1 == NULL) && (str2 == NULL)) return 0;
3103 if (str1 == NULL) return -1;
3104 if (str2 == NULL) return 1;
3106 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3107 if (ret) ret -= 2;
3109 return ret;
3112 /*************************************************************************
3113 * lstrcmpi (KERNEL32.@)
3114 * lstrcmpiA (KERNEL32.@)
3116 * Compare two strings using the current thread locale, ignoring case.
3118 * PARAMS
3119 * str1 [I] First string to compare
3120 * str2 [I] Second string to compare
3122 * RETURNS
3123 * Success: A number less than, equal to or greater than 0 depending on whether
3124 * str2 is less than, equal to or greater than str1 respectively.
3125 * Failure: FALSE. Use GetLastError() to determine the cause.
3127 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3129 int ret;
3131 if ((str1 == NULL) && (str2 == NULL)) return 0;
3132 if (str1 == NULL) return -1;
3133 if (str2 == NULL) return 1;
3135 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3136 if (ret) ret -= 2;
3138 return ret;
3141 /*************************************************************************
3142 * lstrcmpW (KERNEL32.@)
3144 * See lstrcmpA.
3146 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3148 int ret;
3150 if ((str1 == NULL) && (str2 == NULL)) return 0;
3151 if (str1 == NULL) return -1;
3152 if (str2 == NULL) return 1;
3154 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3155 if (ret) ret -= 2;
3157 return ret;
3160 /*************************************************************************
3161 * lstrcmpiW (KERNEL32.@)
3163 * See lstrcmpiA.
3165 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3167 int ret;
3169 if ((str1 == NULL) && (str2 == NULL)) return 0;
3170 if (str1 == NULL) return -1;
3171 if (str2 == NULL) return 1;
3173 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3174 if (ret) ret -= 2;
3176 return ret;
3179 /******************************************************************************
3180 * LOCALE_Init
3182 void LOCALE_Init(void)
3184 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3185 const union cptable *unix_cp );
3187 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3189 #ifdef __APPLE__
3190 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3191 char user_locale[50];
3193 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3194 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3195 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3196 CFStringRef user_locale_string_ref;
3198 if (user_locale_country_ref)
3200 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3201 user_locale_lang_ref, user_locale_country_ref);
3203 else
3205 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3206 user_locale_lang_ref);
3209 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3211 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3212 setenv( "LANG", user_locale, 0 );
3213 TRACE( "setting locale to '%s'\n", user_locale );
3214 #endif /* __APPLE__ */
3216 setlocale( LC_ALL, "" );
3218 unix_cp = setup_unix_locales();
3219 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3221 #ifdef __APPLE__
3222 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3223 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3225 /* Retrieve the preferred language as chosen in System Preferences. */
3226 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3227 leave things be. */
3228 CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3229 CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3230 CFStringRef user_language_string_ref;
3231 if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3232 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3233 !CFEqual(user_language_string_ref, user_locale_lang_ref))
3235 struct locale_name locale_name;
3236 WCHAR buffer[128];
3237 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3238 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3239 parse_locale_name( buffer, &locale_name );
3240 lcid_LC_MESSAGES = locale_name.lcid;
3241 TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3243 CFRelease( all_locales );
3244 if (preferred_locales)
3245 CFRelease( preferred_locales );
3248 CFRelease( user_locale_ref );
3249 CFRelease( user_locale_string_ref );
3250 #endif
3252 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3253 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3254 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3256 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3257 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3258 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3259 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3260 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3261 if (!unix_cp)
3262 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3263 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3265 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3266 ansi_cptable = wine_cp_get_table( 1252 );
3267 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3268 oem_cptable = wine_cp_get_table( 437 );
3269 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3270 mac_cptable = wine_cp_get_table( 10000 );
3271 if (unix_cp != CP_UTF8)
3273 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3274 unix_cptable = wine_cp_get_table( 28591 );
3277 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3279 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3280 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3281 mac_cptable->info.codepage, unix_cp );
3283 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3286 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3288 UNICODE_STRING keyName;
3289 OBJECT_ATTRIBUTES attr;
3290 HANDLE hkey;
3292 RtlInitUnicodeString( &keyName, szKeyName );
3293 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3295 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3296 hkey = 0;
3298 return hkey;
3301 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3302 ULONG keyNameSize)
3304 BYTE buffer[80];
3305 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3306 DWORD dwLen;
3308 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3309 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3310 info->NameLength > keyNameSize)
3312 return FALSE;
3315 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3317 memcpy( szKeyName, info->Name, info->NameLength);
3318 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3320 TRACE("returning %s\n", debugstr_w(szKeyName));
3321 return TRUE;
3324 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3325 LPWSTR szValueName, ULONG valueNameSize,
3326 LPWSTR szValueData, ULONG valueDataSize)
3328 BYTE buffer[80];
3329 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3330 DWORD dwLen;
3332 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3333 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3334 info->NameLength > valueNameSize ||
3335 info->DataLength > valueDataSize)
3337 return FALSE;
3340 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3342 memcpy( szValueName, info->Name, info->NameLength);
3343 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3344 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3345 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3347 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3348 return TRUE;
3351 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3353 BYTE buffer[128];
3354 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3355 DWORD dwSize = sizeof(buffer);
3356 UNICODE_STRING valueName;
3358 RtlInitUnicodeString( &valueName, szValueName );
3360 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3361 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3362 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3363 info->DataLength == sizeof(DWORD))
3365 memcpy(lpVal, info->Data, sizeof(DWORD));
3366 return TRUE;
3369 return FALSE;
3372 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3374 LANGID langId;
3375 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3376 HRSRC hResource;
3377 BOOL bRet = FALSE;
3379 /* FIXME: Is it correct to use the system default langid? */
3380 langId = GetSystemDefaultLangID();
3382 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3383 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3385 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3387 if (hResource)
3389 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3391 if (hResDir)
3393 ULONG iResourceIndex = lgrpid & 0xf;
3394 LPCWSTR lpResEntry = LockResource( hResDir );
3395 ULONG i;
3397 for (i = 0; i < iResourceIndex; i++)
3398 lpResEntry += *lpResEntry + 1;
3400 if (*lpResEntry < nameSize)
3402 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3403 szName[*lpResEntry] = '\0';
3404 bRet = TRUE;
3408 FreeResource( hResource );
3410 return bRet;
3413 /* Registry keys for NLS related information */
3415 static const WCHAR szCountryListName[] = {
3416 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3417 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3418 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3419 'T','e','l','e','p','h','o','n','y','\\',
3420 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3424 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3425 typedef struct
3427 LANGUAGEGROUP_ENUMPROCA procA;
3428 LANGUAGEGROUP_ENUMPROCW procW;
3429 DWORD dwFlags;
3430 LONG_PTR lParam;
3431 } ENUMLANGUAGEGROUP_CALLBACKS;
3433 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3434 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3436 WCHAR szNumber[10], szValue[4];
3437 HANDLE hKey;
3438 BOOL bContinue = TRUE;
3439 ULONG ulIndex = 0;
3441 if (!lpProcs)
3443 SetLastError(ERROR_INVALID_PARAMETER);
3444 return FALSE;
3447 switch (lpProcs->dwFlags)
3449 case 0:
3450 /* Default to LGRPID_INSTALLED */
3451 lpProcs->dwFlags = LGRPID_INSTALLED;
3452 /* Fall through... */
3453 case LGRPID_INSTALLED:
3454 case LGRPID_SUPPORTED:
3455 break;
3456 default:
3457 SetLastError(ERROR_INVALID_FLAGS);
3458 return FALSE;
3461 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3463 if (!hKey)
3464 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3466 while (bContinue)
3468 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3469 szValue, sizeof(szValue) ))
3471 BOOL bInstalled = szValue[0] == '1';
3472 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3474 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3475 bInstalled ? "" : "not ");
3477 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3479 WCHAR szGrpName[48];
3481 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3482 szGrpName[0] = '\0';
3484 if (lpProcs->procW)
3485 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3486 lpProcs->lParam );
3487 else
3489 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3490 char szGrpNameA[48];
3492 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3493 * or whether the language names are ever localised. Assume CP_ACP.
3496 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3497 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3499 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3500 lpProcs->lParam );
3504 ulIndex++;
3506 else
3507 bContinue = FALSE;
3509 if (!bContinue)
3510 break;
3513 if (hKey)
3514 NtClose( hKey );
3516 return TRUE;
3519 /******************************************************************************
3520 * EnumSystemLanguageGroupsA (KERNEL32.@)
3522 * Call a users function for each language group available on the system.
3524 * PARAMS
3525 * pLangGrpEnumProc [I] Callback function to call for each language group
3526 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3527 * lParam [I] User parameter to pass to pLangGrpEnumProc
3529 * RETURNS
3530 * Success: TRUE.
3531 * Failure: FALSE. Use GetLastError() to determine the cause.
3533 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3534 DWORD dwFlags, LONG_PTR lParam)
3536 ENUMLANGUAGEGROUP_CALLBACKS procs;
3538 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3540 procs.procA = pLangGrpEnumProc;
3541 procs.procW = NULL;
3542 procs.dwFlags = dwFlags;
3543 procs.lParam = lParam;
3545 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3548 /******************************************************************************
3549 * EnumSystemLanguageGroupsW (KERNEL32.@)
3551 * See EnumSystemLanguageGroupsA.
3553 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3554 DWORD dwFlags, LONG_PTR lParam)
3556 ENUMLANGUAGEGROUP_CALLBACKS procs;
3558 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3560 procs.procA = NULL;
3561 procs.procW = pLangGrpEnumProc;
3562 procs.dwFlags = dwFlags;
3563 procs.lParam = lParam;
3565 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3568 /******************************************************************************
3569 * IsValidLanguageGroup (KERNEL32.@)
3571 * Determine if a language group is supported and/or installed.
3573 * PARAMS
3574 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3575 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3577 * RETURNS
3578 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3579 * FALSE otherwise.
3581 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3583 static const WCHAR szFormat[] = { '%','x','\0' };
3584 WCHAR szValueName[16], szValue[2];
3585 BOOL bSupported = FALSE, bInstalled = FALSE;
3586 HANDLE hKey;
3589 switch (dwFlags)
3591 case LGRPID_INSTALLED:
3592 case LGRPID_SUPPORTED:
3594 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3596 sprintfW( szValueName, szFormat, lgrpid );
3598 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3600 bSupported = TRUE;
3602 if (szValue[0] == '1')
3603 bInstalled = TRUE;
3606 if (hKey)
3607 NtClose( hKey );
3609 break;
3612 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3613 (dwFlags == LGRPID_INSTALLED && bInstalled))
3614 return TRUE;
3616 return FALSE;
3619 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3620 typedef struct
3622 LANGGROUPLOCALE_ENUMPROCA procA;
3623 LANGGROUPLOCALE_ENUMPROCW procW;
3624 DWORD dwFlags;
3625 LGRPID lgrpid;
3626 LONG_PTR lParam;
3627 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3629 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3630 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3632 static const WCHAR szAlternateSortsKeyName[] = {
3633 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3635 WCHAR szNumber[10], szValue[4];
3636 HANDLE hKey;
3637 BOOL bContinue = TRUE, bAlternate = FALSE;
3638 LGRPID lgrpid;
3639 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3641 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3643 SetLastError(ERROR_INVALID_PARAMETER);
3644 return FALSE;
3647 if (lpProcs->dwFlags)
3649 SetLastError(ERROR_INVALID_FLAGS);
3650 return FALSE;
3653 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3655 if (!hKey)
3656 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3658 while (bContinue)
3660 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3661 szValue, sizeof(szValue) ))
3663 lgrpid = strtoulW( szValue, NULL, 16 );
3665 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3666 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3668 if (lgrpid == lpProcs->lgrpid)
3670 LCID lcid;
3672 lcid = strtoulW( szNumber, NULL, 16 );
3674 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3675 * '00000437 ;Georgian'
3676 * At present we only pass the LCID string.
3679 if (lpProcs->procW)
3680 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3681 else
3683 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3685 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3687 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3691 ulIndex++;
3693 else
3695 /* Finished enumerating this key */
3696 if (!bAlternate)
3698 /* Enumerate alternate sorts also */
3699 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3700 bAlternate = TRUE;
3701 ulIndex = 0;
3703 else
3704 bContinue = FALSE; /* Finished both keys */
3707 if (!bContinue)
3708 break;
3711 if (hKey)
3712 NtClose( hKey );
3714 return TRUE;
3717 /******************************************************************************
3718 * EnumLanguageGroupLocalesA (KERNEL32.@)
3720 * Call a users function for every locale in a language group available on the system.
3722 * PARAMS
3723 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3724 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3725 * dwFlags [I] Reserved, set to 0
3726 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3728 * RETURNS
3729 * Success: TRUE.
3730 * Failure: FALSE. Use GetLastError() to determine the cause.
3732 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3733 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3735 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3737 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3739 callbacks.procA = pLangGrpLcEnumProc;
3740 callbacks.procW = NULL;
3741 callbacks.dwFlags = dwFlags;
3742 callbacks.lgrpid = lgrpid;
3743 callbacks.lParam = lParam;
3745 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3748 /******************************************************************************
3749 * EnumLanguageGroupLocalesW (KERNEL32.@)
3751 * See EnumLanguageGroupLocalesA.
3753 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3754 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3756 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3758 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3760 callbacks.procA = NULL;
3761 callbacks.procW = pLangGrpLcEnumProc;
3762 callbacks.dwFlags = dwFlags;
3763 callbacks.lgrpid = lgrpid;
3764 callbacks.lParam = lParam;
3766 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3769 /******************************************************************************
3770 * EnumSystemGeoID (KERNEL32.@)
3772 * Call a users function for every location available on the system.
3774 * PARAMS
3775 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3776 * reserved [I] Reserved, set to 0
3777 * pGeoEnumProc [I] Callback function to call for each location
3779 * RETURNS
3780 * Success: TRUE.
3781 * Failure: FALSE. Use GetLastError() to determine the cause.
3783 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3785 static const WCHAR szCountryCodeValueName[] = {
3786 'C','o','u','n','t','r','y','C','o','d','e','\0'
3788 WCHAR szNumber[10];
3789 HANDLE hKey;
3790 ULONG ulIndex = 0;
3792 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3794 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3796 SetLastError(ERROR_INVALID_PARAMETER);
3797 return FALSE;
3800 hKey = NLS_RegOpenKey( 0, szCountryListName );
3802 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3804 BOOL bContinue = TRUE;
3805 DWORD dwGeoId;
3806 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3808 if (hSubKey)
3810 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3812 TRACE("Got geoid %d\n", dwGeoId);
3814 if (!pGeoEnumProc( dwGeoId ))
3815 bContinue = FALSE;
3818 NtClose( hSubKey );
3821 if (!bContinue)
3822 break;
3824 ulIndex++;
3827 if (hKey)
3828 NtClose( hKey );
3830 return TRUE;
3833 /******************************************************************************
3834 * InvalidateNLSCache (KERNEL32.@)
3836 * Invalidate the cache of NLS values.
3838 * PARAMS
3839 * None.
3841 * RETURNS
3842 * Success: TRUE.
3843 * Failure: FALSE.
3845 BOOL WINAPI InvalidateNLSCache(void)
3847 FIXME("() stub\n");
3848 return FALSE;
3851 /******************************************************************************
3852 * GetUserGeoID (KERNEL32.@)
3854 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3856 GEOID ret = GEOID_NOT_AVAILABLE;
3857 static const WCHAR geoW[] = {'G','e','o',0};
3858 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3859 WCHAR bufferW[40], *end;
3860 DWORD count;
3861 HANDLE hkey, hSubkey = 0;
3862 UNICODE_STRING keyW;
3863 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3864 RtlInitUnicodeString( &keyW, nationW );
3865 count = sizeof(bufferW);
3867 if(!(hkey = create_registry_key())) return ret;
3869 switch( GeoClass ){
3870 case GEOCLASS_NATION:
3871 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3873 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3874 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3875 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3877 break;
3878 case GEOCLASS_REGION:
3879 FIXME("GEOCLASS_REGION not handled yet\n");
3880 break;
3883 NtClose(hkey);
3884 if (hSubkey) NtClose(hSubkey);
3885 return ret;
3888 /******************************************************************************
3889 * SetUserGeoID (KERNEL32.@)
3891 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3893 static const WCHAR geoW[] = {'G','e','o',0};
3894 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3895 static const WCHAR formatW[] = {'%','i',0};
3896 UNICODE_STRING nameW,keyW;
3897 WCHAR bufferW[10];
3898 OBJECT_ATTRIBUTES attr;
3899 HANDLE hkey;
3901 if(!(hkey = create_registry_key())) return FALSE;
3903 attr.Length = sizeof(attr);
3904 attr.RootDirectory = hkey;
3905 attr.ObjectName = &nameW;
3906 attr.Attributes = 0;
3907 attr.SecurityDescriptor = NULL;
3908 attr.SecurityQualityOfService = NULL;
3909 RtlInitUnicodeString( &nameW, geoW );
3910 RtlInitUnicodeString( &keyW, nationW );
3912 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3915 NtClose(attr.RootDirectory);
3916 return FALSE;
3919 sprintfW(bufferW, formatW, GeoID);
3920 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3921 NtClose(attr.RootDirectory);
3922 NtClose(hkey);
3923 return TRUE;
3926 typedef struct
3928 union
3930 UILANGUAGE_ENUMPROCA procA;
3931 UILANGUAGE_ENUMPROCW procW;
3932 } u;
3933 DWORD flags;
3934 LONG_PTR param;
3935 } ENUM_UILANG_CALLBACK;
3937 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3938 LPCSTR name, WORD LangID, LONG_PTR lParam )
3940 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3941 char buf[20];
3943 sprintf(buf, "%08x", (UINT)LangID);
3944 return enum_uilang->u.procA( buf, enum_uilang->param );
3947 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3948 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3950 static const WCHAR formatW[] = {'%','0','8','x',0};
3951 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3952 WCHAR buf[20];
3954 sprintfW( buf, formatW, (UINT)LangID );
3955 return enum_uilang->u.procW( buf, enum_uilang->param );
3958 /******************************************************************************
3959 * EnumUILanguagesA (KERNEL32.@)
3961 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3963 ENUM_UILANG_CALLBACK enum_uilang;
3965 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3967 if(!pUILangEnumProc) {
3968 SetLastError(ERROR_INVALID_PARAMETER);
3969 return FALSE;
3971 if(dwFlags) {
3972 SetLastError(ERROR_INVALID_FLAGS);
3973 return FALSE;
3976 enum_uilang.u.procA = pUILangEnumProc;
3977 enum_uilang.flags = dwFlags;
3978 enum_uilang.param = lParam;
3980 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3981 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3982 (LONG_PTR)&enum_uilang);
3983 return TRUE;
3986 /******************************************************************************
3987 * EnumUILanguagesW (KERNEL32.@)
3989 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3991 ENUM_UILANG_CALLBACK enum_uilang;
3993 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3996 if(!pUILangEnumProc) {
3997 SetLastError(ERROR_INVALID_PARAMETER);
3998 return FALSE;
4000 if(dwFlags) {
4001 SetLastError(ERROR_INVALID_FLAGS);
4002 return FALSE;
4005 enum_uilang.u.procW = pUILangEnumProc;
4006 enum_uilang.flags = dwFlags;
4007 enum_uilang.param = lParam;
4009 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4010 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4011 (LONG_PTR)&enum_uilang);
4012 return TRUE;
4015 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
4016 int cchData, LANGID language)
4018 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4019 return 0;
4022 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
4023 int cchData, LANGID language)
4025 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4026 return 0;
4029 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4031 LCID userlcid;
4033 TRACE("%p, %d\n", localename, buffersize);
4035 userlcid = GetUserDefaultLCID();
4036 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4039 /******************************************************************************
4040 * NormalizeString (KERNEL32.@)
4042 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4043 LPWSTR lpDstString, INT cwDstLength)
4045 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4046 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4047 return 0;
4050 /******************************************************************************
4051 * IsNormalizedString (KERNEL32.@)
4053 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4055 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4056 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4057 return FALSE;
4060 enum {
4061 BASE = 36,
4062 TMIN = 1,
4063 TMAX = 26,
4064 SKEW = 38,
4065 DAMP = 700,
4066 INIT_BIAS = 72,
4067 INIT_N = 128
4070 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4072 INT k;
4074 delta /= (firsttime ? DAMP : 2);
4075 delta += delta/numpoints;
4077 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4078 delta /= BASE-TMIN;
4079 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4082 /******************************************************************************
4083 * IdnToAscii (KERNEL32.@)
4084 * Implementation of Punycode based on RFC 3492.
4086 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4087 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4089 static const WCHAR prefixW[] = {'x','n','-','-'};
4091 WCHAR *norm_str;
4092 INT i, label_start, label_end, norm_len, out_label, out = 0;
4094 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4095 lpASCIICharStr, cchASCIIChar);
4097 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4098 if(!norm_len)
4099 return 0;
4100 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4101 if(!norm_str) {
4102 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4103 return 0;
4105 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4106 cchUnicodeChar, norm_str, norm_len);
4107 if(!norm_len) {
4108 HeapFree(GetProcessHeap(), 0, norm_str);
4109 return 0;
4112 for(label_start=0; label_start<norm_len;) {
4113 INT n = INIT_N, bias = INIT_BIAS;
4114 INT delta = 0, b = 0, h;
4116 out_label = out;
4117 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4118 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4119 if(norm_str[i] < 0x80)
4120 b++;
4121 label_end = i;
4123 if(b == label_end-label_start) {
4124 if(label_end < norm_len)
4125 b++;
4126 if(!lpASCIICharStr) {
4127 out += b;
4128 }else if(out+b <= cchASCIIChar) {
4129 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4130 out += b;
4131 }else {
4132 HeapFree(GetProcessHeap(), 0, norm_str);
4133 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4134 return 0;
4136 label_start = label_end+1;
4137 continue;
4140 if(!lpASCIICharStr) {
4141 out += 5+b; /* strlen(xn--...-) */
4142 }else if(out+5+b <= cchASCIIChar) {
4143 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4144 out += 4;
4145 for(i=label_start; i<label_end; i++)
4146 if(norm_str[i] < 0x80)
4147 lpASCIICharStr[out++] = norm_str[i];
4148 lpASCIICharStr[out++] = '-';
4149 }else {
4150 HeapFree(GetProcessHeap(), 0, norm_str);
4151 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4152 return 0;
4154 if(!b)
4155 out--;
4157 for(h=b; h<label_end-label_start;) {
4158 INT m = 0xffff, q, k;
4160 for(i=label_start; i<label_end; i++) {
4161 if(norm_str[i]>=n && m>norm_str[i])
4162 m = norm_str[i];
4164 delta += (m-n)*(h+1);
4165 n = m;
4167 for(i=label_start; i<label_end; i++) {
4168 if(norm_str[i] < n) {
4169 delta++;
4170 }else if(norm_str[i] == n) {
4171 for(q=delta, k=BASE; ; k+=BASE) {
4172 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4173 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4174 if(!lpASCIICharStr) {
4175 out++;
4176 }else if(out+1 <= cchASCIIChar) {
4177 lpASCIICharStr[out++] = disp<='z'-'a' ?
4178 'a'+disp : '0'+disp-'z'+'a'-1;
4179 }else {
4180 HeapFree(GetProcessHeap(), 0, norm_str);
4181 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4182 return 0;
4184 if(q < t)
4185 break;
4186 q = (q-t)/(BASE-t);
4188 bias = adapt(delta, h+1, h==b);
4189 delta = 0;
4190 h++;
4193 delta++;
4194 n++;
4197 if(out-out_label > 63) {
4198 HeapFree(GetProcessHeap(), 0, norm_str);
4199 SetLastError(ERROR_INVALID_NAME);
4200 return 0;
4203 if(label_end < norm_len) {
4204 if(!lpASCIICharStr) {
4205 out++;
4206 }else if(out+1 <= cchASCIIChar) {
4207 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4208 }else {
4209 HeapFree(GetProcessHeap(), 0, norm_str);
4210 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4211 return 0;
4214 label_start = label_end+1;
4217 HeapFree(GetProcessHeap(), 0, norm_str);
4218 return out;
4221 /******************************************************************************
4222 * IdnToNameprepUnicode (KERNEL32.@)
4224 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4225 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4227 enum {
4228 UNASSIGNED = 0x1,
4229 PROHIBITED = 0x2,
4230 BIDI_RAL = 0x4,
4231 BIDI_L = 0x8
4234 extern const unsigned short nameprep_char_type[];
4235 extern const WCHAR nameprep_mapping[];
4236 const WCHAR *ptr;
4237 WORD flags;
4238 WCHAR buf[64], *map_str, norm_str[64], ch;
4239 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4240 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4242 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4243 lpNameprepCharStr, cchNameprepChar);
4245 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4246 SetLastError(ERROR_INVALID_FLAGS);
4247 return 0;
4250 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4251 SetLastError(ERROR_INVALID_PARAMETER);
4252 return 0;
4255 if(cchUnicodeChar == -1)
4256 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4257 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4258 SetLastError(ERROR_INVALID_NAME);
4259 return 0;
4262 for(label_start=0; label_start<cchUnicodeChar;) {
4263 ascii_only = TRUE;
4264 for(i=label_start; i<cchUnicodeChar; i++) {
4265 ch = lpUnicodeCharStr[i];
4267 if(i!=cchUnicodeChar-1 && !ch) {
4268 SetLastError(ERROR_INVALID_NAME);
4269 return 0;
4271 /* check if ch is one of label separators defined in RFC3490 */
4272 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4273 break;
4275 if(ch > 0x7f) {
4276 ascii_only = FALSE;
4277 continue;
4280 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4281 continue;
4282 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4283 || (ch>='0' && ch<='9') || ch=='-')
4284 continue;
4286 SetLastError(ERROR_INVALID_NAME);
4287 return 0;
4289 label_end = i;
4290 /* last label may be empty */
4291 if(label_start==label_end && ch) {
4292 SetLastError(ERROR_INVALID_NAME);
4293 return 0;
4296 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4297 lpUnicodeCharStr[label_end-1]=='-')) {
4298 SetLastError(ERROR_INVALID_NAME);
4299 return 0;
4302 if(ascii_only) {
4303 /* maximal label length is 63 characters */
4304 if(label_end-label_start > 63) {
4305 SetLastError(ERROR_INVALID_NAME);
4306 return 0;
4308 if(label_end < cchUnicodeChar)
4309 label_end++;
4311 if(!lpNameprepCharStr) {
4312 out += label_end-label_start;
4313 }else if(out+label_end-label_start <= cchNameprepChar) {
4314 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4315 (label_end-label_start)*sizeof(WCHAR));
4316 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4317 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4318 out += label_end-label_start;
4319 }else {
4320 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4321 return 0;
4324 label_start = label_end;
4325 continue;
4328 map_len = 0;
4329 for(i=label_start; i<label_end; i++) {
4330 ch = lpUnicodeCharStr[i];
4331 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4332 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4334 if(!ptr[0]) map_len++;
4335 else if(!ptr[1]) map_len++;
4336 else if(!ptr[2]) map_len += 2;
4337 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4339 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4340 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4341 if(!map_str) {
4342 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4343 return 0;
4345 }else {
4346 map_str = buf;
4348 map_len = 0;
4349 for(i=label_start; i<label_end; i++) {
4350 ch = lpUnicodeCharStr[i];
4351 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4352 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4354 if(!ptr[0]) {
4355 map_str[map_len++] = ch;
4356 }else if(!ptr[1]) {
4357 map_str[map_len++] = ptr[0];
4358 }else if(!ptr[2]) {
4359 map_str[map_len++] = ptr[0];
4360 map_str[map_len++] = ptr[1];
4361 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4362 map_str[map_len++] = ptr[0];
4363 map_str[map_len++] = ptr[1];
4364 map_str[map_len++] = ptr[2];
4368 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4369 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4370 if(map_str != buf)
4371 HeapFree(GetProcessHeap(), 0, map_str);
4372 if(!norm_len) {
4373 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4374 SetLastError(ERROR_INVALID_NAME);
4375 return 0;
4378 if(label_end < cchUnicodeChar) {
4379 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4380 label_end++;
4383 if(!lpNameprepCharStr) {
4384 out += norm_len;
4385 }else if(out+norm_len <= cchNameprepChar) {
4386 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4387 out += norm_len;
4388 }else {
4389 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4390 return 0;
4393 have_bidi_ral = prohibit_bidi_ral = FALSE;
4394 mask = PROHIBITED;
4395 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4396 mask |= UNASSIGNED;
4397 for(i=0; i<norm_len; i++) {
4398 ch = norm_str[i];
4399 flags = get_table_entry( nameprep_char_type, ch );
4401 if(flags & mask) {
4402 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4403 : ERROR_NO_UNICODE_TRANSLATION);
4404 return 0;
4407 if(flags & BIDI_RAL)
4408 have_bidi_ral = TRUE;
4409 if(flags & BIDI_L)
4410 prohibit_bidi_ral = TRUE;
4413 if(have_bidi_ral) {
4414 ch = norm_str[0];
4415 flags = get_table_entry( nameprep_char_type, ch );
4416 if((flags & BIDI_RAL) == 0)
4417 prohibit_bidi_ral = TRUE;
4419 ch = norm_str[norm_len-1];
4420 flags = get_table_entry( nameprep_char_type, ch );
4421 if((flags & BIDI_RAL) == 0)
4422 prohibit_bidi_ral = TRUE;
4425 if(have_bidi_ral && prohibit_bidi_ral) {
4426 SetLastError(ERROR_INVALID_NAME);
4427 return 0;
4430 label_start = label_end;
4433 return out;
4436 /******************************************************************************
4437 * IdnToUnicode (KERNEL32.@)
4439 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4440 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4442 extern const unsigned short nameprep_char_type[];
4444 INT i, label_start, label_end, out_label, out = 0;
4445 WCHAR ch;
4447 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4448 lpUnicodeCharStr, cchUnicodeChar);
4450 for(label_start=0; label_start<cchASCIIChar;) {
4451 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4453 out_label = out;
4454 for(i=label_start; i<cchASCIIChar; i++) {
4455 ch = lpASCIICharStr[i];
4457 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4458 SetLastError(ERROR_INVALID_NAME);
4459 return 0;
4462 if(!ch || ch=='.')
4463 break;
4464 if(ch == '-')
4465 delim = i;
4467 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4468 continue;
4469 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4470 || (ch>='0' && ch<='9') || ch=='-')
4471 continue;
4473 SetLastError(ERROR_INVALID_NAME);
4474 return 0;
4476 label_end = i;
4477 /* last label may be empty */
4478 if(label_start==label_end && ch) {
4479 SetLastError(ERROR_INVALID_NAME);
4480 return 0;
4483 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4484 lpUnicodeCharStr[label_end-1]=='-')) {
4485 SetLastError(ERROR_INVALID_NAME);
4486 return 0;
4488 if(label_end-label_start > 63) {
4489 SetLastError(ERROR_INVALID_NAME);
4490 return 0;
4493 if(label_end-label_start<4 ||
4494 tolowerW(lpASCIICharStr[label_start])!='x' ||
4495 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4496 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4497 if(label_end < cchUnicodeChar)
4498 label_end++;
4500 if(!lpUnicodeCharStr) {
4501 out += label_end-label_start;
4502 }else if(out+label_end-label_start <= cchUnicodeChar) {
4503 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4504 (label_end-label_start)*sizeof(WCHAR));
4505 out += label_end-label_start;
4506 }else {
4507 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4508 return 0;
4511 label_start = label_end;
4512 continue;
4515 if(delim == label_start+3)
4516 delim++;
4517 if(!lpUnicodeCharStr) {
4518 out += delim-label_start-4;
4519 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4520 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4521 (delim-label_start-4)*sizeof(WCHAR));
4522 out += delim-label_start-4;
4523 }else {
4524 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4525 return 0;
4527 if(out != out_label)
4528 delim++;
4530 for(i=delim; i<label_end;) {
4531 old_pos = pos;
4532 w = 1;
4533 for(k=BASE; ; k+=BASE) {
4534 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4535 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4536 SetLastError(ERROR_INVALID_NAME);
4537 return 0;
4539 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4540 pos += digit*w;
4541 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4542 if(digit < t)
4543 break;
4544 w *= BASE-t;
4546 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4547 n += pos/(out-out_label+1);
4548 pos %= out-out_label+1;
4550 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4551 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4552 SetLastError(ERROR_INVALID_NAME);
4553 return 0;
4555 if(!lpUnicodeCharStr) {
4556 out++;
4557 }else if(out+1 <= cchASCIIChar) {
4558 memmove(lpUnicodeCharStr+out_label+pos+1,
4559 lpUnicodeCharStr+out_label+pos,
4560 (out-out_label-pos)*sizeof(WCHAR));
4561 lpUnicodeCharStr[out_label+pos] = n;
4562 out++;
4563 }else {
4564 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4565 return 0;
4567 pos++;
4570 if(out-out_label > 63) {
4571 SetLastError(ERROR_INVALID_NAME);
4572 return 0;
4575 if(label_end < cchASCIIChar) {
4576 if(!lpUnicodeCharStr) {
4577 out++;
4578 }else if(out+1 <= cchUnicodeChar) {
4579 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4580 }else {
4581 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4582 return 0;
4585 label_start = label_end+1;
4588 return out;