kernel32: Move the error message table to kernelbase.
[wine.git] / dlls / kernelbase / locale.c
blob997c7b6f4bcc1bd9b8e51d7988ec623a68fcac51
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 2003 Jon Griffiths
8 * Copyright 2005 Dmitry Timoshkov
9 * Copyright 2002, 2019 Alexandre Julliard
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <stdarg.h>
27 #include <stdlib.h>
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #define WINNORMALIZEAPI
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winreg.h"
35 #include "winnls.h"
36 #include "winuser.h"
37 #include "winternl.h"
38 #include "kernelbase.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(nls);
43 #define CALINFO_MAX_YEAR 2029
45 extern const unsigned int collation_table[] DECLSPEC_HIDDEN;
47 static HMODULE kernelbase_handle;
49 struct registry_entry
51 const WCHAR *value;
52 enum { NOT_CACHED, CACHED, MISSING } status;
53 WCHAR data[80];
56 static struct registry_entry entry_icalendartype = { L"iCalendarType" };
57 static struct registry_entry entry_icountry = { L"iCountry" };
58 static struct registry_entry entry_icurrdigits = { L"iCurrDigits" };
59 static struct registry_entry entry_icurrency = { L"iCurrency" };
60 static struct registry_entry entry_idigits = { L"iDigits" };
61 static struct registry_entry entry_idigitsubstitution = { L"NumShape" };
62 static struct registry_entry entry_ifirstdayofweek = { L"iFirstDayOfWeek" };
63 static struct registry_entry entry_ifirstweekofyear = { L"iFirstWeekOfYear" };
64 static struct registry_entry entry_ilzero = { L"iLZero" };
65 static struct registry_entry entry_imeasure = { L"iMeasure" };
66 static struct registry_entry entry_inegcurr = { L"iNegCurr" };
67 static struct registry_entry entry_inegnumber = { L"iNegNumber" };
68 static struct registry_entry entry_ipapersize = { L"iPaperSize" };
69 static struct registry_entry entry_s1159 = { L"s1159" };
70 static struct registry_entry entry_s2359 = { L"s2359" };
71 static struct registry_entry entry_scurrency = { L"sCurrency" };
72 static struct registry_entry entry_sdecimal = { L"sDecimal" };
73 static struct registry_entry entry_sgrouping = { L"sGrouping" };
74 static struct registry_entry entry_slist = { L"sList" };
75 static struct registry_entry entry_slongdate = { L"sLongDate" };
76 static struct registry_entry entry_smondecimalsep = { L"sMonDecimalSep" };
77 static struct registry_entry entry_smongrouping = { L"sMonGrouping" };
78 static struct registry_entry entry_smonthousandsep = { L"sMonThousandSep" };
79 static struct registry_entry entry_snativedigits = { L"sNativeDigits" };
80 static struct registry_entry entry_snegativesign = { L"sNegativeSign" };
81 static struct registry_entry entry_spositivesign = { L"sPositiveSign" };
82 static struct registry_entry entry_sshortdate = { L"sShortDate" };
83 static struct registry_entry entry_sshorttime = { L"sShortTime" };
84 static struct registry_entry entry_sthousand = { L"sThousand" };
85 static struct registry_entry entry_stimeformat = { L"sTimeFormat" };
86 static struct registry_entry entry_syearmonth = { L"sYearMonth" };
89 static const struct { UINT cp; const WCHAR *name; } codepage_names[] =
91 { 37, L"IBM EBCDIC US Canada" },
92 { 424, L"IBM EBCDIC Hebrew" },
93 { 437, L"OEM United States" },
94 { 500, L"IBM EBCDIC International" },
95 { 708, L"Arabic ASMO" },
96 { 720, L"Arabic (Transparent ASMO)" },
97 { 737, L"OEM Greek 437G" },
98 { 775, L"OEM Baltic" },
99 { 850, L"OEM Multilingual Latin 1" },
100 { 852, L"OEM Slovak Latin 2" },
101 { 855, L"OEM Cyrillic" },
102 { 856, L"Hebrew PC" },
103 { 857, L"OEM Turkish" },
104 { 860, L"OEM Portuguese" },
105 { 861, L"OEM Icelandic" },
106 { 862, L"OEM Hebrew" },
107 { 863, L"OEM Canadian French" },
108 { 864, L"OEM Arabic" },
109 { 865, L"OEM Nordic" },
110 { 866, L"OEM Russian" },
111 { 869, L"OEM Greek" },
112 { 874, L"ANSI/OEM Thai" },
113 { 875, L"IBM EBCDIC Greek" },
114 { 878, L"Russian KOI8" },
115 { 932, L"ANSI/OEM Japanese Shift-JIS" },
116 { 936, L"ANSI/OEM Simplified Chinese GBK" },
117 { 949, L"ANSI/OEM Korean Unified Hangul" },
118 { 950, L"ANSI/OEM Traditional Chinese Big5" },
119 { 1006, L"IBM Arabic" },
120 { 1026, L"IBM EBCDIC Latin 5 Turkish" },
121 { 1250, L"ANSI Eastern Europe" },
122 { 1251, L"ANSI Cyrillic" },
123 { 1252, L"ANSI Latin 1" },
124 { 1253, L"ANSI Greek" },
125 { 1254, L"ANSI Turkish" },
126 { 1255, L"ANSI Hebrew" },
127 { 1256, L"ANSI Arabic" },
128 { 1257, L"ANSI Baltic" },
129 { 1258, L"ANSI/OEM Viet Nam" },
130 { 1361, L"Korean Johab" },
131 { 10000, L"Mac Roman" },
132 { 10001, L"Mac Japanese" },
133 { 10002, L"Mac Traditional Chinese" },
134 { 10003, L"Mac Korean" },
135 { 10004, L"Mac Arabic" },
136 { 10005, L"Mac Hebrew" },
137 { 10006, L"Mac Greek" },
138 { 10007, L"Mac Cyrillic" },
139 { 10008, L"Mac Simplified Chinese" },
140 { 10010, L"Mac Romanian" },
141 { 10017, L"Mac Ukrainian" },
142 { 10021, L"Mac Thai" },
143 { 10029, L"Mac Latin 2" },
144 { 10079, L"Mac Icelandic" },
145 { 10081, L"Mac Turkish" },
146 { 10082, L"Mac Croatian" },
147 { 20127, L"US-ASCII (7bit)" },
148 { 20866, L"Russian KOI8" },
149 { 20932, L"EUC-JP" },
150 { 20949, L"Korean Wansung" },
151 { 21866, L"Ukrainian KOI8" },
152 { 28591, L"ISO 8859-1 Latin 1" },
153 { 28592, L"ISO 8859-2 Latin 2 (East European)" },
154 { 28593, L"ISO 8859-3 Latin 3 (South European)" },
155 { 28594, L"ISO 8859-4 Latin 4 (Baltic old)" },
156 { 28595, L"ISO 8859-5 Cyrillic" },
157 { 28596, L"ISO 8859-6 Arabic" },
158 { 28597, L"ISO 8859-7 Greek" },
159 { 28598, L"ISO 8859-8 Hebrew" },
160 { 28599, L"ISO 8859-9 Latin 5 (Turkish)" },
161 { 28600, L"ISO 8859-10 Latin 6 (Nordic)" },
162 { 28601, L"ISO 8859-11 Latin (Thai)" },
163 { 28603, L"ISO 8859-13 Latin 7 (Baltic)" },
164 { 28604, L"ISO 8859-14 Latin 8 (Celtic)" },
165 { 28605, L"ISO 8859-15 Latin 9 (Euro)" },
166 { 28606, L"ISO 8859-16 Latin 10 (Balkan)" },
167 { 65000, L"Unicode (UTF-7)" },
168 { 65001, L"Unicode (UTF-8)" }
171 /* Unicode expanded ligatures */
172 static const WCHAR ligatures[][5] =
174 { 0x00c6, 'A','E',0 },
175 { 0x00de, 'T','H',0 },
176 { 0x00df, 's','s',0 },
177 { 0x00e6, 'a','e',0 },
178 { 0x00fe, 't','h',0 },
179 { 0x0132, 'I','J',0 },
180 { 0x0133, 'i','j',0 },
181 { 0x0152, 'O','E',0 },
182 { 0x0153, 'o','e',0 },
183 { 0x01c4, 'D',0x017d,0 },
184 { 0x01c5, 'D',0x017e,0 },
185 { 0x01c6, 'd',0x017e,0 },
186 { 0x01c7, 'L','J',0 },
187 { 0x01c8, 'L','j',0 },
188 { 0x01c9, 'l','j',0 },
189 { 0x01ca, 'N','J',0 },
190 { 0x01cb, 'N','j',0 },
191 { 0x01cc, 'n','j',0 },
192 { 0x01e2, 0x0100,0x0112,0 },
193 { 0x01e3, 0x0101,0x0113,0 },
194 { 0x01f1, 'D','Z',0 },
195 { 0x01f2, 'D','z',0 },
196 { 0x01f3, 'd','z',0 },
197 { 0x01fc, 0x00c1,0x00c9,0 },
198 { 0x01fd, 0x00e1,0x00e9,0 },
199 { 0x05f0, 0x05d5,0x05d5,0 },
200 { 0x05f1, 0x05d5,0x05d9,0 },
201 { 0x05f2, 0x05d9,0x05d9,0 },
202 { 0xfb00, 'f','f',0 },
203 { 0xfb01, 'f','i',0 },
204 { 0xfb02, 'f','l',0 },
205 { 0xfb03, 'f','f','i',0 },
206 { 0xfb04, 'f','f','l',0 },
207 { 0xfb05, 0x017f,'t',0 },
208 { 0xfb06, 's','t',0 },
212 static const struct geo_id
214 GEOID id;
215 WCHAR latitude[12];
216 WCHAR longitude[12];
217 GEOCLASS class;
218 GEOID parent;
219 WCHAR iso2[4];
220 WCHAR iso3[4];
221 USHORT uncode;
222 USHORT dialcode;
223 WCHAR currcode[4];
224 WCHAR currsymbol[8];
225 } *geo_ids;
227 static const struct geo_index
229 WCHAR name[4];
230 UINT idx;
231 } *geo_index;
233 static unsigned int geo_ids_count;
234 static unsigned int geo_index_count;
236 /* NLS normalization file */
237 struct norm_table
239 WCHAR name[13]; /* 00 file name */
240 USHORT checksum[3]; /* 1a checksum? */
241 USHORT version[4]; /* 20 Unicode version */
242 USHORT form; /* 28 normalization form */
243 USHORT len_factor; /* 2a factor for length estimates */
244 USHORT unknown1; /* 2c */
245 USHORT decomp_size; /* 2e decomposition hash size */
246 USHORT comp_size; /* 30 composition hash size */
247 USHORT unknown2; /* 32 */
248 USHORT classes; /* 34 combining classes table offset */
249 USHORT props_level1; /* 36 char properties table level 1 offset */
250 USHORT props_level2; /* 38 char properties table level 2 offset */
251 USHORT decomp_hash; /* 3a decomposition hash table offset */
252 USHORT decomp_map; /* 3c decomposition character map table offset */
253 USHORT decomp_seq; /* 3e decomposition character sequences offset */
254 USHORT comp_hash; /* 40 composition hash table offset */
255 USHORT comp_seq; /* 42 composition character sequences offset */
256 /* BYTE[] combining class values */
257 /* BYTE[0x2200] char properties index level 1 */
258 /* BYTE[] char properties index level 2 */
259 /* WORD[] decomposition hash table */
260 /* WORD[] decomposition character map */
261 /* WORD[] decomposition character sequences */
262 /* WORD[] composition hash table */
263 /* WORD[] composition character sequences */
266 static NLSTABLEINFO nls_info;
267 static UINT unix_cp = CP_UTF8;
268 static UINT mac_cp = 10000;
269 static LCID system_lcid;
270 static LCID user_lcid;
271 static HKEY intl_key;
272 static HKEY nls_key;
273 static HKEY tz_key;
274 static const NLS_LOCALE_LCID_INDEX *lcids_index;
275 static const NLS_LOCALE_LCNAME_INDEX *lcnames_index;
276 static const NLS_LOCALE_HEADER *locale_table;
277 static const WCHAR *locale_strings;
278 static const NLS_LOCALE_DATA *system_locale;
279 static const NLS_LOCALE_DATA *user_locale;
281 static CPTABLEINFO codepages[128];
282 static unsigned int nb_codepages;
284 static struct norm_table *norm_info;
286 struct sortguid
288 GUID id; /* sort GUID */
289 DWORD flags; /* flags */
290 DWORD compr; /* offset to compression table */
291 DWORD except; /* exception table offset in sortkey table */
292 DWORD ling_except; /* exception table offset for linguistic casing */
293 DWORD casemap; /* linguistic casemap table offset */
296 #define FLAG_HAS_3_BYTE_WEIGHTS 0x01
297 #define FLAG_REVERSEDIACRITICS 0x10
298 #define FLAG_DOUBLECOMPRESSION 0x20
299 #define FLAG_INVERSECASING 0x40
301 static const struct sortguid *current_locale_sort;
303 static const GUID default_sort_guid = { 0x00000001, 0x57ee, 0x1e5c, { 0x00, 0xb4, 0xd0, 0x00, 0x0b, 0xb1, 0xe1, 0x1e }};
305 static struct
307 DWORD *keys; /* sortkey table, indexed by char */
308 USHORT *casemap; /* casemap table, in l_intl.nls format */
309 WORD *ctypes; /* CT_CTYPE1,2,3 values */
310 BYTE *ctype_idx; /* index to map char to ctypes array entry */
311 DWORD version; /* NLS version */
312 DWORD guid_count; /* number of sort GUIDs */
313 struct sortguid *guids; /* table of sort GUIDs */
314 } sort;
316 static CRITICAL_SECTION locale_section;
317 static CRITICAL_SECTION_DEBUG critsect_debug =
319 0, 0, &locale_section,
320 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
321 0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") }
323 static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
326 static void load_locale_nls(void)
328 struct
330 UINT ctypes;
331 UINT unknown1;
332 UINT unknown2;
333 UINT unknown3;
334 UINT locales;
335 UINT charmaps;
336 UINT geoids;
337 UINT scripts;
338 } *header;
339 struct geo_header
341 WCHAR signature[4]; /* L"geo" */
342 UINT total_size;
343 UINT ids_offset;
344 UINT ids_count;
345 UINT index_offset;
346 UINT index_count;
347 } *geo_header;
349 LCID lcid;
350 LARGE_INTEGER dummy;
352 RtlGetLocaleFileMappingAddress( (void **)&header, &lcid, &dummy );
353 locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales);
354 lcids_index = (const NLS_LOCALE_LCID_INDEX *)((char *)locale_table + locale_table->lcids_offset);
355 lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset);
356 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
357 geo_header = (struct geo_header *)((char *)header + header->geoids);
358 geo_ids = (const struct geo_id *)((char *)geo_header + geo_header->ids_offset);
359 geo_index = (const struct geo_index *)((char *)geo_header + geo_header->index_offset);
360 geo_ids_count = geo_header->ids_count;
361 geo_index_count = geo_header->index_count;
365 static void init_sortkeys( DWORD *ptr )
367 WORD *ctype;
368 DWORD *table;
370 sort.keys = (DWORD *)((char *)ptr + ptr[0]);
371 sort.casemap = (USHORT *)((char *)ptr + ptr[1]);
373 ctype = (WORD *)((char *)ptr + ptr[2]);
374 sort.ctypes = ctype + 2;
375 sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2;
377 table = (DWORD *)((char *)ptr + ptr[3]);
378 sort.version = table[0];
379 sort.guid_count = table[1];
380 sort.guids = (struct sortguid *)(table + 2);
384 static const struct sortguid *find_sortguid( const GUID *guid )
386 int pos, ret, min = 0, max = sort.guid_count - 1;
388 while (min <= max)
390 pos = (min + max) / 2;
391 ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) );
392 if (!ret) return &sort.guids[pos];
393 if (ret > 0) min = pos + 1;
394 else max = pos - 1;
396 ERR( "no sort found for %s\n", debugstr_guid( guid ));
397 return NULL;
401 static const struct sortguid *get_language_sort( const WCHAR *locale )
403 WCHAR *p, *end, buffer[LOCALE_NAME_MAX_LENGTH], guidstr[39];
404 const struct sortguid *ret;
405 UNICODE_STRING str;
406 GUID guid;
407 HKEY key = 0;
408 DWORD size, type;
410 if (locale == LOCALE_NAME_USER_DEFAULT)
412 if (current_locale_sort) return current_locale_sort;
413 GetUserDefaultLocaleName( buffer, ARRAY_SIZE( buffer ));
415 else lstrcpynW( buffer, locale, LOCALE_NAME_MAX_LENGTH );
417 if (buffer[0] && !RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key ))
419 for (;;)
421 size = sizeof(guidstr);
422 if (!RegQueryValueExW( key, buffer, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ)
424 RtlInitUnicodeString( &str, guidstr );
425 if (!RtlGUIDFromString( &str, &guid ))
427 ret = find_sortguid( &guid );
428 goto done;
430 break;
432 for (p = end = buffer; *p; p++) if (*p == '-' || *p == '_') end = p;
433 if (end == buffer) break;
434 *end = 0;
437 ret = find_sortguid( &default_sort_guid );
438 done:
439 RegCloseKey( key );
440 return ret;
444 static const NLS_LOCALE_DATA *get_locale_data( UINT idx )
446 ULONG offset = locale_table->locales_offset + idx * locale_table->locale_size;
447 return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
451 static int compare_locale_names( const WCHAR *n1, const WCHAR *n2 )
453 for (;;)
455 WCHAR ch1 = *n1++;
456 WCHAR ch2 = *n2++;
457 if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A';
458 else if (ch1 == '_') ch1 = '-';
459 if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A';
460 else if (ch2 == '_') ch2 = '-';
461 if (!ch1 || ch1 != ch2) return ch1 - ch2;
466 static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry( const WCHAR *name )
468 int min = 0, max = locale_table->nb_lcnames - 1;
470 while (min <= max)
472 int res, pos = (min + max) / 2;
473 const WCHAR *str = locale_strings + lcnames_index[pos].name;
474 res = compare_locale_names( name, str + 1 );
475 if (res < 0) max = pos - 1;
476 else if (res > 0) min = pos + 1;
477 else return &lcnames_index[pos];
479 return NULL;
483 static const NLS_LOCALE_LCID_INDEX *find_lcid_entry( LCID lcid )
485 int min = 0, max = locale_table->nb_lcids - 1;
487 while (min <= max)
489 int pos = (min + max) / 2;
490 if (lcid < lcids_index[pos].id) max = pos - 1;
491 else if (lcid > lcids_index[pos].id) min = pos + 1;
492 else return &lcids_index[pos];
494 return NULL;
498 static const struct geo_id *find_geo_id_entry( GEOID id )
500 int min = 0, max = geo_ids_count - 1;
502 while (min <= max)
504 int pos = (min + max) / 2;
505 if (id < geo_ids[pos].id) max = pos - 1;
506 else if (id > geo_ids[pos].id) min = pos + 1;
507 else return &geo_ids[pos];
509 return NULL;
513 static const struct geo_id *find_geo_name_entry( const WCHAR *name )
515 int min = 0, max = geo_index_count - 1;
517 while (min <= max)
519 int res, pos = (min + max) / 2;
520 res = wcsicmp( name, geo_index[pos].name );
521 if (res < 0) max = pos - 1;
522 else if (res > 0) min = pos + 1;
523 else return &geo_ids[geo_index[pos].idx];
525 return NULL;
529 static const NLS_LOCALE_DATA *get_locale_by_name( const WCHAR *name, LCID *lcid )
531 const NLS_LOCALE_LCNAME_INDEX *entry;
533 if (name == LOCALE_NAME_USER_DEFAULT)
535 *lcid = user_lcid;
536 return user_locale;
538 if (!(entry = find_lcname_entry( name ))) return NULL;
539 *lcid = entry->id;
540 return get_locale_data( entry->idx );
544 static const NLS_LOCALE_DATA *get_locale_by_id( LCID *lcid, DWORD flags )
546 const NLS_LOCALE_LCID_INDEX *entry;
547 const NLS_LOCALE_DATA *locale;
549 switch (*lcid)
551 case LOCALE_SYSTEM_DEFAULT:
552 *lcid = system_lcid;
553 return system_locale;
554 case LOCALE_NEUTRAL:
555 case LOCALE_USER_DEFAULT:
556 case LOCALE_CUSTOM_DEFAULT:
557 *lcid = user_lcid;
558 return user_locale;
559 default:
560 if (!(entry = find_lcid_entry( *lcid ))) return NULL;
561 locale = get_locale_data( entry->idx );
562 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
563 locale = get_locale_by_name( locale_strings + locale->ssortlocale + 1, lcid );
564 return locale;
569 static int locale_return_data( const WCHAR *data, int datalen, LCTYPE type, WCHAR *buffer, int len )
571 if (type & LOCALE_RETURN_NUMBER)
573 SetLastError( ERROR_INVALID_FLAGS );
574 return 0;
577 if (!len) return datalen;
578 if (datalen > len)
580 SetLastError( ERROR_INSUFFICIENT_BUFFER );
581 return 0;
583 memcpy( buffer, data, datalen * sizeof(WCHAR) );
584 return datalen;
588 static BOOL set_registry_entry( struct registry_entry *entry, const WCHAR *data )
590 DWORD size = (wcslen(data) + 1) * sizeof(WCHAR);
591 LSTATUS ret;
593 if (size > sizeof(entry->data))
595 SetLastError( ERROR_INVALID_FLAGS );
596 return FALSE;
598 TRACE( "setting %s to %s\n", debugstr_w(entry->value), debugstr_w(data) );
600 RtlEnterCriticalSection( &locale_section );
601 if (!(ret = RegSetValueExW( intl_key, entry->value, 0, REG_SZ, (BYTE *)data, size )))
603 wcscpy( entry->data, data );
604 entry->status = CACHED;
606 RtlLeaveCriticalSection( &locale_section );
607 if (ret) SetLastError( ret );
608 return !ret;
612 static int locale_return_reg_string( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
614 DWORD size;
615 LRESULT res;
616 int ret = -1;
618 if (type & LOCALE_NOUSEROVERRIDE) return -1;
620 RtlEnterCriticalSection( &locale_section );
621 switch (entry->status)
623 case NOT_CACHED:
624 size = sizeof(entry->data);
625 res = RegQueryValueExW( intl_key, entry->value, NULL, NULL, (BYTE *)entry->data, &size );
626 if (res)
628 entry->status = MISSING;
629 break;
631 entry->status = CACHED;
632 /* fall through */
633 case CACHED:
634 ret = locale_return_data( entry->data, wcslen(entry->data) + 1, type, buffer, len );
635 break;
636 case MISSING:
637 break;
639 RtlLeaveCriticalSection( &locale_section );
640 return ret;
644 static int locale_return_string( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
646 return locale_return_data( locale_strings + pos + 1, locale_strings[pos] + 1, type, buffer, len );
650 static int locale_return_number( UINT val, LCTYPE type, WCHAR *buffer, int len )
652 int ret;
653 WCHAR tmp[80];
655 if (!(type & LOCALE_RETURN_NUMBER))
657 switch (LOWORD(type))
659 case LOCALE_ILANGUAGE:
660 case LOCALE_IDEFAULTLANGUAGE:
661 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%04x", val ) + 1;
662 break;
663 case LOCALE_IDEFAULTEBCDICCODEPAGE:
664 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", val ) + 1;
665 break;
666 default:
667 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val ) + 1;
668 break;
671 else ret = sizeof(UINT) / sizeof(WCHAR);
673 if (!len) return ret;
674 if (ret > len)
676 SetLastError( ERROR_INSUFFICIENT_BUFFER );
677 return 0;
680 if (type & LOCALE_RETURN_NUMBER) memcpy( buffer, &val, sizeof(val) );
681 else wcscpy( buffer, tmp );
683 return ret;
687 static int locale_return_reg_number( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
689 int ret, val;
690 WCHAR *end, tmp[80];
692 if (type & LOCALE_RETURN_NUMBER)
694 ret = locale_return_reg_string( entry, type & ~LOCALE_RETURN_NUMBER, tmp, ARRAY_SIZE( tmp ));
695 if (ret == -1) return ret;
696 val = wcstol( tmp, &end, 10 );
697 if (*end) /* invalid number */
699 SetLastError( ERROR_INVALID_FLAGS );
700 return 0;
702 return locale_return_number( val, type, buffer, len );
704 return locale_return_reg_string( entry, type, buffer, len );
708 static int locale_return_grouping( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
710 WORD i, count = locale_strings[pos];
711 const WCHAR *str = locale_strings + pos + 1;
712 int ret;
714 if (type & LOCALE_RETURN_NUMBER)
716 SetLastError( ERROR_INVALID_FLAGS );
717 return 0;
719 ret = 2 * count;
720 if (str[count - 1]) ret += 2; /* for final zero */
722 if (!len) return ret;
723 if (ret > len)
725 SetLastError( ERROR_INSUFFICIENT_BUFFER );
726 return 0;
728 for (i = 0; i < count; i++)
730 if (!str[i]) /* explicit null termination */
732 buffer[-1] = 0;
733 return ret;
735 *buffer++ = '0' + str[i];
736 *buffer++ = ';';
738 *buffer++ = '0';
739 *buffer = 0;
740 return ret;
744 static int locale_return_strarray( DWORD pos, WORD idx, LCTYPE type, WCHAR *buffer, int len )
746 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
747 WORD count = locale_strings[pos];
749 return locale_return_string( idx < count ? array[idx] : 0, type, buffer, len );
753 static int locale_return_strarray_concat( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
755 WORD i, count = locale_strings[pos];
756 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
757 int ret;
759 if (type & LOCALE_RETURN_NUMBER)
761 SetLastError( ERROR_INVALID_FLAGS );
762 return 0;
764 for (i = 0, ret = 1; i < count; i++) ret += locale_strings[array[i]];
766 if (!len) return ret;
767 if (ret > len)
769 SetLastError( ERROR_INSUFFICIENT_BUFFER );
770 return 0;
772 for (i = 0; i < count; i++)
774 memcpy( buffer, locale_strings + array[i] + 1, locale_strings[array[i]] * sizeof(WCHAR) );
775 buffer += locale_strings[array[i]];
777 *buffer = 0;
778 return ret;
782 /* find the first format char in a format string */
783 static WCHAR *find_format( WCHAR *str, const WCHAR *accept )
785 for ( ; *str; str++)
787 if (*str == '\'')
789 if (!(str = wcschr( str + 1, '\'' ))) return NULL;
791 else if (wcschr( accept, *str ))
793 /* ignore "ddd" and "dddd" */
794 if (str[0] != 'd' || str[1] != 'd' || str[2] != 'd') return str;
795 str += 2;
796 while (str[1] == 'd') str++;
799 return NULL;
803 /* replace the separator in a date/time format string */
804 static WCHAR *locale_replace_separator( WCHAR *buffer, const WCHAR *sep )
806 UINT pos = 0;
807 WCHAR res[80];
808 WCHAR *next, *str = find_format( buffer, L"dMyHhms" );
810 if (!str) return buffer;
811 pos = str - buffer;
812 memcpy( res, buffer, pos * sizeof(WCHAR) );
813 for (;;)
815 res[pos++] = *str++;
816 while (str[0] == str[-1]) res[pos++] = *str++; /* copy repeated chars */
817 if (!(next = find_format( str, L"dMyHhms" ))) break;
818 wcscpy( res + pos, sep );
819 pos += wcslen(sep);
820 str = next;
822 wcscpy( res + pos, str );
823 return wcscpy( buffer, res );
827 /* FIXME: hardcoded, sortname is apparently not available in locale.nls */
828 static const WCHAR *get_locale_sortname( LCID lcid )
830 switch (PRIMARYLANGID( lcid ))
832 case LANG_CHINESE:
833 switch (SORTIDFROMLCID( lcid ))
835 case SORT_CHINESE_PRCP:
836 switch (SUBLANGID( lcid ))
838 case SUBLANG_CHINESE_TRADITIONAL:
839 case SUBLANG_CHINESE_HONGKONG:
840 case 0x1f:
841 return L"Stroke Count";
842 default:
843 return L"Pronunciation";
845 case SORT_CHINESE_UNICODE: return L"Unicode";
846 case SORT_CHINESE_PRC: return L"Stroke Count";
847 case SORT_CHINESE_BOPOMOFO: return L"Bopomofo";
848 case SORT_CHINESE_RADICALSTROKE: return L"Radical/Stroke";
849 case 5: return L"Surname";
851 break;
853 case LANG_GEORGIAN:
854 if (SORTIDFROMLCID( lcid ) == SORT_GEORGIAN_MODERN) return L"Modern";
855 return L"Traditional";
857 case LANG_GERMAN:
858 switch (SUBLANGID( lcid ))
860 case SUBLANG_NEUTRAL:
861 case SUBLANG_DEFAULT:
862 if (SORTIDFROMLCID( lcid ) == SORT_GERMAN_PHONE_BOOK) return L"Phone Book (DIN)";
863 return L"Dictionary";
865 break;
867 case LANG_HUNGARIAN:
868 if (SORTIDFROMLCID( lcid ) == SORT_HUNGARIAN_TECHNICAL) return L"Technical";
869 break;
871 case LANG_INVARIANT:
872 if (SORTIDFROMLCID( lcid ) == SORT_INVARIANT_MATH) return L"Default";
873 return L"Maths Alphanumerics";
875 case LANG_JAPANESE:
876 switch (SORTIDFROMLCID( lcid ))
878 case SORT_JAPANESE_XJIS: return L"XJIS";
879 case SORT_JAPANESE_UNICODE: return L"Unicode";
880 case SORT_JAPANESE_RADICALSTROKE: return L"Radical/Stroke";
882 break;
884 case LANG_KOREAN:
885 if (SORTIDFROMLCID( lcid ) == SORT_KOREAN_UNICODE) return L"Unicode";
886 return L"Dictionary";
888 case LANG_SPANISH:
889 switch (SUBLANGID( lcid ))
891 case SUBLANG_NEUTRAL:
892 case SUBLANG_SPANISH_MODERN:
893 return L"International";
894 case SUBLANG_DEFAULT:
895 return L"Traditional";
897 break;
899 return L"Default";
903 /* get locale information from the locale.nls file */
904 static int get_locale_info( const NLS_LOCALE_DATA *locale, LCID lcid, LCTYPE type,
905 WCHAR *buffer, int len )
907 static const WCHAR spermille[] = { 0x2030, 0 }; /* this one seems hardcoded */
908 static const BYTE ipossignposn[] = { 3, 3, 4, 2, 1, 1, 3, 4, 1, 3, 4, 2, 4, 3, 3, 1 };
909 static const BYTE inegsignposn[] = { 0, 3, 4, 2, 0, 1, 3, 4, 1, 3, 4, 2, 4, 3, 0, 0 };
910 static const BYTE inegsymprecedes[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, };
911 const WCHAR *sort;
912 WCHAR *str, *end, tmp[80];
913 UINT val;
914 int ret;
916 if (locale != user_locale) type |= LOCALE_NOUSEROVERRIDE;
918 switch (LOWORD(type))
920 case LOCALE_ILANGUAGE:
921 /* return default language for neutral locales */
922 val = locale->inotneutral ? locale->ilanguage : locale->idefaultlanguage;
923 return locale_return_number( val, type, buffer, len );
925 case LOCALE_SLOCALIZEDDISPLAYNAME:
926 /* FIXME: localization */
927 return locale_return_string( locale->sengdisplayname, type, buffer, len );
929 case LOCALE_SABBREVLANGNAME:
930 return locale_return_string( locale->sabbrevlangname, type, buffer, len );
932 case LOCALE_SNATIVELANGNAME:
933 return locale_return_string( locale->snativelangname, type, buffer, len );
935 case LOCALE_ICOUNTRY:
936 if ((ret = locale_return_reg_number( &entry_icountry, type, buffer, len )) != -1) return ret;
937 return locale_return_number( locale->icountry, type, buffer, len );
939 case LOCALE_SLOCALIZEDCOUNTRYNAME:
940 /* FIXME: localization */
941 return locale_return_string( locale->sengcountry, type, buffer, len );
943 case LOCALE_SABBREVCTRYNAME:
944 return locale_return_string( locale->sabbrevctryname, type, buffer, len );
946 case LOCALE_SNATIVECTRYNAME:
947 return locale_return_string( locale->snativectryname, type, buffer, len );
949 case LOCALE_IDEFAULTLANGUAGE:
950 return locale_return_number( locale->idefaultlanguage, type, buffer, len );
952 case LOCALE_IDEFAULTCOUNTRY:
953 return locale_return_number( locale->icountry, type, buffer, len );
955 case LOCALE_IDEFAULTCODEPAGE:
956 val = locale->idefaultcodepage == CP_UTF8 ? CP_OEMCP : locale->idefaultcodepage;
957 return locale_return_number( val, type, buffer, len );
959 case LOCALE_SLIST:
960 if ((ret = locale_return_reg_string( &entry_slist, type, buffer, len )) != -1) return ret;
961 return locale_return_string( locale->slist, type, buffer, len );
963 case LOCALE_IMEASURE:
964 if ((ret = locale_return_reg_number( &entry_imeasure, type, buffer, len )) != -1) return ret;
965 return locale_return_number( locale->imeasure, type, buffer, len );
967 case LOCALE_SDECIMAL:
968 if ((ret = locale_return_reg_string( &entry_sdecimal, type, buffer, len )) != -1) return ret;
969 return locale_return_string( locale->sdecimal, type, buffer, len );
971 case LOCALE_STHOUSAND:
972 if ((ret = locale_return_reg_string( &entry_sthousand, type, buffer, len )) != -1) return ret;
973 return locale_return_string( locale->sthousand, type, buffer, len );
975 case LOCALE_SGROUPING:
976 if ((ret = locale_return_reg_string( &entry_sgrouping, type, buffer, len )) != -1) return ret;
977 return locale_return_grouping( locale->sgrouping, type, buffer, len );
979 case LOCALE_IDIGITS:
980 if ((ret = locale_return_reg_number( &entry_idigits, type, buffer, len )) != -1) return ret;
981 return locale_return_number( locale->idigits, type, buffer, len );
983 case LOCALE_ILZERO:
984 if ((ret = locale_return_reg_number( &entry_ilzero, type, buffer, len )) != -1) return ret;
985 return locale_return_number( locale->ilzero, type, buffer, len );
987 case LOCALE_SNATIVEDIGITS:
988 if ((ret = locale_return_reg_string( &entry_snativedigits, type, buffer, len )) != -1) return ret;
989 return locale_return_strarray_concat( locale->snativedigits, type, buffer, len );
991 case LOCALE_SCURRENCY:
992 if ((ret = locale_return_reg_string( &entry_scurrency, type, buffer, len )) != -1) return ret;
993 return locale_return_string( locale->scurrency, type, buffer, len );
995 case LOCALE_SINTLSYMBOL:
996 return locale_return_string( locale->sintlsymbol, type, buffer, len );
998 case LOCALE_SMONDECIMALSEP:
999 if ((ret = locale_return_reg_string( &entry_smondecimalsep, type, buffer, len )) != -1) return ret;
1000 return locale_return_string( locale->smondecimalsep, type, buffer, len );
1002 case LOCALE_SMONTHOUSANDSEP:
1003 if ((ret = locale_return_reg_string( &entry_smonthousandsep, type, buffer, len )) != -1) return ret;
1004 return locale_return_string( locale->smonthousandsep, type, buffer, len );
1006 case LOCALE_SMONGROUPING:
1007 if ((ret = locale_return_reg_string( &entry_smongrouping, type, buffer, len )) != -1) return ret;
1008 return locale_return_grouping( locale->smongrouping, type, buffer, len );
1010 case LOCALE_ICURRDIGITS:
1011 case LOCALE_IINTLCURRDIGITS:
1012 if ((ret = locale_return_reg_number( &entry_icurrdigits, type, buffer, len )) != -1) return ret;
1013 return locale_return_number( locale->icurrdigits, type, buffer, len );
1015 case LOCALE_ICURRENCY:
1016 if ((ret = locale_return_reg_number( &entry_icurrency, type, buffer, len )) != -1) return ret;
1017 return locale_return_number( locale->icurrency, type, buffer, len );
1019 case LOCALE_INEGCURR:
1020 if ((ret = locale_return_reg_number( &entry_inegcurr, type, buffer, len )) != -1) return ret;
1021 return locale_return_number( locale->inegcurr, type, buffer, len );
1023 case LOCALE_SDATE:
1024 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1025 tmp, ARRAY_SIZE( tmp ))) break;
1026 if (!(str = find_format( tmp, L"dMy" ))) break;
1027 while (str[1] == str[0]) str++; /* skip repeated chars */
1028 if (!(end = find_format( ++str, L"dMy" ))) break;
1029 *end++ = 0;
1030 return locale_return_data( str, end - str, type, buffer, len );
1032 case LOCALE_STIME:
1033 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1034 tmp, ARRAY_SIZE( tmp ))) break;
1035 if (!(str = find_format( tmp, L"Hhms" ))) break;
1036 while (str[1] == str[0]) str++; /* skip repeated chars */
1037 if (!(end = find_format( ++str, L"Hhms" ))) break;
1038 *end++ = 0;
1039 return locale_return_data( str, end - str, type, buffer, len );
1041 case LOCALE_SSHORTDATE:
1042 if ((ret = locale_return_reg_string( &entry_sshortdate, type, buffer, len )) != -1) return ret;
1043 return locale_return_strarray( locale->sshortdate, 0, type, buffer, len );
1045 case LOCALE_SLONGDATE:
1046 if ((ret = locale_return_reg_string( &entry_slongdate, type, buffer, len )) != -1) return ret;
1047 return locale_return_strarray( locale->slongdate, 0, type, buffer, len );
1049 case LOCALE_IDATE:
1050 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1051 tmp, ARRAY_SIZE( tmp ))) break;
1052 /* if both year and day are found before month, the last one takes precedence */
1053 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1055 if (*str == 'M') break;
1056 val = (*str == 'y' ? 2 : 1);
1058 return locale_return_number( val, type, buffer, len );
1060 case LOCALE_ILDATE:
1061 if (!get_locale_info( locale, lcid, LOCALE_SLONGDATE | (type & LOCALE_NOUSEROVERRIDE),
1062 tmp, ARRAY_SIZE( tmp ))) break;
1063 /* if both year and day are found before month, the last one takes precedence */
1064 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1066 if (*str == 'M') break;
1067 val = (*str == 'y' ? 2 : 1);
1069 return locale_return_number( val, type, buffer, len );
1071 case LOCALE_ITIME:
1072 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1073 tmp, ARRAY_SIZE( tmp ))) break;
1074 if (!(str = find_format( tmp, L"Hh" ))) break;
1075 return locale_return_number( *str == 'H', type, buffer, len );
1077 case LOCALE_ICENTURY:
1078 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1079 tmp, ARRAY_SIZE( tmp ))) break;
1080 if (!(str = find_format( tmp, L"y" ))) break;
1081 return locale_return_number( !wcsncmp( str, L"yyyy", 4 ), type, buffer, len );
1083 case LOCALE_ITLZERO:
1084 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1085 tmp, ARRAY_SIZE( tmp ))) break;
1086 if (!(str = find_format( tmp, L"Hh" ))) break;
1087 return locale_return_number( str[1] == str[0], type, buffer, len );
1089 case LOCALE_IDAYLZERO:
1090 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1091 tmp, ARRAY_SIZE( tmp ))) break;
1092 if (!(str = find_format( tmp, L"d" ))) break;
1093 return locale_return_number( str[1] == 'd', type, buffer, len );
1095 case LOCALE_IMONLZERO:
1096 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1097 tmp, ARRAY_SIZE( tmp ))) break;
1098 if (!(str = find_format( tmp, L"M" ))) break;
1099 return locale_return_number( str[1] == 'M', type, buffer, len );
1101 case LOCALE_S1159:
1102 if ((ret = locale_return_reg_string( &entry_s1159, type, buffer, len )) != -1) return ret;
1103 return locale_return_string( locale->s1159, type, buffer, len );
1105 case LOCALE_S2359:
1106 if ((ret = locale_return_reg_string( &entry_s2359, type, buffer, len )) != -1) return ret;
1107 return locale_return_string( locale->s2359, type, buffer, len );
1109 case LOCALE_SDAYNAME1:
1110 case LOCALE_SDAYNAME2:
1111 case LOCALE_SDAYNAME3:
1112 case LOCALE_SDAYNAME4:
1113 case LOCALE_SDAYNAME5:
1114 case LOCALE_SDAYNAME6:
1115 case LOCALE_SDAYNAME7:
1116 return locale_return_strarray( locale->sdayname,
1117 LOWORD(type - LOCALE_SDAYNAME1 + 1) % 7, type, buffer, len );
1119 case LOCALE_SABBREVDAYNAME1:
1120 case LOCALE_SABBREVDAYNAME2:
1121 case LOCALE_SABBREVDAYNAME3:
1122 case LOCALE_SABBREVDAYNAME4:
1123 case LOCALE_SABBREVDAYNAME5:
1124 case LOCALE_SABBREVDAYNAME6:
1125 case LOCALE_SABBREVDAYNAME7:
1126 return locale_return_strarray( locale->sabbrevdayname,
1127 LOWORD(type - LOCALE_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1129 case LOCALE_SMONTHNAME1:
1130 case LOCALE_SMONTHNAME2:
1131 case LOCALE_SMONTHNAME3:
1132 case LOCALE_SMONTHNAME4:
1133 case LOCALE_SMONTHNAME5:
1134 case LOCALE_SMONTHNAME6:
1135 case LOCALE_SMONTHNAME7:
1136 case LOCALE_SMONTHNAME8:
1137 case LOCALE_SMONTHNAME9:
1138 case LOCALE_SMONTHNAME10:
1139 case LOCALE_SMONTHNAME11:
1140 case LOCALE_SMONTHNAME12:
1141 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1142 locale->sgenitivemonth : locale->smonthname,
1143 type - LOCALE_SMONTHNAME1, type, buffer, len );
1145 case LOCALE_SABBREVMONTHNAME1:
1146 case LOCALE_SABBREVMONTHNAME2:
1147 case LOCALE_SABBREVMONTHNAME3:
1148 case LOCALE_SABBREVMONTHNAME4:
1149 case LOCALE_SABBREVMONTHNAME5:
1150 case LOCALE_SABBREVMONTHNAME6:
1151 case LOCALE_SABBREVMONTHNAME7:
1152 case LOCALE_SABBREVMONTHNAME8:
1153 case LOCALE_SABBREVMONTHNAME9:
1154 case LOCALE_SABBREVMONTHNAME10:
1155 case LOCALE_SABBREVMONTHNAME11:
1156 case LOCALE_SABBREVMONTHNAME12:
1157 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1158 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1159 type - LOCALE_SABBREVMONTHNAME1, type, buffer, len );
1161 case LOCALE_SPOSITIVESIGN:
1162 if ((ret = locale_return_reg_string( &entry_spositivesign, type, buffer, len )) != -1) return ret;
1163 return locale_return_string( locale->spositivesign, type, buffer, len );
1165 case LOCALE_SNEGATIVESIGN:
1166 if ((ret = locale_return_reg_string( &entry_snegativesign, type, buffer, len )) != -1) return ret;
1167 return locale_return_string( locale->snegativesign, type, buffer, len );
1169 case LOCALE_IPOSSIGNPOSN:
1170 if (!get_locale_info( locale, lcid,
1171 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1172 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1173 return locale_return_number( ipossignposn[val], type, buffer, len );
1175 case LOCALE_INEGSIGNPOSN:
1176 if (!get_locale_info( locale, lcid,
1177 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1178 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1179 return locale_return_number( inegsignposn[val], type, buffer, len );
1181 case LOCALE_IPOSSYMPRECEDES:
1182 if (!get_locale_info( locale, lcid,
1183 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1184 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1185 return locale_return_number( !(val & 1), type, buffer, len );
1187 case LOCALE_IPOSSEPBYSPACE:
1188 if (!get_locale_info( locale, lcid,
1189 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1190 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1191 return locale_return_number( !!(val & 2), type, buffer, len );
1193 case LOCALE_INEGSYMPRECEDES:
1194 if (!get_locale_info( locale, lcid,
1195 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1196 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1197 return locale_return_number( inegsymprecedes[val], type, buffer, len );
1199 case LOCALE_INEGSEPBYSPACE:
1200 if (!get_locale_info( locale, lcid,
1201 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1202 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1203 return locale_return_number( (val >= 8), type, buffer, len );
1205 case LOCALE_FONTSIGNATURE:
1206 return locale_return_data( locale_strings + locale->fontsignature + 1,
1207 locale_strings[locale->fontsignature], type, buffer, len );
1209 case LOCALE_SISO639LANGNAME:
1210 return locale_return_string( locale->siso639langname, type, buffer, len );
1212 case LOCALE_SISO3166CTRYNAME:
1213 return locale_return_string( locale->siso3166ctryname, type, buffer, len );
1215 case LOCALE_IGEOID:
1216 return locale_return_number( locale->igeoid, type, buffer, len );
1218 case LOCALE_SNAME:
1219 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1221 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1222 if (entry) return locale_return_string( entry->name, type, buffer, len );
1224 return locale_return_string( locale->sname, type, buffer, len );
1226 case LOCALE_SDURATION:
1227 return locale_return_strarray( locale->sduration, 0, type, buffer, len );
1229 case LOCALE_SKEYBOARDSTOINSTALL:
1230 return locale_return_string( locale->skeyboardstoinstall, type, buffer, len );
1232 case LOCALE_SSHORTESTDAYNAME1:
1233 case LOCALE_SSHORTESTDAYNAME2:
1234 case LOCALE_SSHORTESTDAYNAME3:
1235 case LOCALE_SSHORTESTDAYNAME4:
1236 case LOCALE_SSHORTESTDAYNAME5:
1237 case LOCALE_SSHORTESTDAYNAME6:
1238 case LOCALE_SSHORTESTDAYNAME7:
1239 return locale_return_strarray( locale->sshortestdayname,
1240 LOWORD(type - LOCALE_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1242 case LOCALE_SISO639LANGNAME2:
1243 return locale_return_string( locale->siso639langname2, type, buffer, len );
1245 case LOCALE_SISO3166CTRYNAME2:
1246 return locale_return_string( locale->siso3166ctryname2, type, buffer, len );
1248 case LOCALE_SNAN:
1249 return locale_return_string( locale->snan, type, buffer, len );
1251 case LOCALE_SPOSINFINITY:
1252 return locale_return_string( locale->sposinfinity, type, buffer, len );
1254 case LOCALE_SNEGINFINITY:
1255 return locale_return_string( locale->sneginfinity, type, buffer, len );
1257 case LOCALE_SSCRIPTS:
1258 return locale_return_string( locale->sscripts, type, buffer, len );
1260 case LOCALE_SPARENT:
1261 return locale_return_string( locale->sparent, type, buffer, len );
1263 case LOCALE_SCONSOLEFALLBACKNAME:
1264 return locale_return_string( locale->sconsolefallbackname, type, buffer, len );
1266 case LOCALE_SLOCALIZEDLANGUAGENAME:
1267 /* FIXME: localization */
1268 return locale_return_string( locale->senglanguage, type, buffer, len );
1270 case LOCALE_IREADINGLAYOUT:
1271 return locale_return_number( locale->ireadinglayout, type, buffer, len );
1273 case LOCALE_INEUTRAL:
1274 return locale_return_number( !locale->inotneutral, type, buffer, len );
1276 case LOCALE_SENGLISHDISPLAYNAME:
1277 return locale_return_string( locale->sengdisplayname, type, buffer, len );
1279 case LOCALE_SNATIVEDISPLAYNAME:
1280 return locale_return_string( locale->snativedisplayname, type, buffer, len );
1282 case LOCALE_INEGATIVEPERCENT:
1283 return locale_return_number( locale->inegativepercent, type, buffer, len );
1285 case LOCALE_IPOSITIVEPERCENT:
1286 return locale_return_number( locale->ipositivepercent, type, buffer, len );
1288 case LOCALE_SPERCENT:
1289 return locale_return_string( locale->spercent, type, buffer, len );
1291 case LOCALE_SPERMILLE:
1292 return locale_return_data( spermille, ARRAY_SIZE(spermille), type, buffer, len );
1294 case LOCALE_SMONTHDAY:
1295 return locale_return_strarray( locale->smonthday, 0, type, buffer, len );
1297 case LOCALE_SSHORTTIME:
1298 if ((ret = locale_return_reg_string( &entry_sshorttime, type, buffer, len )) != -1) return ret;
1299 return locale_return_strarray( locale->sshorttime, 0, type, buffer, len );
1301 case LOCALE_SOPENTYPELANGUAGETAG:
1302 return locale_return_string( locale->sopentypelanguagetag, type, buffer, len );
1304 case LOCALE_SSORTLOCALE:
1305 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1307 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1308 if (entry) return locale_return_string( entry->name, type, buffer, len );
1310 return locale_return_string( locale->ssortlocale, type, buffer, len );
1312 case LOCALE_SRELATIVELONGDATE:
1313 return locale_return_string( locale->srelativelongdate, type, buffer, len );
1315 case 0x007d: /* undocumented */
1316 return locale_return_number( 0, type, buffer, len );
1318 case LOCALE_SSHORTESTAM:
1319 return locale_return_string( locale->sshortestam, type, buffer, len );
1321 case LOCALE_SSHORTESTPM:
1322 return locale_return_string( locale->sshortestpm, type, buffer, len );
1324 case LOCALE_SENGLANGUAGE:
1325 return locale_return_string( locale->senglanguage, type, buffer, len );
1327 case LOCALE_SENGCOUNTRY:
1328 return locale_return_string( locale->sengcountry, type, buffer, len );
1330 case LOCALE_STIMEFORMAT:
1331 if ((ret = locale_return_reg_string( &entry_stimeformat, type, buffer, len )) != -1) return ret;
1332 return locale_return_strarray( locale->stimeformat, 0, type, buffer, len );
1334 case LOCALE_IDEFAULTANSICODEPAGE:
1335 val = locale->idefaultansicodepage == CP_UTF8 ? CP_ACP : locale->idefaultansicodepage;
1336 return locale_return_number( val, type, buffer, len );
1338 case LOCALE_ITIMEMARKPOSN:
1339 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1340 tmp, ARRAY_SIZE( tmp ))) break;
1341 if (!(str = find_format( tmp, L"Hhmst" ))) break;
1342 return locale_return_number( *str == 't', type, buffer, len );
1344 case LOCALE_SYEARMONTH:
1345 if ((ret = locale_return_reg_string( &entry_syearmonth, type, buffer, len )) != -1) return ret;
1346 return locale_return_strarray( locale->syearmonth, 0, type, buffer, len );
1348 case LOCALE_SENGCURRNAME:
1349 return locale_return_string( locale->sengcurrname, type, buffer, len );
1351 case LOCALE_SNATIVECURRNAME:
1352 return locale_return_string( locale->snativecurrname, type, buffer, len );
1354 case LOCALE_ICALENDARTYPE:
1355 if ((ret = locale_return_reg_number( &entry_icalendartype, type, buffer, len )) != -1) return ret;
1356 return locale_return_number( locale_strings[locale->scalendartype + 1], type, buffer, len );
1358 case LOCALE_IPAPERSIZE:
1359 if ((ret = locale_return_reg_number( &entry_ipapersize, type, buffer, len )) != -1) return ret;
1360 return locale_return_number( locale->ipapersize, type, buffer, len );
1362 case LOCALE_IOPTIONALCALENDAR:
1363 return locale_return_number( locale_strings[locale->scalendartype + 2], type, buffer, len );
1365 case LOCALE_IFIRSTDAYOFWEEK:
1366 if ((ret = locale_return_reg_number( &entry_ifirstdayofweek, type, buffer, len )) != -1) return ret;
1367 return locale_return_number( (locale->ifirstdayofweek + 6) % 7, type, buffer, len );
1369 case LOCALE_IFIRSTWEEKOFYEAR:
1370 if ((ret = locale_return_reg_number( &entry_ifirstweekofyear, type, buffer, len )) != -1) return ret;
1371 return locale_return_number( locale->ifirstweekofyear, type, buffer, len );
1373 case LOCALE_SMONTHNAME13:
1374 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1375 locale->sgenitivemonth : locale->smonthname,
1376 12, type, buffer, len );
1378 case LOCALE_SABBREVMONTHNAME13:
1379 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1380 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1381 12, type, buffer, len );
1383 case LOCALE_INEGNUMBER:
1384 if ((ret = locale_return_reg_number( &entry_inegnumber, type, buffer, len )) != -1) return ret;
1385 return locale_return_number( locale->inegnumber, type, buffer, len );
1387 case LOCALE_IDEFAULTMACCODEPAGE:
1388 val = locale->idefaultmaccodepage == CP_UTF8 ? CP_MACCP : locale->idefaultmaccodepage;
1389 return locale_return_number( val, type, buffer, len );
1391 case LOCALE_IDEFAULTEBCDICCODEPAGE:
1392 return locale_return_number( locale->idefaultebcdiccodepage, type, buffer, len );
1394 case LOCALE_SSORTNAME:
1395 sort = get_locale_sortname( lcid );
1396 return locale_return_data( sort, wcslen(sort) + 1, type, buffer, len );
1398 case LOCALE_IDIGITSUBSTITUTION:
1399 if ((ret = locale_return_reg_number( &entry_idigitsubstitution, type, buffer, len )) != -1) return ret;
1400 return locale_return_number( locale->idigitsubstitution, type, buffer, len );
1402 SetLastError( ERROR_INVALID_FLAGS );
1403 return 0;
1407 /* get geo information from the locale.nls file */
1408 static int get_geo_info( const struct geo_id *geo, enum SYSGEOTYPE type,
1409 WCHAR *buffer, int len, LANGID lang )
1411 WCHAR tmp[12];
1412 const WCHAR *str = tmp;
1413 int ret;
1415 switch (type)
1417 case GEO_NATION:
1418 if (geo->class != GEOCLASS_NATION) return 0;
1419 /* fall through */
1420 case GEO_ID:
1421 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->id );
1422 break;
1423 case GEO_ISO_UN_NUMBER:
1424 swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", geo->uncode );
1425 break;
1426 case GEO_PARENT:
1427 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->parent );
1428 break;
1429 case GEO_DIALINGCODE:
1430 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->dialcode );
1431 break;
1432 case GEO_ISO2:
1433 str = geo->iso2;
1434 break;
1435 case GEO_ISO3:
1436 str = geo->iso3;
1437 break;
1438 case GEO_LATITUDE:
1439 str = geo->latitude;
1440 break;
1441 case GEO_LONGITUDE:
1442 str = geo->longitude;
1443 break;
1444 case GEO_CURRENCYCODE:
1445 str = geo->currcode;
1446 break;
1447 case GEO_CURRENCYSYMBOL:
1448 str = geo->currsymbol;
1449 break;
1450 case GEO_RFC1766:
1451 case GEO_LCID:
1452 case GEO_FRIENDLYNAME:
1453 case GEO_OFFICIALNAME:
1454 case GEO_TIMEZONES:
1455 case GEO_OFFICIALLANGUAGES:
1456 case GEO_NAME:
1457 FIXME( "type %u is not supported\n", type );
1458 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1459 return 0;
1460 default:
1461 SetLastError( ERROR_INVALID_FLAGS );
1462 return 0;
1465 ret = lstrlenW(str) + 1;
1466 if (!buffer || !len) return ret;
1468 memcpy( buffer, str, min( ret, len ) * sizeof(WCHAR) );
1469 if (len < ret) SetLastError( ERROR_INSUFFICIENT_BUFFER );
1470 return len < ret ? 0 : ret;
1474 /* update a registry value based on the current user locale info */
1475 static void update_registry_value( UINT type, const WCHAR *value )
1477 WCHAR buffer[80];
1478 UINT len = get_locale_info( user_locale, user_lcid, type, buffer, ARRAY_SIZE(buffer) );
1479 if (len) RegSetValueExW( intl_key, value, 0, REG_SZ, (BYTE *)buffer, len * sizeof(WCHAR) );
1483 /* update all registry values upon user locale change */
1484 static void update_locale_registry(void)
1486 WCHAR buffer[80];
1487 UINT len;
1489 len = swprintf( buffer, ARRAY_SIZE(buffer), L"%08x", GetUserDefaultLCID() );
1490 RegSetValueExW( intl_key, L"Locale", 0, REG_SZ, (BYTE *)buffer, (len + 1) * sizeof(WCHAR) );
1492 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICALENDARTYPE, entry_icalendartype.value );
1493 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICOUNTRY, entry_icountry.value );
1494 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICURRDIGITS, entry_icurrdigits.value );
1495 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICURRENCY, entry_icurrency.value );
1496 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDIGITS, entry_idigits.value );
1497 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDIGITSUBSTITUTION, entry_idigitsubstitution.value );
1498 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IFIRSTDAYOFWEEK, entry_ifirstdayofweek.value );
1499 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IFIRSTWEEKOFYEAR, entry_ifirstweekofyear.value );
1500 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ILZERO, entry_ilzero.value );
1501 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IMEASURE, entry_imeasure.value );
1502 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_INEGCURR, entry_inegcurr.value );
1503 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_INEGNUMBER, entry_inegnumber.value );
1504 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IPAPERSIZE, entry_ipapersize.value );
1505 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_S1159, entry_s1159.value );
1506 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_S2359, entry_s2359.value );
1507 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SCURRENCY, entry_scurrency.value );
1508 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SDECIMAL, entry_sdecimal.value );
1509 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SGROUPING, entry_sgrouping.value );
1510 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SLIST, entry_slist.value );
1511 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SLONGDATE, entry_slongdate.value );
1512 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SMONDECIMALSEP, entry_smondecimalsep.value );
1513 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SMONGROUPING, entry_smongrouping.value );
1514 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SMONTHOUSANDSEP, entry_smonthousandsep.value );
1515 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNATIVEDIGITS, entry_snativedigits.value );
1516 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNEGATIVESIGN, entry_snegativesign.value );
1517 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SPOSITIVESIGN, entry_spositivesign.value );
1518 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SSHORTDATE, entry_sshortdate.value );
1519 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SSHORTTIME, entry_sshorttime.value );
1520 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STHOUSAND, entry_sthousand.value );
1521 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STIMEFORMAT, entry_stimeformat.value );
1522 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SYEARMONTH, entry_syearmonth.value );
1523 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDATE, L"iDate" );
1524 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIME, L"iTime" );
1525 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIMEMARKPOSN, L"iTimePrefix" );
1526 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITLZERO, L"iTLZero" );
1527 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SDATE, L"sDate" );
1528 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STIME, L"sTime" );
1529 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SABBREVLANGNAME, L"sLanguage" );
1530 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SCOUNTRY, L"sCountry" );
1531 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNAME, L"LocaleName" );
1532 SetUserGeoID( user_locale->igeoid );
1536 /***********************************************************************
1537 * init_locale
1539 void init_locale( HMODULE module )
1541 UINT ansi_cp = 0, oem_cp = 0;
1542 USHORT *ansi_ptr, *oem_ptr;
1543 void *sort_ptr;
1544 WCHAR bufferW[LOCALE_NAME_MAX_LENGTH];
1545 DYNAMIC_TIME_ZONE_INFORMATION timezone;
1546 DWORD count;
1547 SIZE_T size;
1548 HKEY hkey;
1550 load_locale_nls();
1551 NtQueryDefaultLocale( FALSE, &system_lcid );
1552 NtQueryDefaultLocale( FALSE, &user_lcid );
1553 system_locale = get_locale_by_id( &system_lcid, 0 );
1554 user_locale = get_locale_by_id( &user_lcid, 0 );
1555 kernelbase_handle = module;
1557 if (GetEnvironmentVariableW( L"WINEUNIXCP", bufferW, ARRAY_SIZE(bufferW) ))
1558 unix_cp = wcstoul( bufferW, NULL, 10 );
1560 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
1561 (WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) );
1562 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
1563 (WCHAR *)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
1564 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
1565 (WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
1567 NtGetNlsSectionPtr( 9, 0, NULL, &sort_ptr, &size );
1568 NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size );
1569 init_sortkeys( sort_ptr );
1571 if (!ansi_cp || NtGetNlsSectionPtr( 11, ansi_cp, NULL, (void **)&ansi_ptr, &size ))
1572 NtGetNlsSectionPtr( 11, 1252, NULL, (void **)&ansi_ptr, &size );
1573 if (!oem_cp || NtGetNlsSectionPtr( 11, oem_cp, 0, (void **)&oem_ptr, &size ))
1574 NtGetNlsSectionPtr( 11, 437, NULL, (void **)&oem_ptr, &size );
1575 NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr;
1576 NtCurrentTeb()->Peb->OemCodePageData = oem_ptr;
1577 NtCurrentTeb()->Peb->UnicodeCaseTableData = sort.casemap;
1578 RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info );
1579 RtlResetRtlTranslations( &nls_info );
1581 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
1582 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
1583 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1584 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tz_key, NULL );
1585 RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International",
1586 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL );
1588 current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT );
1590 if (GetDynamicTimeZoneInformation( &timezone ) != TIME_ZONE_ID_INVALID &&
1591 !RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\TimeZoneInformation",
1592 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1594 RegSetValueExW( hkey, L"StandardName", 0, REG_SZ, (BYTE *)timezone.StandardName,
1595 (lstrlenW(timezone.StandardName) + 1) * sizeof(WCHAR) );
1596 RegSetValueExW( hkey, L"TimeZoneKeyName", 0, REG_SZ, (BYTE *)timezone.TimeZoneKeyName,
1597 (lstrlenW(timezone.TimeZoneKeyName) + 1) * sizeof(WCHAR) );
1598 RegCloseKey( hkey );
1601 /* Update registry contents if the user locale has changed.
1602 * This simulates the action of the Windows control panel. */
1604 count = sizeof(bufferW);
1605 if (!RegQueryValueExW( intl_key, L"Locale", NULL, NULL, (BYTE *)bufferW, &count ))
1607 if (wcstoul( bufferW, NULL, 16 ) == user_lcid) return; /* already set correctly */
1608 TRACE( "updating registry, locale changed %s -> %08lx\n", debugstr_w(bufferW), user_lcid );
1610 else TRACE( "updating registry, locale changed none -> %08lx\n", user_lcid );
1612 update_locale_registry();
1614 if (!RegCreateKeyExW( nls_key, L"Codepage",
1615 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1617 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", ansi_cp );
1618 RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1619 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", oem_cp );
1620 RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1621 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", mac_cp );
1622 RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1623 RegCloseKey( hkey );
1628 static inline USHORT get_table_entry( const USHORT *table, WCHAR ch )
1630 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
1634 static inline WCHAR casemap( const USHORT *table, WCHAR ch )
1636 return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
1640 static inline WORD get_char_type( DWORD type, WCHAR ch )
1642 const BYTE *ptr = sort.ctype_idx + ((const WORD *)sort.ctype_idx)[ch >> 8];
1643 ptr = sort.ctype_idx + ((const WORD *)ptr)[(ch >> 4) & 0x0f] + (ch & 0x0f);
1644 return sort.ctypes[*ptr * 3 + type / 2];
1648 static BYTE rol( BYTE val, BYTE count )
1650 return (val << count) | (val >> (8 - count));
1654 static BYTE get_char_props( const struct norm_table *info, unsigned int ch )
1656 const BYTE *level1 = (const BYTE *)((const USHORT *)info + info->props_level1);
1657 const BYTE *level2 = (const BYTE *)((const USHORT *)info + info->props_level2);
1658 BYTE off = level1[ch / 128];
1660 if (!off || off >= 0xfb) return rol( off, 5 );
1661 return level2[(off - 1) * 128 + ch % 128];
1665 static const WCHAR *get_decomposition( WCHAR ch, unsigned int *ret_len )
1667 const struct pair { WCHAR src; USHORT dst; } *pairs;
1668 const USHORT *hash_table = (const USHORT *)norm_info + norm_info->decomp_hash;
1669 const WCHAR *ret;
1670 unsigned int i, pos, end, len, hash;
1672 *ret_len = 1;
1673 hash = ch % norm_info->decomp_size;
1674 pos = hash_table[hash];
1675 if (pos >> 13)
1677 if (get_char_props( norm_info, ch ) != 0xbf) return NULL;
1678 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pos & 0x1fff);
1679 len = pos >> 13;
1681 else
1683 pairs = (const struct pair *)((const USHORT *)norm_info + norm_info->decomp_map);
1685 /* find the end of the hash bucket */
1686 for (i = hash + 1; i < norm_info->decomp_size; i++) if (!(hash_table[i] >> 13)) break;
1687 if (i < norm_info->decomp_size) end = hash_table[i];
1688 else for (end = pos; pairs[end].src; end++) ;
1690 for ( ; pos < end; pos++)
1692 if (pairs[pos].src != (WCHAR)ch) continue;
1693 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pairs[pos].dst & 0x1fff);
1694 len = pairs[pos].dst >> 13;
1695 break;
1697 if (pos >= end) return NULL;
1700 if (len == 7) while (ret[len]) len++;
1701 if (!ret[0]) len = 0; /* ignored char */
1702 *ret_len = len;
1703 return ret;
1707 static WCHAR compose_chars( WCHAR ch1, WCHAR ch2 )
1709 const USHORT *table = (const USHORT *)norm_info + norm_info->comp_hash;
1710 const WCHAR *chars = (const USHORT *)norm_info + norm_info->comp_seq;
1711 unsigned int hash, start, end, i;
1712 WCHAR ch[3];
1714 hash = (ch1 + 95 * ch2) % norm_info->comp_size;
1715 start = table[hash];
1716 end = table[hash + 1];
1717 while (start < end)
1719 for (i = 0; i < 3; i++, start++)
1721 ch[i] = chars[start];
1722 if (IS_HIGH_SURROGATE( ch[i] )) start++;
1724 if (ch[0] == ch1 && ch[1] == ch2) return ch[2];
1726 return 0;
1730 static UINT get_lcid_codepage( LCID lcid, ULONG flags )
1732 UINT ret = GetACP();
1734 if (!(flags & LOCALE_USE_CP_ACP) && lcid != GetSystemDefaultLCID())
1735 GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
1736 (WCHAR *)&ret, sizeof(ret)/sizeof(WCHAR) );
1737 return ret;
1741 static const CPTABLEINFO *get_codepage_table( UINT codepage )
1743 unsigned int i;
1744 USHORT *ptr;
1745 SIZE_T size;
1747 switch (codepage)
1749 case CP_ACP:
1750 return &nls_info.AnsiTableInfo;
1751 case CP_OEMCP:
1752 return &nls_info.OemTableInfo;
1753 case CP_MACCP:
1754 codepage = mac_cp;
1755 break;
1756 case CP_THREAD_ACP:
1757 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return &nls_info.AnsiTableInfo;
1758 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 );
1759 if (!codepage) return &nls_info.AnsiTableInfo;
1760 break;
1761 default:
1762 if (codepage == nls_info.AnsiTableInfo.CodePage) return &nls_info.AnsiTableInfo;
1763 if (codepage == nls_info.OemTableInfo.CodePage) return &nls_info.OemTableInfo;
1764 break;
1767 RtlEnterCriticalSection( &locale_section );
1769 for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done;
1771 if (i == ARRAY_SIZE( codepages ))
1773 RtlLeaveCriticalSection( &locale_section );
1774 ERR( "too many codepages\n" );
1775 return NULL;
1777 if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size ))
1779 RtlLeaveCriticalSection( &locale_section );
1780 SetLastError( ERROR_INVALID_PARAMETER );
1781 return NULL;
1783 RtlInitCodePageTable( ptr, &codepages[i] );
1784 nb_codepages++;
1785 done:
1786 RtlLeaveCriticalSection( &locale_section );
1787 return &codepages[i];
1791 static const WCHAR *get_ligature( WCHAR wc )
1793 int low = 0, high = ARRAY_SIZE( ligatures ) -1;
1794 while (low <= high)
1796 int pos = (low + high) / 2;
1797 if (ligatures[pos][0] < wc) low = pos + 1;
1798 else if (ligatures[pos][0] > wc) high = pos - 1;
1799 else return ligatures[pos] + 1;
1801 return NULL;
1805 static NTSTATUS expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1807 int i, len, pos = 0;
1808 NTSTATUS ret = STATUS_SUCCESS;
1809 const WCHAR *expand;
1811 for (i = 0; i < srclen; i++)
1813 if (!(expand = get_ligature( src[i] )))
1815 expand = src + i;
1816 len = 1;
1818 else len = lstrlenW( expand );
1820 if (*dstlen && ret == STATUS_SUCCESS)
1822 if (pos + len <= *dstlen) memcpy( dst + pos, expand, len * sizeof(WCHAR) );
1823 else ret = STATUS_BUFFER_TOO_SMALL;
1825 pos += len;
1827 *dstlen = pos;
1828 return ret;
1832 static NTSTATUS fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1834 extern const WCHAR wine_digitmap[] DECLSPEC_HIDDEN;
1835 int i, len = *dstlen;
1837 *dstlen = srclen;
1838 if (!len) return STATUS_SUCCESS;
1839 if (srclen > len) return STATUS_BUFFER_TOO_SMALL;
1840 for (i = 0; i < srclen; i++)
1842 WCHAR digit = get_table_entry( wine_digitmap, src[i] );
1843 dst[i] = digit ? digit : src[i];
1845 return STATUS_SUCCESS;
1849 static NTSTATUS fold_string( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1851 NTSTATUS ret;
1852 WCHAR *tmp;
1854 switch (flags)
1856 case MAP_PRECOMPOSED:
1857 return RtlNormalizeString( NormalizationC, src, srclen, dst, dstlen );
1858 case MAP_FOLDCZONE:
1859 case MAP_PRECOMPOSED | MAP_FOLDCZONE:
1860 return RtlNormalizeString( NormalizationKC, src, srclen, dst, dstlen );
1861 case MAP_COMPOSITE:
1862 return RtlNormalizeString( NormalizationD, src, srclen, dst, dstlen );
1863 case MAP_COMPOSITE | MAP_FOLDCZONE:
1864 return RtlNormalizeString( NormalizationKD, src, srclen, dst, dstlen );
1865 case MAP_FOLDDIGITS:
1866 return fold_digits( src, srclen, dst, dstlen );
1867 case MAP_EXPAND_LIGATURES:
1868 case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
1869 return expand_ligatures( src, srclen, dst, dstlen );
1870 case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
1871 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1872 return STATUS_NO_MEMORY;
1873 fold_digits( src, srclen, tmp, &srclen );
1874 ret = RtlNormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
1875 break;
1876 case MAP_FOLDDIGITS | MAP_FOLDCZONE:
1877 case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
1878 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1879 return STATUS_NO_MEMORY;
1880 fold_digits( src, srclen, tmp, &srclen );
1881 ret = RtlNormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
1882 break;
1883 case MAP_FOLDDIGITS | MAP_COMPOSITE:
1884 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1885 return STATUS_NO_MEMORY;
1886 fold_digits( src, srclen, tmp, &srclen );
1887 ret = RtlNormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
1888 break;
1889 case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
1890 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1891 return STATUS_NO_MEMORY;
1892 fold_digits( src, srclen, tmp, &srclen );
1893 ret = RtlNormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
1894 break;
1895 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
1896 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
1897 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1898 return STATUS_NO_MEMORY;
1899 fold_digits( src, srclen, tmp, &srclen );
1900 ret = expand_ligatures( tmp, srclen, dst, dstlen );
1901 break;
1902 default:
1903 return STATUS_INVALID_PARAMETER_1;
1905 RtlFreeHeap( GetProcessHeap(), 0, tmp );
1906 return ret;
1910 static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1912 int len, i;
1914 if (flags)
1916 SetLastError( ERROR_INVALID_FLAGS );
1917 return 0;
1919 if (!dstlen) return srclen;
1920 len = min( srclen, dstlen );
1921 for (i = 0; i < len; i++)
1923 unsigned char c = src[i];
1924 dst[i] = (c < 0x20) ? c : c + 0xf000;
1926 if (len < srclen)
1928 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1929 return 0;
1931 return len;
1935 static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1937 static const signed char base64_decoding_table[] =
1939 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1940 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1941 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1942 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1943 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1944 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1945 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1946 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1949 const char *source_end = src + srclen;
1950 int offset = 0, pos = 0;
1951 DWORD byte_pair = 0;
1953 if (flags)
1955 SetLastError( ERROR_INVALID_FLAGS );
1956 return 0;
1958 #define OUTPUT(ch) \
1959 do { \
1960 if (dstlen > 0) \
1962 if (pos >= dstlen) goto overflow; \
1963 dst[pos] = (ch); \
1965 pos++; \
1966 } while(0)
1968 while (src < source_end)
1970 if (*src == '+')
1972 src++;
1973 if (src >= source_end) break;
1974 if (*src == '-')
1976 /* just a plus sign escaped as +- */
1977 OUTPUT( '+' );
1978 src++;
1979 continue;
1984 signed char sextet = *src;
1985 if (sextet == '-')
1987 /* skip over the dash and end base64 decoding
1988 * the current, unfinished byte pair is discarded */
1989 src++;
1990 offset = 0;
1991 break;
1993 if (sextet < 0)
1995 /* the next character of src is < 0 and therefore not part of a base64 sequence
1996 * the current, unfinished byte pair is NOT discarded in this case
1997 * this is probably a bug in Windows */
1998 break;
2000 sextet = base64_decoding_table[sextet];
2001 if (sextet == -1)
2003 /* -1 means that the next character of src is not part of a base64 sequence
2004 * in other words, all sextets in this base64 sequence have been processed
2005 * the current, unfinished byte pair is discarded */
2006 offset = 0;
2007 break;
2010 byte_pair = (byte_pair << 6) | sextet;
2011 offset += 6;
2012 if (offset >= 16)
2014 /* this byte pair is done */
2015 OUTPUT( byte_pair >> (offset - 16) );
2016 offset -= 16;
2018 src++;
2020 while (src < source_end);
2022 else
2024 OUTPUT( (unsigned char)*src );
2025 src++;
2028 return pos;
2030 overflow:
2031 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2032 return 0;
2033 #undef OUTPUT
2037 static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2039 DWORD reslen;
2040 NTSTATUS status;
2042 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
2044 SetLastError( ERROR_INVALID_FLAGS );
2045 return 0;
2047 if (!dstlen) dst = NULL;
2048 status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen );
2049 if (status == STATUS_SOME_NOT_MAPPED)
2051 if (flags & MB_ERR_INVALID_CHARS)
2053 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2054 return 0;
2057 else if (!set_ntstatus( status )) reslen = 0;
2059 return reslen / sizeof(WCHAR);
2063 static inline int is_private_use_area_char( WCHAR code )
2065 return (code >= 0xe000 && code <= 0xf8ff);
2069 static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen )
2071 if (info->DBCSOffsets)
2073 for ( ; srclen; src++, srclen-- )
2075 USHORT off = info->DBCSOffsets[*src];
2076 if (off)
2078 if (srclen == 1) break; /* partial char, error */
2079 if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar &&
2080 ((src[0] << 8) | src[1]) != info->TransUniDefaultChar) break;
2081 src++;
2082 srclen--;
2083 continue;
2085 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2086 break;
2087 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2090 else
2092 for ( ; srclen; src++, srclen-- )
2094 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2095 break;
2096 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2099 return !!srclen;
2104 static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2105 WCHAR *dst, int dstlen )
2107 WCHAR ch;
2108 USHORT off;
2109 int len;
2110 const WCHAR *decomp;
2111 unsigned int decomp_len;
2113 if (info->DBCSOffsets)
2115 if (!dstlen) /* compute length */
2117 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2119 if ((off = info->DBCSOffsets[*src]))
2121 if (srclen > 1 && src[1])
2123 src++;
2124 srclen--;
2125 ch = info->DBCSOffsets[off + *src];
2127 else ch = info->UniDefaultChar;
2129 else ch = info->MultiByteTable[*src];
2130 get_decomposition( ch, &decomp_len );
2132 return len;
2135 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2137 if ((off = info->DBCSOffsets[*src]))
2139 if (srclen > 1 && src[1])
2141 src++;
2142 srclen--;
2143 ch = info->DBCSOffsets[off + *src];
2145 else ch = info->UniDefaultChar;
2147 else ch = info->MultiByteTable[*src];
2149 if ((decomp = get_decomposition( ch, &decomp_len )))
2151 if (len < decomp_len) break;
2152 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2154 else *dst = ch;
2157 else
2159 if (!dstlen) /* compute length */
2161 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2162 get_decomposition( info->MultiByteTable[*src], &decomp_len );
2163 return len;
2166 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2168 ch = info->MultiByteTable[*src];
2169 if ((decomp = get_decomposition( ch, &decomp_len )))
2171 if (len < decomp_len) break;
2172 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2174 else *dst = ch;
2178 if (srclen)
2180 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2181 return 0;
2183 return dstlen - len;
2187 static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2188 WCHAR *dst, int dstlen )
2190 const USHORT *table = info->MultiByteTable;
2191 int ret = srclen;
2193 if (!dstlen) return srclen;
2195 if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */
2197 srclen = dstlen;
2198 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2199 ret = 0;
2202 while (srclen >= 16)
2204 dst[0] = table[src[0]];
2205 dst[1] = table[src[1]];
2206 dst[2] = table[src[2]];
2207 dst[3] = table[src[3]];
2208 dst[4] = table[src[4]];
2209 dst[5] = table[src[5]];
2210 dst[6] = table[src[6]];
2211 dst[7] = table[src[7]];
2212 dst[8] = table[src[8]];
2213 dst[9] = table[src[9]];
2214 dst[10] = table[src[10]];
2215 dst[11] = table[src[11]];
2216 dst[12] = table[src[12]];
2217 dst[13] = table[src[13]];
2218 dst[14] = table[src[14]];
2219 dst[15] = table[src[15]];
2220 src += 16;
2221 dst += 16;
2222 srclen -= 16;
2225 /* now handle the remaining characters */
2226 src += srclen;
2227 dst += srclen;
2228 switch (srclen)
2230 case 15: dst[-15] = table[src[-15]];
2231 case 14: dst[-14] = table[src[-14]];
2232 case 13: dst[-13] = table[src[-13]];
2233 case 12: dst[-12] = table[src[-12]];
2234 case 11: dst[-11] = table[src[-11]];
2235 case 10: dst[-10] = table[src[-10]];
2236 case 9: dst[-9] = table[src[-9]];
2237 case 8: dst[-8] = table[src[-8]];
2238 case 7: dst[-7] = table[src[-7]];
2239 case 6: dst[-6] = table[src[-6]];
2240 case 5: dst[-5] = table[src[-5]];
2241 case 4: dst[-4] = table[src[-4]];
2242 case 3: dst[-3] = table[src[-3]];
2243 case 2: dst[-2] = table[src[-2]];
2244 case 1: dst[-1] = table[src[-1]];
2245 case 0: break;
2247 return ret;
2251 static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2252 WCHAR *dst, int dstlen )
2254 USHORT off;
2255 int i;
2257 if (!dstlen)
2259 for (i = 0; srclen; i++, src++, srclen--)
2260 if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; }
2261 return i;
2264 for (i = dstlen; srclen && i; i--, srclen--, src++, dst++)
2266 if ((off = info->DBCSOffsets[*src]))
2268 if (srclen > 1 && src[1])
2270 src++;
2271 srclen--;
2272 *dst = info->DBCSOffsets[off + *src];
2274 else *dst = info->UniDefaultChar;
2276 else *dst = info->MultiByteTable[*src];
2278 if (srclen)
2280 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2281 return 0;
2283 return dstlen - i;
2287 static int mbstowcs_codepage( UINT codepage, DWORD flags, const char *src, int srclen,
2288 WCHAR *dst, int dstlen )
2290 CPTABLEINFO local_info;
2291 const CPTABLEINFO *info = get_codepage_table( codepage );
2292 const unsigned char *str = (const unsigned char *)src;
2294 if (!info)
2296 SetLastError( ERROR_INVALID_PARAMETER );
2297 return 0;
2299 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
2301 SetLastError( ERROR_INVALID_FLAGS );
2302 return 0;
2305 if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256)
2307 local_info = *info;
2308 local_info.MultiByteTable += 257;
2309 info = &local_info;
2311 if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen ))
2313 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2314 return 0;
2317 if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen );
2319 if (info->DBCSOffsets)
2320 return mbstowcs_dbcs( info, str, srclen, dst, dstlen );
2321 else
2322 return mbstowcs_sbcs( info, str, srclen, dst, dstlen );
2326 static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2327 const char *defchar, BOOL *used )
2329 int len, i;
2331 if (flags)
2333 SetLastError( ERROR_INVALID_FLAGS );
2334 return 0;
2336 if (defchar || used)
2338 SetLastError( ERROR_INVALID_PARAMETER );
2339 return 0;
2341 if (!dstlen) return srclen;
2342 len = min( srclen, dstlen );
2343 for (i = 0; i < len; i++)
2345 if (src[i] < 0x20) dst[i] = src[i];
2346 else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000;
2347 else
2349 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2350 return 0;
2353 if (srclen > len)
2355 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2356 return 0;
2358 return len;
2362 static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2363 const char *defchar, BOOL *used )
2365 static const char directly_encodable[] =
2367 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
2368 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
2369 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
2370 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
2371 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
2372 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
2373 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
2374 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
2376 #define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
2378 static const char base64_encoding_table[] =
2379 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2381 const WCHAR *source_end = src + srclen;
2382 int pos = 0;
2384 if (defchar || used)
2386 SetLastError( ERROR_INVALID_PARAMETER );
2387 return 0;
2389 if (flags)
2391 SetLastError( ERROR_INVALID_FLAGS );
2392 return 0;
2395 #define OUTPUT(ch) \
2396 do { \
2397 if (dstlen > 0) \
2399 if (pos >= dstlen) goto overflow; \
2400 dst[pos] = (ch); \
2402 pos++; \
2403 } while (0)
2405 while (src < source_end)
2407 if (*src == '+')
2409 OUTPUT( '+' );
2410 OUTPUT( '-' );
2411 src++;
2413 else if (ENCODABLE(*src))
2415 OUTPUT( *src );
2416 src++;
2418 else
2420 unsigned int offset = 0, byte_pair = 0;
2422 OUTPUT( '+' );
2423 while (src < source_end && !ENCODABLE(*src))
2425 byte_pair = (byte_pair << 16) | *src;
2426 offset += 16;
2427 while (offset >= 6)
2429 offset -= 6;
2430 OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] );
2432 src++;
2434 if (offset)
2436 /* Windows won't create a padded base64 character if there's no room for the - sign
2437 * as well ; this is probably a bug in Windows */
2438 if (dstlen > 0 && pos + 1 >= dstlen) goto overflow;
2439 byte_pair <<= (6 - offset);
2440 OUTPUT( base64_encoding_table[byte_pair & 0x3f] );
2442 /* Windows always explicitly terminates the base64 sequence
2443 even though RFC 2152 (page 3, rule 2) does not require this */
2444 OUTPUT( '-' );
2447 return pos;
2449 overflow:
2450 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2451 return 0;
2452 #undef OUTPUT
2453 #undef ENCODABLE
2457 static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2458 const char *defchar, BOOL *used )
2460 DWORD reslen;
2461 NTSTATUS status;
2463 if (defchar || used)
2465 SetLastError( ERROR_INVALID_PARAMETER );
2466 return 0;
2468 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
2469 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
2471 SetLastError( ERROR_INVALID_FLAGS );
2472 return 0;
2474 if (!dstlen) dst = NULL;
2475 status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
2476 if (status == STATUS_SOME_NOT_MAPPED)
2478 if (flags & WC_ERR_INVALID_CHARS)
2480 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2481 return 0;
2484 else if (!set_ntstatus( status )) reslen = 0;
2485 return reslen;
2489 static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2490 char *dst, unsigned int dstlen )
2492 const char *table = info->WideCharTable;
2493 int ret = srclen;
2495 if (!dstlen) return srclen;
2497 if (dstlen < srclen)
2499 /* buffer too small: fill it up to dstlen and return error */
2500 srclen = dstlen;
2501 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2502 ret = 0;
2505 while (srclen >= 16)
2507 dst[0] = table[src[0]];
2508 dst[1] = table[src[1]];
2509 dst[2] = table[src[2]];
2510 dst[3] = table[src[3]];
2511 dst[4] = table[src[4]];
2512 dst[5] = table[src[5]];
2513 dst[6] = table[src[6]];
2514 dst[7] = table[src[7]];
2515 dst[8] = table[src[8]];
2516 dst[9] = table[src[9]];
2517 dst[10] = table[src[10]];
2518 dst[11] = table[src[11]];
2519 dst[12] = table[src[12]];
2520 dst[13] = table[src[13]];
2521 dst[14] = table[src[14]];
2522 dst[15] = table[src[15]];
2523 src += 16;
2524 dst += 16;
2525 srclen -= 16;
2528 /* now handle remaining characters */
2529 src += srclen;
2530 dst += srclen;
2531 switch(srclen)
2533 case 15: dst[-15] = table[src[-15]];
2534 case 14: dst[-14] = table[src[-14]];
2535 case 13: dst[-13] = table[src[-13]];
2536 case 12: dst[-12] = table[src[-12]];
2537 case 11: dst[-11] = table[src[-11]];
2538 case 10: dst[-10] = table[src[-10]];
2539 case 9: dst[-9] = table[src[-9]];
2540 case 8: dst[-8] = table[src[-8]];
2541 case 7: dst[-7] = table[src[-7]];
2542 case 6: dst[-6] = table[src[-6]];
2543 case 5: dst[-5] = table[src[-5]];
2544 case 4: dst[-4] = table[src[-4]];
2545 case 3: dst[-3] = table[src[-3]];
2546 case 2: dst[-2] = table[src[-2]];
2547 case 1: dst[-1] = table[src[-1]];
2548 case 0: break;
2550 return ret;
2554 static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2555 char *dst, unsigned int dstlen )
2557 const USHORT *table = info->WideCharTable;
2558 int i;
2560 if (!dstlen)
2562 for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++;
2563 return i;
2566 for (i = dstlen; srclen && i; i--, srclen--, src++)
2568 if (table[*src] & 0xff00)
2570 if (i == 1) break; /* do not output a partial char */
2571 i--;
2572 *dst++ = table[*src] >> 8;
2574 *dst++ = (char)table[*src];
2576 if (srclen)
2578 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2579 return 0;
2581 return dstlen - i;
2585 static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2587 const unsigned char *table = info->WideCharTable;
2589 if (wch >= 0x10000) return 0;
2590 if ((flags & WC_NO_BEST_FIT_CHARS) || table[wch] == info->DefaultChar)
2591 return (info->MultiByteTable[table[wch]] == wch);
2592 return 1;
2596 static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2598 const unsigned short *table = info->WideCharTable;
2599 unsigned short ch;
2601 if (wch >= 0x10000) return 0;
2602 ch = table[wch];
2603 if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
2605 if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch;
2606 return info->MultiByteTable[ch] == wch;
2608 return 1;
2612 static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2613 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2615 const char *table = info->WideCharTable;
2616 const char def = defchar ? *defchar : (char)info->DefaultChar;
2617 int i;
2618 BOOL tmp;
2619 WCHAR wch;
2620 unsigned int composed;
2622 if (!used) used = &tmp; /* avoid checking on every char */
2623 *used = FALSE;
2625 if (!dstlen)
2627 for (i = 0; srclen; i++, src++, srclen--)
2629 wch = *src;
2630 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2632 /* now check if we can use the composed char */
2633 if (is_valid_sbcs_mapping( info, flags, composed ))
2635 /* we have a good mapping, use it */
2636 src++;
2637 srclen--;
2638 continue;
2640 /* no mapping for the composed char, check the other flags */
2641 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2643 *used = TRUE;
2644 src++; /* skip the non-spacing char */
2645 srclen--;
2646 continue;
2648 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2650 src++;
2651 srclen--;
2653 /* WC_SEPCHARS is the default */
2655 if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch );
2657 return i;
2660 for (i = dstlen; srclen && i; dst++, i--, src++, srclen--)
2662 wch = *src;
2663 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2665 /* now check if we can use the composed char */
2666 if (is_valid_sbcs_mapping( info, flags, composed ))
2668 /* we have a good mapping, use it */
2669 *dst = table[composed];
2670 src++;
2671 srclen--;
2672 continue;
2674 /* no mapping for the composed char, check the other flags */
2675 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2677 *dst = def;
2678 *used = TRUE;
2679 src++; /* skip the non-spacing char */
2680 srclen--;
2681 continue;
2683 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2685 src++;
2686 srclen--;
2688 /* WC_SEPCHARS is the default */
2691 *dst = table[wch];
2692 if (!is_valid_sbcs_mapping( info, flags, wch ))
2694 *dst = def;
2695 *used = TRUE;
2698 if (srclen)
2700 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2701 return 0;
2703 return dstlen - i;
2707 static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2708 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2710 const USHORT *table = info->WideCharTable;
2711 WCHAR wch, defchar_value;
2712 unsigned int composed;
2713 unsigned short res;
2714 BOOL tmp;
2715 int i;
2717 if (!defchar[1]) defchar_value = (unsigned char)defchar[0];
2718 else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1];
2720 if (!used) used = &tmp; /* avoid checking on every char */
2721 *used = FALSE;
2723 if (!dstlen)
2725 if (!defchar && !used && !(flags & WC_COMPOSITECHECK))
2727 for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++;
2728 return i;
2730 for (i = 0; srclen; srclen--, src++, i++)
2732 wch = *src;
2733 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2735 /* now check if we can use the composed char */
2736 if (is_valid_dbcs_mapping( info, flags, composed ))
2738 /* we have a good mapping for the composed char, use it */
2739 res = table[composed];
2740 if (res & 0xff00) i++;
2741 src++;
2742 srclen--;
2743 continue;
2745 /* no mapping for the composed char, check the other flags */
2746 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2748 if (defchar_value & 0xff00) i++;
2749 *used = TRUE;
2750 src++; /* skip the non-spacing char */
2751 srclen--;
2752 continue;
2754 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2756 src++;
2757 srclen--;
2759 /* WC_SEPCHARS is the default */
2762 res = table[wch];
2763 if (!is_valid_dbcs_mapping( info, flags, wch ))
2765 res = defchar_value;
2766 *used = TRUE;
2768 if (res & 0xff00) i++;
2770 return i;
2774 for (i = dstlen; srclen && i; i--, srclen--, src++)
2776 wch = *src;
2777 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2779 /* now check if we can use the composed char */
2780 if (is_valid_dbcs_mapping( info, flags, composed ))
2782 /* we have a good mapping for the composed char, use it */
2783 res = table[composed];
2784 src++;
2785 srclen--;
2786 goto output_char;
2788 /* no mapping for the composed char, check the other flags */
2789 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2791 res = defchar_value;
2792 *used = TRUE;
2793 src++; /* skip the non-spacing char */
2794 srclen--;
2795 goto output_char;
2797 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2799 src++;
2800 srclen--;
2802 /* WC_SEPCHARS is the default */
2805 res = table[wch];
2806 if (!is_valid_dbcs_mapping( info, flags, wch ))
2808 res = defchar_value;
2809 *used = TRUE;
2812 output_char:
2813 if (res & 0xff00)
2815 if (i == 1) break; /* do not output a partial char */
2816 i--;
2817 *dst++ = res >> 8;
2819 *dst++ = (char)res;
2821 if (srclen)
2823 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2824 return 0;
2826 return dstlen - i;
2830 static int wcstombs_codepage( UINT codepage, DWORD flags, const WCHAR *src, int srclen,
2831 char *dst, int dstlen, const char *defchar, BOOL *used )
2833 const CPTABLEINFO *info = get_codepage_table( codepage );
2835 if (!info)
2837 SetLastError( ERROR_INVALID_PARAMETER );
2838 return 0;
2840 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
2841 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
2843 SetLastError( ERROR_INVALID_FLAGS );
2844 return 0;
2846 if (flags || defchar || used)
2848 if (!defchar) defchar = (const char *)&info->DefaultChar;
2849 if (info->DBCSOffsets)
2850 return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
2851 else
2852 return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
2854 if (info->DBCSOffsets)
2855 return wcstombs_dbcs( info, src, srclen, dst, dstlen );
2856 else
2857 return wcstombs_sbcs( info, src, srclen, dst, dstlen );
2861 static int get_sortkey( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen )
2863 WCHAR dummy[4]; /* no decomposition is larger than 4 chars */
2864 int key_len[4];
2865 char *key_ptr[4];
2866 const WCHAR *src_save = src;
2867 int srclen_save = srclen;
2869 key_len[0] = key_len[1] = key_len[2] = key_len[3] = 0;
2870 for (; srclen; srclen--, src++)
2872 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
2873 dummy[0] = *src;
2874 if (decomposed_len)
2876 for (i = 0; i < decomposed_len; i++)
2878 WCHAR wch = dummy[i];
2879 unsigned int ce;
2881 if ((flags & NORM_IGNORESYMBOLS) &&
2882 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
2883 continue;
2885 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
2887 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
2888 if (ce != (unsigned int)-1)
2890 if (ce >> 16) key_len[0] += 2;
2891 if ((ce >> 8) & 0xff) key_len[1]++;
2892 if ((ce >> 4) & 0x0f) key_len[2]++;
2893 if (ce & 1)
2895 if (wch >> 8) key_len[3]++;
2896 key_len[3]++;
2899 else
2901 key_len[0] += 2;
2902 if (wch >> 8) key_len[0]++;
2903 if (wch & 0xff) key_len[0]++;
2909 if (!dstlen) /* compute length */
2910 /* 4 * '\1' + key length */
2911 return key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4;
2913 if (dstlen < key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4 + 1)
2914 return 0; /* overflow */
2916 src = src_save;
2917 srclen = srclen_save;
2919 key_ptr[0] = dst;
2920 key_ptr[1] = key_ptr[0] + key_len[0] + 1;
2921 key_ptr[2] = key_ptr[1] + key_len[1] + 1;
2922 key_ptr[3] = key_ptr[2] + key_len[2] + 1;
2924 for (; srclen; srclen--, src++)
2926 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
2927 dummy[0] = *src;
2928 if (decomposed_len)
2930 for (i = 0; i < decomposed_len; i++)
2932 WCHAR wch = dummy[i];
2933 unsigned int ce;
2935 if ((flags & NORM_IGNORESYMBOLS) &&
2936 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
2937 continue;
2939 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
2941 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
2942 if (ce != (unsigned int)-1)
2944 WCHAR key;
2945 if ((key = ce >> 16))
2947 *key_ptr[0]++ = key >> 8;
2948 *key_ptr[0]++ = key & 0xff;
2950 /* make key 1 start from 2 */
2951 if ((key = (ce >> 8) & 0xff)) *key_ptr[1]++ = key + 1;
2952 /* make key 2 start from 2 */
2953 if ((key = (ce >> 4) & 0x0f)) *key_ptr[2]++ = key + 1;
2954 /* key 3 is always a character code */
2955 if (ce & 1)
2957 if (wch >> 8) *key_ptr[3]++ = wch >> 8;
2958 if (wch & 0xff) *key_ptr[3]++ = wch & 0xff;
2961 else
2963 *key_ptr[0]++ = 0xff;
2964 *key_ptr[0]++ = 0xfe;
2965 if (wch >> 8) *key_ptr[0]++ = wch >> 8;
2966 if (wch & 0xff) *key_ptr[0]++ = wch & 0xff;
2972 *key_ptr[0] = 1;
2973 *key_ptr[1] = 1;
2974 *key_ptr[2] = 1;
2975 *key_ptr[3]++ = 1;
2976 *key_ptr[3] = 0;
2977 return key_ptr[3] - dst;
2981 /* compose a full-width katakana. return consumed source characters. */
2982 static int compose_katakana( const WCHAR *src, int srclen, WCHAR *dst )
2984 static const BYTE katakana_map[] =
2986 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
2987 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
2988 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
2989 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
2990 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
2991 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
2992 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
2993 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
2995 WCHAR dummy;
2996 int shift;
2998 if (!dst) dst = &dummy;
3000 switch (*src)
3002 case 0x309b:
3003 case 0x309c:
3004 *dst = *src - 2;
3005 return 1;
3006 case 0x30f0:
3007 case 0x30f1:
3008 case 0x30fd:
3009 *dst = *src;
3010 break;
3011 default:
3012 shift = *src - 0xff61;
3013 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
3014 *dst = katakana_map[shift] | 0x3000;
3015 break;
3018 if (srclen <= 1) return 1;
3020 switch (src[1])
3022 case 0xff9e: /* datakuten (voiced sound) */
3023 if ((*src >= 0xff76 && *src <= 0xff84) || (*src >= 0xff8a && *src <= 0xff8e) || *src == 0x30fd)
3024 *dst += 1;
3025 else if (*src == 0xff73)
3026 *dst = 0x30f4; /* KATAKANA LETTER VU */
3027 else if (*src == 0xff9c)
3028 *dst = 0x30f7; /* KATAKANA LETTER VA */
3029 else if (*src == 0x30f0)
3030 *dst = 0x30f8; /* KATAKANA LETTER VI */
3031 else if (*src == 0x30f1)
3032 *dst = 0x30f9; /* KATAKANA LETTER VE */
3033 else if (*src == 0xff66)
3034 *dst = 0x30fa; /* KATAKANA LETTER VO */
3035 else
3036 return 1;
3037 break;
3038 case 0xff9f: /* handakuten (semi-voiced sound) */
3039 if (*src >= 0xff8a && *src <= 0xff8e)
3040 *dst += 2;
3041 else
3042 return 1;
3043 break;
3044 default:
3045 return 1;
3047 return 2;
3050 /* map one or two half-width characters to one full-width character */
3051 static int map_to_fullwidth( const WCHAR *src, int srclen, WCHAR *dst )
3053 INT n;
3055 if (*src <= '~' && *src > ' ' && *src != '\\')
3056 *dst = *src - 0x20 + 0xff00;
3057 else if (*src == ' ')
3058 *dst = 0x3000;
3059 else if (*src <= 0x00af && *src >= 0x00a2)
3061 static const BYTE misc_symbols_table[] =
3063 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3064 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3066 if (misc_symbols_table[*src - 0x00a2])
3067 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3068 else
3069 *dst = *src;
3071 else if (*src == 0x20a9) /* WON SIGN */
3072 *dst = 0xffe6;
3073 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3074 return n;
3075 else if (*src >= 0xffa0 && *src <= 0xffdc)
3077 static const BYTE hangul_mapping_table[] =
3079 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3080 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3081 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3082 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3083 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3084 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3085 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3086 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3089 if (hangul_mapping_table[*src - 0xffa0])
3090 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3091 else
3092 *dst = *src;
3094 else
3095 *dst = *src;
3097 return 1;
3100 /* decompose a full-width katakana character into one or two half-width characters. */
3101 static int decompose_katakana( WCHAR c, WCHAR *dst, int dstlen )
3103 static const BYTE katakana_map[] =
3105 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3106 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3107 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3108 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3109 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3110 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3111 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3112 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3113 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3114 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3115 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3116 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3117 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3119 int len = 0, shift = c - 0x3099;
3120 BYTE k;
3122 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
3124 k = katakana_map[shift];
3125 if (!k)
3127 if (dstlen > 0) *dst = c;
3128 len++;
3130 else if (k > 0x60)
3132 if (dstlen > 0) *dst = k | 0xff00;
3133 len++;
3135 else
3137 if (dstlen >= 2)
3139 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3140 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3142 len += 2;
3144 return len;
3147 /* map single full-width character to single or double half-width characters. */
3148 static int map_to_halfwidth( WCHAR c, WCHAR *dst, int dstlen )
3150 int n = decompose_katakana( c, dst, dstlen );
3151 if (n > 0) return n;
3153 if (c == 0x3000)
3154 *dst = ' ';
3155 else if (c == 0x3001)
3156 *dst = 0xff64;
3157 else if (c == 0x3002)
3158 *dst = 0xff61;
3159 else if (c == 0x300c || c == 0x300d)
3160 *dst = (c - 0x300c) + 0xff62;
3161 else if (c >= 0x3131 && c <= 0x3163)
3163 *dst = c - 0x3131 + 0xffa1;
3164 if (*dst >= 0xffbf) *dst += 3;
3165 if (*dst >= 0xffc8) *dst += 2;
3166 if (*dst >= 0xffd0) *dst += 2;
3167 if (*dst >= 0xffd8) *dst += 2;
3169 else if (c == 0x3164)
3170 *dst = 0xffa0;
3171 else if (c == 0x2019)
3172 *dst = '\'';
3173 else if (c == 0x201d)
3174 *dst = '"';
3175 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3176 *dst = c - 0xff00 + 0x20;
3177 else if (c >= 0xffe0 && c <= 0xffe6)
3179 static const WCHAR misc_symbol_map[] = { 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9 };
3180 *dst = misc_symbol_map[c - 0xffe0];
3182 else
3183 *dst = c;
3185 return 1;
3189 /* 32-bit collation element table format:
3190 * unicode weight - high 16 bit, diacritic weight - high 8 bit of low 16 bit,
3191 * case weight - high 4 bit of low 8 bit.
3194 enum weight { UNICODE_WEIGHT, DIACRITIC_WEIGHT, CASE_WEIGHT };
3196 static unsigned int get_weight( WCHAR ch, enum weight type )
3198 unsigned int ret;
3200 ret = collation_table[collation_table[collation_table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
3201 if (ret == ~0u) return ch;
3203 switch (type)
3205 case UNICODE_WEIGHT: return ret >> 16;
3206 case DIACRITIC_WEIGHT: return (ret >> 8) & 0xff;
3207 case CASE_WEIGHT: return (ret >> 4) & 0x0f;
3208 default: return 0;
3213 static void inc_str_pos( const WCHAR **str, int *len, unsigned int *dpos, unsigned int *dlen )
3215 (*dpos)++;
3216 if (*dpos == *dlen)
3218 *dpos = *dlen = 0;
3219 (*str)++;
3220 (*len)--;
3225 static int compare_weights(int flags, const WCHAR *str1, int len1,
3226 const WCHAR *str2, int len2, enum weight type )
3228 unsigned int ce1, ce2, dpos1 = 0, dpos2 = 0, dlen1 = 0, dlen2 = 0;
3229 const WCHAR *dstr1 = NULL, *dstr2 = NULL;
3231 while (len1 > 0 && len2 > 0)
3233 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3234 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3236 if (flags & NORM_IGNORESYMBOLS)
3238 int skip = 0;
3239 /* FIXME: not tested */
3240 if (get_char_type( CT_CTYPE1, dstr1[dpos1] ) & (C1_PUNCT | C1_SPACE))
3242 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3243 skip = 1;
3245 if (get_char_type( CT_CTYPE1, dstr2[dpos2] ) & (C1_PUNCT | C1_SPACE))
3247 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3248 skip = 1;
3250 if (skip) continue;
3253 /* hyphen and apostrophe are treated differently depending on
3254 * whether SORT_STRINGSORT specified or not
3256 if (type == UNICODE_WEIGHT && !(flags & SORT_STRINGSORT))
3258 if (dstr1[dpos1] == '-' || dstr1[dpos1] == '\'')
3260 if (dstr2[dpos2] != '-' && dstr2[dpos2] != '\'')
3262 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3263 continue;
3266 else if (dstr2[dpos2] == '-' || dstr2[dpos2] == '\'')
3268 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3269 continue;
3273 ce1 = get_weight( dstr1[dpos1], type );
3274 if (!ce1)
3276 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3277 continue;
3279 ce2 = get_weight( dstr2[dpos2], type );
3280 if (!ce2)
3282 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3283 continue;
3286 if (ce1 - ce2) return ce1 - ce2;
3288 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3289 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3291 while (len1)
3293 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3294 ce1 = get_weight( dstr1[dpos1], type );
3295 if (ce1) break;
3296 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3298 while (len2)
3300 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3301 ce2 = get_weight( dstr2[dpos2], type );
3302 if (ce2) break;
3303 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3305 return len1 - len2;
3309 static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare )
3311 static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
3312 int first, last, limit, dayinsecs;
3314 if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */
3315 if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */
3317 /* if year is 0 then date is in day-of-week format, otherwise
3318 * it's absolute date.
3320 if (!compare->wYear)
3322 /* wDay is interpreted as number of the week in the month
3323 * 5 means: the last week in the month */
3324 /* calculate the day of the first DayOfWeek in the month */
3325 first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1;
3326 /* check needed for the 5th weekday of the month */
3327 last = month_lengths[tf->Month - 1] +
3328 (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400))));
3329 limit = first + 7 * (compare->wDay - 1);
3330 if (limit > last) limit -= 7;
3332 else limit = compare->wDay;
3334 limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60;
3335 dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second;
3336 return dayinsecs - limit;
3340 static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local )
3342 int year;
3343 BOOL before_standard_date, after_daylight_date;
3344 LARGE_INTEGER t2;
3345 TIME_FIELDS tf;
3347 if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN;
3349 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
3350 if (info->StandardDate.wMonth == 0 ||
3351 (info->StandardDate.wYear == 0 &&
3352 (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 ||
3353 info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5)))
3355 SetLastError( ERROR_INVALID_PARAMETER );
3356 return TIME_ZONE_ID_INVALID;
3359 if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000;
3360 RtlTimeToTimeFields( &time, &tf );
3361 year = tf.Year;
3362 if (!is_local)
3364 t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000;
3365 RtlTimeToTimeFields( &t2, &tf );
3367 if (tf.Year == year)
3368 before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0;
3369 else
3370 before_standard_date = tf.Year < year;
3372 if (!is_local)
3374 t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000;
3375 RtlTimeToTimeFields( &t2, &tf );
3377 if (tf.Year == year)
3378 after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0;
3379 else
3380 after_daylight_date = tf.Year > year;
3382 if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */
3384 if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3386 else /* Down south */
3388 if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3390 return TIME_ZONE_ID_STANDARD;
3394 /* Note: the Internal_ functions are not documented. The number of parameters
3395 * should be correct, but their exact meaning may not.
3398 /******************************************************************************
3399 * Internal_EnumCalendarInfo (kernelbase.@)
3401 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc, LCID lcid, CALID id,
3402 CALTYPE type, BOOL unicode, BOOL ex,
3403 BOOL exex, LPARAM lparam )
3405 WCHAR buffer[256];
3406 CALID calendars[2] = { id };
3407 INT ret, i;
3409 if (!proc)
3411 SetLastError( ERROR_INVALID_PARAMETER );
3412 return FALSE;
3415 if (id == ENUM_ALL_CALENDARS)
3417 if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
3418 (WCHAR *)&calendars[0], sizeof(calendars[0]) / sizeof(WCHAR) )) return FALSE;
3419 if (!GetLocaleInfoW( lcid, LOCALE_IOPTIONALCALENDAR | LOCALE_RETURN_NUMBER,
3420 (WCHAR *)&calendars[1], sizeof(calendars[1]) / sizeof(WCHAR) )) calendars[1] = 0;
3423 for (i = 0; i < ARRAY_SIZE(calendars) && calendars[i]; i++)
3425 id = calendars[i];
3426 if (type & CAL_RETURN_NUMBER)
3427 ret = GetCalendarInfoW( lcid, id, type, NULL, 0, (LPDWORD)buffer );
3428 else if (unicode)
3429 ret = GetCalendarInfoW( lcid, id, type, buffer, ARRAY_SIZE(buffer), NULL );
3430 else
3432 WCHAR bufW[256];
3433 ret = GetCalendarInfoW( lcid, id, type, bufW, ARRAY_SIZE(bufW), NULL );
3434 if (ret) WideCharToMultiByte( CP_ACP, 0, bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
3437 if (ret)
3439 if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
3440 else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
3441 else ret = proc( buffer );
3443 if (!ret) break;
3445 return TRUE;
3449 /**************************************************************************
3450 * Internal_EnumDateFormats (kernelbase.@)
3452 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
3453 BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
3455 WCHAR buffer[256];
3456 LCTYPE lctype;
3457 CALID cal_id;
3458 INT ret;
3460 if (!proc)
3462 SetLastError( ERROR_INVALID_PARAMETER );
3463 return FALSE;
3465 if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER,
3466 (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR) ))
3467 return FALSE;
3469 switch (flags & ~LOCALE_USE_CP_ACP)
3471 case 0:
3472 case DATE_SHORTDATE:
3473 lctype = LOCALE_SSHORTDATE;
3474 break;
3475 case DATE_LONGDATE:
3476 lctype = LOCALE_SLONGDATE;
3477 break;
3478 case DATE_YEARMONTH:
3479 lctype = LOCALE_SYEARMONTH;
3480 break;
3481 default:
3482 FIXME( "unknown date format 0x%08lx\n", flags );
3483 SetLastError( ERROR_INVALID_PARAMETER );
3484 return FALSE;
3487 lctype |= flags & LOCALE_USE_CP_ACP;
3488 if (unicode)
3489 ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
3490 else
3491 ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
3493 if (ret)
3495 if (exex) ((DATEFMT_ENUMPROCEXEX)proc)( buffer, cal_id, lparam );
3496 else if (ex) ((DATEFMT_ENUMPROCEXW)proc)( buffer, cal_id );
3497 else proc( buffer );
3499 return TRUE;
3503 /******************************************************************************
3504 * Internal_EnumLanguageGroupLocales (kernelbase.@)
3506 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
3507 DWORD flags, LONG_PTR param, BOOL unicode )
3509 WCHAR name[10], value[10];
3510 DWORD name_len, value_len, type, index = 0, alt = 0;
3511 HKEY key, altkey;
3512 LCID lcid;
3514 if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
3516 SetLastError( ERROR_INVALID_PARAMETER );
3517 return FALSE;
3520 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
3521 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
3523 for (;;)
3525 name_len = ARRAY_SIZE(name);
3526 value_len = sizeof(value);
3527 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
3528 &type, (BYTE *)value, &value_len ))
3530 if (alt++) break;
3531 index = 0;
3532 continue;
3534 if (type != REG_SZ) continue;
3535 if (id != wcstoul( value, NULL, 16 )) continue;
3536 lcid = wcstoul( name, NULL, 16 );
3537 if (!unicode)
3539 char nameA[10];
3540 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3541 if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
3543 else if (!proc( id, lcid, name, param )) break;
3545 RegCloseKey( altkey );
3546 RegCloseKey( key );
3547 return TRUE;
3551 /***********************************************************************
3552 * Internal_EnumSystemCodePages (kernelbase.@)
3554 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
3555 BOOL unicode )
3557 WCHAR name[10];
3558 DWORD name_len, type, index = 0;
3559 HKEY key;
3561 if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
3563 for (;;)
3565 name_len = ARRAY_SIZE(name);
3566 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
3567 if (type != REG_SZ) continue;
3568 if (!wcstoul( name, NULL, 10 )) continue;
3569 if (!unicode)
3571 char nameA[10];
3572 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3573 if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
3575 else if (!proc( name )) break;
3577 RegCloseKey( key );
3578 return TRUE;
3582 /******************************************************************************
3583 * Internal_EnumSystemLanguageGroups (kernelbase.@)
3585 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
3586 DWORD flags, LONG_PTR param, BOOL unicode )
3588 WCHAR name[10], value[10], descr[80];
3589 DWORD name_len, value_len, type, index = 0;
3590 HKEY key;
3591 LGRPID id;
3593 if (!proc)
3595 SetLastError( ERROR_INVALID_PARAMETER );
3596 return FALSE;
3599 switch (flags)
3601 case 0:
3602 flags = LGRPID_INSTALLED;
3603 break;
3604 case LGRPID_INSTALLED:
3605 case LGRPID_SUPPORTED:
3606 break;
3607 default:
3608 SetLastError( ERROR_INVALID_FLAGS );
3609 return FALSE;
3612 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
3614 for (;;)
3616 name_len = ARRAY_SIZE(name);
3617 value_len = sizeof(value);
3618 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
3619 if (type != REG_SZ) continue;
3621 id = wcstoul( name, NULL, 16 );
3623 if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
3624 if (!LoadStringW( kernelbase_handle, id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
3625 TRACE( "%p: %lu %s %s %lx %Ix\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
3626 if (!unicode)
3628 char nameA[10], descrA[80];
3629 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3630 WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
3631 if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
3633 else if (!proc( id, name, descr, flags, param )) break;
3635 RegCloseKey( key );
3636 return TRUE;
3640 /**************************************************************************
3641 * Internal_EnumTimeFormats (kernelbase.@)
3643 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
3644 BOOL unicode, BOOL ex, LPARAM lparam )
3646 WCHAR buffer[256];
3647 LCTYPE lctype;
3648 INT ret;
3650 if (!proc)
3652 SetLastError( ERROR_INVALID_PARAMETER );
3653 return FALSE;
3655 switch (flags & ~LOCALE_USE_CP_ACP)
3657 case 0:
3658 lctype = LOCALE_STIMEFORMAT;
3659 break;
3660 case TIME_NOSECONDS:
3661 lctype = LOCALE_SSHORTTIME;
3662 break;
3663 default:
3664 FIXME( "Unknown time format %lx\n", flags );
3665 SetLastError( ERROR_INVALID_PARAMETER );
3666 return FALSE;
3669 lctype |= flags & LOCALE_USE_CP_ACP;
3670 if (unicode)
3671 ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
3672 else
3673 ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
3675 if (ret)
3677 if (ex) ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
3678 else proc( buffer );
3680 return TRUE;
3684 /******************************************************************************
3685 * Internal_EnumUILanguages (kernelbase.@)
3687 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
3688 LONG_PTR param, BOOL unicode )
3690 WCHAR nameW[LOCALE_NAME_MAX_LENGTH];
3691 char nameA[LOCALE_NAME_MAX_LENGTH];
3692 DWORD i;
3694 if (!proc)
3696 SetLastError( ERROR_INVALID_PARAMETER );
3697 return FALSE;
3699 if (flags & ~(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))
3701 SetLastError( ERROR_INVALID_FLAGS );
3702 return FALSE;
3705 for (i = 0; i < locale_table->nb_lcnames; i++)
3707 if (!lcnames_index[i].name) continue; /* skip invariant locale */
3708 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
3709 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
3710 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
3711 continue; /* skip alternate sorts */
3712 if (flags & MUI_LANGUAGE_NAME)
3714 const WCHAR *str = locale_strings + lcnames_index[i].name;
3716 if (unicode)
3718 memcpy( nameW, str + 1, (*str + 1) * sizeof(WCHAR) );
3719 if (!proc( nameW, param )) break;
3721 else
3723 WideCharToMultiByte( CP_ACP, 0, str + 1, -1, nameA, sizeof(nameA), NULL, NULL );
3724 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
3727 else
3729 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
3730 if (unicode)
3732 swprintf( nameW, ARRAY_SIZE(nameW), L"%04lx", lcnames_index[i].id );
3733 if (!proc( nameW, param )) break;
3735 else
3737 sprintf( nameA, "%04x", lcnames_index[i].id );
3738 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
3742 return TRUE;
3746 /******************************************************************************
3747 * CompareStringEx (kernelbase.@)
3749 INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1,
3750 const WCHAR *str2, int len2, NLSVERSIONINFO *version,
3751 void *reserved, LPARAM handle )
3753 DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | SORT_STRINGSORT |
3754 NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH | LOCALE_USE_CP_ACP;
3755 DWORD semistub_flags = NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE | LINGUISTIC_IGNOREDIACRITIC |
3756 SORT_DIGITSASNUMBERS | 0x10000000;
3757 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3758 INT ret;
3759 static int once;
3761 if (version) FIXME( "unexpected version parameter\n" );
3762 if (reserved) FIXME( "unexpected reserved value\n" );
3763 if (handle) FIXME( "unexpected handle\n" );
3765 if (!str1 || !str2)
3767 SetLastError( ERROR_INVALID_PARAMETER );
3768 return 0;
3771 if (flags & ~(supported_flags | semistub_flags))
3773 SetLastError( ERROR_INVALID_FLAGS );
3774 return 0;
3777 if (flags & semistub_flags)
3779 if (!once++) FIXME( "semi-stub behavior for flag(s) 0x%lx\n", flags & semistub_flags );
3782 if (len1 < 0) len1 = lstrlenW(str1);
3783 if (len2 < 0) len2 = lstrlenW(str2);
3785 ret = compare_weights( flags, str1, len1, str2, len2, UNICODE_WEIGHT );
3786 if (!ret)
3788 if (!(flags & NORM_IGNORENONSPACE))
3789 ret = compare_weights( flags, str1, len1, str2, len2, DIACRITIC_WEIGHT );
3790 if (!ret && !(flags & NORM_IGNORECASE))
3791 ret = compare_weights( flags, str1, len1, str2, len2, CASE_WEIGHT );
3793 if (!ret) return CSTR_EQUAL;
3794 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3798 /******************************************************************************
3799 * CompareStringA (kernelbase.@)
3801 INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1,
3802 const char *str2, int len2 )
3804 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3805 WCHAR *buf2W = buf1W + 130;
3806 LPWSTR str1W, str2W;
3807 INT len1W = 0, len2W = 0, ret;
3808 UINT locale_cp = CP_ACP;
3810 if (!str1 || !str2)
3812 SetLastError( ERROR_INVALID_PARAMETER );
3813 return 0;
3816 if (flags & SORT_DIGITSASNUMBERS)
3818 SetLastError( ERROR_INVALID_FLAGS );
3819 return 0;
3822 if (len1 < 0) len1 = strlen(str1);
3823 if (len2 < 0) len2 = strlen(str2);
3825 locale_cp = get_lcid_codepage( lcid, flags );
3826 if (len1)
3828 if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 );
3829 if (len1W) str1W = buf1W;
3830 else
3832 len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 );
3833 str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) );
3834 if (!str1W)
3836 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3837 return 0;
3839 MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W );
3842 else
3844 len1W = 0;
3845 str1W = buf1W;
3848 if (len2)
3850 if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 );
3851 if (len2W) str2W = buf2W;
3852 else
3854 len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 );
3855 str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) );
3856 if (!str2W)
3858 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
3859 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3860 return 0;
3862 MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W );
3865 else
3867 len2W = 0;
3868 str2W = buf2W;
3871 ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W );
3873 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
3874 if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W );
3875 return ret;
3879 /******************************************************************************
3880 * CompareStringW (kernelbase.@)
3882 INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1,
3883 const WCHAR *str2, int len2 )
3885 return CompareStringEx( NULL, flags, str1, len1, str2, len2, NULL, NULL, 0 );
3889 /******************************************************************************
3890 * CompareStringOrdinal (kernelbase.@)
3892 INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
3893 const WCHAR *str2, INT len2, BOOL ignore_case )
3895 int ret;
3897 if (!str1 || !str2)
3899 SetLastError( ERROR_INVALID_PARAMETER );
3900 return 0;
3902 if (len1 < 0) len1 = lstrlenW( str1 );
3903 if (len2 < 0) len2 = lstrlenW( str2 );
3905 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
3906 if (ret < 0) return CSTR_LESS_THAN;
3907 if (ret > 0) return CSTR_GREATER_THAN;
3908 return CSTR_EQUAL;
3912 /******************************************************************************
3913 * ConvertDefaultLocale (kernelbase.@)
3915 LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
3917 get_locale_by_id( &lcid, 0 );
3918 return lcid;
3922 /******************************************************************************
3923 * EnumCalendarInfoW (kernelbase.@)
3925 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
3926 CALID id, CALTYPE type )
3928 return Internal_EnumCalendarInfo( proc, lcid, id, type, TRUE, FALSE, FALSE, 0 );
3932 /******************************************************************************
3933 * EnumCalendarInfoExW (kernelbase.@)
3935 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
3936 CALID id, CALTYPE type )
3938 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, FALSE, 0 );
3941 /******************************************************************************
3942 * EnumCalendarInfoExEx (kernelbase.@)
3944 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
3945 LPCWSTR reserved, CALTYPE type, LPARAM lparam )
3947 LCID lcid = LocaleNameToLCID( locale, 0 );
3948 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, TRUE, lparam );
3952 /**************************************************************************
3953 * EnumDateFormatsW (kernelbase.@)
3955 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
3957 return Internal_EnumDateFormats( proc, lcid, flags, TRUE, FALSE, FALSE, 0 );
3961 /**************************************************************************
3962 * EnumDateFormatsExW (kernelbase.@)
3964 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
3966 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, FALSE, 0 );
3970 /**************************************************************************
3971 * EnumDateFormatsExEx (kernelbase.@)
3973 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
3974 DWORD flags, LPARAM lparam )
3976 LCID lcid = LocaleNameToLCID( locale, 0 );
3977 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, TRUE, lparam );
3982 /******************************************************************************
3983 * EnumDynamicTimeZoneInformation (kernelbase.@)
3985 DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index,
3986 DYNAMIC_TIME_ZONE_INFORMATION *info )
3988 DYNAMIC_TIME_ZONE_INFORMATION tz;
3989 LSTATUS ret;
3990 DWORD size;
3992 if (!info) return ERROR_INVALID_PARAMETER;
3994 size = ARRAY_SIZE(tz.TimeZoneKeyName);
3995 ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL );
3996 if (ret) return ret;
3998 tz.DynamicDaylightTimeDisabled = TRUE;
3999 if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError();
4001 lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName );
4002 info->DynamicDaylightTimeDisabled = FALSE;
4003 return 0;
4007 /******************************************************************************
4008 * EnumLanguageGroupLocalesW (kernelbase.@)
4010 BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
4011 DWORD flags, LONG_PTR param )
4013 return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
4017 /******************************************************************************
4018 * EnumUILanguagesW (kernelbase.@)
4020 BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
4022 return Internal_EnumUILanguages( proc, flags, param, TRUE );
4026 /***********************************************************************
4027 * EnumSystemCodePagesW (kernelbase.@)
4029 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
4031 return Internal_EnumSystemCodePages( proc, flags, TRUE );
4035 /******************************************************************************
4036 * EnumSystemGeoID (kernelbase.@)
4038 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc )
4040 INT i;
4042 TRACE( "(%ld, %ld, %p)\n", class, parent, proc );
4044 if (!proc)
4046 SetLastError( ERROR_INVALID_PARAMETER );
4047 return FALSE;
4049 if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL)
4051 SetLastError( ERROR_INVALID_FLAGS );
4052 return FALSE;
4055 for (i = 0; i < geo_ids_count; i++)
4057 if (class != GEOCLASS_ALL && geo_ids[i].class != class) continue;
4058 if (parent && geo_ids[i].parent != parent) continue;
4059 if (!proc( geo_ids[i].id )) break;
4061 return TRUE;
4065 /******************************************************************************
4066 * EnumSystemLanguageGroupsW (kernelbase.@)
4068 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
4069 DWORD flags, LONG_PTR param )
4071 return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
4075 /******************************************************************************
4076 * EnumSystemLocalesA (kernelbase.@)
4078 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
4080 char name[10];
4081 DWORD i;
4083 for (i = 0; i < locale_table->nb_lcnames; i++)
4085 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4086 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4087 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4088 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4089 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
4090 continue; /* skip alternate sorts */
4091 sprintf( name, "%08x", lcnames_index[i].id );
4092 if (!proc( name )) break;
4094 return TRUE;
4098 /******************************************************************************
4099 * EnumSystemLocalesW (kernelbase.@)
4101 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
4103 WCHAR name[10];
4104 DWORD i;
4106 for (i = 0; i < locale_table->nb_lcnames; i++)
4108 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4109 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4110 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4111 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4112 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
4113 continue; /* skip alternate sorts */
4114 swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id );
4115 if (!proc( name )) break;
4117 return TRUE;
4121 /******************************************************************************
4122 * EnumSystemLocalesEx (kernelbase.@)
4124 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
4125 LPARAM param, void *reserved )
4127 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
4128 DWORD i, flags;
4130 if (reserved)
4132 SetLastError( ERROR_INVALID_PARAMETER );
4133 return FALSE;
4136 for (i = 0; i < locale_table->nb_lcnames; i++)
4138 const NLS_LOCALE_DATA *locale = get_locale_data( lcnames_index[i].idx );
4139 const WCHAR *str = locale_strings + lcnames_index[i].name;
4141 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4142 memcpy( buffer, str + 1, (*str + 1) * sizeof(WCHAR) );
4143 if (SORTIDFROMLCID( lcnames_index[i].id ) || wcschr( str + 1, '_' ))
4144 flags = LOCALE_ALTERNATE_SORTS;
4145 else
4146 flags = LOCALE_WINDOWS | (locale->inotneutral ? LOCALE_SPECIFICDATA : LOCALE_NEUTRALDATA);
4147 if (wanted_flags && !(flags & wanted_flags)) continue;
4148 if (!proc( buffer, flags, param )) break;
4150 return TRUE;
4154 /**************************************************************************
4155 * EnumTimeFormatsW (kernelbase.@)
4157 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4159 return Internal_EnumTimeFormats( proc, lcid, flags, TRUE, FALSE, 0 );
4163 /**************************************************************************
4164 * EnumTimeFormatsEx (kernelbase.@)
4166 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
4167 DWORD flags, LPARAM lparam )
4169 LCID lcid = LocaleNameToLCID( locale, 0 );
4170 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, lparam );
4174 /**************************************************************************
4175 * FindNLSString (kernelbase.@)
4177 INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src,
4178 int srclen, const WCHAR *value, int valuelen, int *found )
4180 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
4182 LCIDToLocaleName( lcid, locale, ARRAY_SIZE(locale), 0 );
4183 return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 );
4187 /**************************************************************************
4188 * FindNLSStringEx (kernelbase.@)
4190 INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src,
4191 int srclen, const WCHAR *value, int valuelen, int *found,
4192 NLSVERSIONINFO *version, void *reserved, LPARAM handle )
4194 /* FIXME: this function should normalize strings before calling CompareStringEx() */
4195 DWORD mask = flags;
4196 int offset, inc, count;
4198 TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale), flags,
4199 wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found,
4200 version, reserved, handle );
4202 if (version || reserved || handle || !IsValidLocaleName(locale) ||
4203 !src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1)
4205 SetLastError( ERROR_INVALID_PARAMETER );
4206 return -1;
4208 if (srclen == -1) srclen = lstrlenW(src);
4209 if (valuelen == -1) valuelen = lstrlenW(value);
4211 srclen -= valuelen;
4212 if (srclen < 0) return -1;
4214 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
4215 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? srclen + 1 : 1;
4216 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : srclen;
4217 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4218 while (count--)
4220 if (CompareStringEx( locale, mask, src + offset, valuelen,
4221 value, valuelen, NULL, NULL, 0 ) == CSTR_EQUAL)
4223 if (found) *found = valuelen;
4224 return offset;
4226 offset += inc;
4228 return -1;
4232 /******************************************************************************
4233 * FindStringOrdinal (kernelbase.@)
4235 INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
4236 const WCHAR *val, INT val_size, BOOL ignore_case )
4238 INT offset, inc, count;
4240 TRACE( "%#lx %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
4241 wine_dbgstr_w(val), val_size, ignore_case );
4243 if (!src || !val)
4245 SetLastError( ERROR_INVALID_PARAMETER );
4246 return -1;
4249 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
4251 SetLastError( ERROR_INVALID_FLAGS );
4252 return -1;
4255 if (src_size == -1) src_size = lstrlenW( src );
4256 if (val_size == -1) val_size = lstrlenW( val );
4258 SetLastError( ERROR_SUCCESS );
4259 src_size -= val_size;
4260 if (src_size < 0) return -1;
4262 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
4263 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
4264 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4265 while (count--)
4267 if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
4268 return offset;
4269 offset += inc;
4271 return -1;
4275 /******************************************************************************
4276 * FoldStringW (kernelbase.@)
4278 INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
4280 NTSTATUS status;
4281 WCHAR *buf = dst;
4282 int len = dstlen;
4284 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
4286 SetLastError( ERROR_INVALID_PARAMETER );
4287 return 0;
4289 if (srclen == -1) srclen = lstrlenW(src) + 1;
4291 if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE)))
4293 len = srclen * 4;
4294 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4296 SetLastError( ERROR_OUTOFMEMORY );
4297 return 0;
4301 for (;;)
4303 status = fold_string( flags, src, srclen, buf, &len );
4304 if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf );
4305 if (status != STATUS_BUFFER_TOO_SMALL) break;
4306 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4308 SetLastError( ERROR_OUTOFMEMORY );
4309 return 0;
4312 if (status == STATUS_INVALID_PARAMETER_1)
4314 SetLastError( ERROR_INVALID_FLAGS );
4315 return 0;
4317 if (!set_ntstatus( status )) return 0;
4319 if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
4320 return len;
4324 static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang,
4325 BOOL ansi, WCHAR **buffer )
4327 DWORD len;
4329 if (!(flags & FORMAT_MESSAGE_FROM_STRING))
4331 const MESSAGE_RESOURCE_ENTRY *entry;
4332 NTSTATUS status = STATUS_INVALID_PARAMETER;
4334 if (flags & FORMAT_MESSAGE_FROM_HMODULE)
4336 HMODULE module = (HMODULE)src;
4337 if (!module) module = GetModuleHandleW( 0 );
4338 status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry );
4340 if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
4342 /* Fold win32 hresult to its embedded error code. */
4343 if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32)
4344 id = HRESULT_CODE( id );
4345 status = RtlFindMessage( kernelbase_handle, RT_MESSAGETABLE, lang, id, &entry );
4347 if (!set_ntstatus( status )) return NULL;
4349 src = entry->Text;
4350 ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE);
4353 if (!ansi) return src;
4354 len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
4355 if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
4356 MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len );
4357 return *buffer;
4361 /***********************************************************************
4362 * FormatMessageA (kernelbase.@)
4364 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4365 char *buffer, DWORD size, va_list *args )
4367 DWORD ret = 0;
4368 ULONG len, retsize = 0;
4369 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4370 const WCHAR *src;
4371 WCHAR *result, *message = NULL;
4372 NTSTATUS status;
4374 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4376 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4378 if (!buffer)
4380 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4381 return 0;
4383 *(char **)buffer = NULL;
4385 if (size >= 32768)
4387 SetLastError( ERROR_INVALID_PARAMETER );
4388 return 0;
4391 if (width == 0xff) width = ~0u;
4393 if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0;
4395 if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 )))
4396 status = STATUS_NO_MEMORY;
4397 else
4398 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4399 TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4400 result, 65536, &retsize );
4402 HeapFree( GetProcessHeap(), 0, message );
4404 if (status == STATUS_BUFFER_OVERFLOW)
4406 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4407 goto done;
4409 if (!set_ntstatus( status )) goto done;
4411 len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL );
4412 if (len <= 1)
4414 SetLastError( ERROR_NO_WORK_DONE );
4415 goto done;
4418 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4420 char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len ));
4421 if (!buf)
4423 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4424 goto done;
4426 *(char **)buffer = buf;
4427 WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL );
4429 else if (len > size)
4431 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4432 goto done;
4434 else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL );
4436 ret = len - 1;
4438 done:
4439 HeapFree( GetProcessHeap(), 0, result );
4440 return ret;
4444 /***********************************************************************
4445 * FormatMessageW (kernelbase.@)
4447 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4448 WCHAR *buffer, DWORD size, va_list *args )
4450 ULONG retsize = 0;
4451 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4452 const WCHAR *src;
4453 WCHAR *message = NULL;
4454 NTSTATUS status;
4456 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4458 if (!buffer)
4460 SetLastError( ERROR_INVALID_PARAMETER );
4461 return 0;
4464 if (width == 0xff) width = ~0u;
4466 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL;
4468 if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0;
4470 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4472 WCHAR *result;
4473 ULONG alloc = max( size * sizeof(WCHAR), 65536 );
4475 for (;;)
4477 if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc )))
4479 status = STATUS_NO_MEMORY;
4480 break;
4482 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4483 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4484 result, alloc, &retsize );
4485 if (!status)
4487 if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );
4488 else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
4489 result, max( retsize, size * sizeof(WCHAR) ));
4490 break;
4492 HeapFree( GetProcessHeap(), 0, result );
4493 if (status != STATUS_BUFFER_OVERFLOW) break;
4494 alloc *= 2;
4497 else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4498 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4499 buffer, size * sizeof(WCHAR), &retsize );
4501 HeapFree( GetProcessHeap(), 0, message );
4503 if (status == STATUS_BUFFER_OVERFLOW)
4505 if (size) buffer[size - 1] = 0;
4506 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4507 return 0;
4509 if (!set_ntstatus( status )) return 0;
4510 if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE );
4511 return retsize / sizeof(WCHAR) - 1;
4515 /******************************************************************************
4516 * GetACP (kernelbase.@)
4518 UINT WINAPI GetACP(void)
4520 return nls_info.AnsiTableInfo.CodePage;
4524 /***********************************************************************
4525 * GetCPInfo (kernelbase.@)
4527 BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
4529 const CPTABLEINFO *table;
4531 if (!cpinfo)
4533 SetLastError( ERROR_INVALID_PARAMETER );
4534 return FALSE;
4536 switch (codepage)
4538 case CP_UTF7:
4539 case CP_UTF8:
4540 cpinfo->DefaultChar[0] = 0x3f;
4541 cpinfo->DefaultChar[1] = 0;
4542 memset( cpinfo->LeadByte, 0, sizeof(cpinfo->LeadByte) );
4543 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
4544 break;
4545 default:
4546 if (!(table = get_codepage_table( codepage ))) return FALSE;
4547 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4548 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4549 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4550 break;
4552 return TRUE;
4556 /***********************************************************************
4557 * GetCPInfoExW (kernelbase.@)
4559 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
4561 const CPTABLEINFO *table;
4562 int min, max, pos;
4564 if (!cpinfo)
4566 SetLastError( ERROR_INVALID_PARAMETER );
4567 return FALSE;
4569 switch (codepage)
4571 case CP_UTF7:
4572 cpinfo->DefaultChar[0] = 0x3f;
4573 cpinfo->DefaultChar[1] = 0;
4574 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
4575 cpinfo->MaxCharSize = 5;
4576 cpinfo->CodePage = CP_UTF7;
4577 cpinfo->UnicodeDefaultChar = 0x3f;
4578 break;
4579 case CP_UTF8:
4580 cpinfo->DefaultChar[0] = 0x3f;
4581 cpinfo->DefaultChar[1] = 0;
4582 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
4583 cpinfo->MaxCharSize = 4;
4584 cpinfo->CodePage = CP_UTF8;
4585 cpinfo->UnicodeDefaultChar = 0x3f;
4586 break;
4587 default:
4588 if (!(table = get_codepage_table( codepage ))) return FALSE;
4589 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4590 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4591 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4592 cpinfo->CodePage = table->CodePage;
4593 cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
4594 break;
4597 min = 0;
4598 max = ARRAY_SIZE(codepage_names) - 1;
4599 cpinfo->CodePageName[0] = 0;
4600 while (min <= max)
4602 pos = (min + max) / 2;
4603 if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
4604 else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
4605 else
4607 wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
4608 break;
4611 return TRUE;
4615 /***********************************************************************
4616 * GetCalendarInfoW (kernelbase.@)
4618 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
4619 WCHAR *data, INT count, DWORD *value )
4621 static const LCTYPE lctype_map[] =
4623 0, /* not used */
4624 0, /* CAL_ICALINTVALUE */
4625 0, /* CAL_SCALNAME */
4626 0, /* CAL_IYEAROFFSETRANGE */
4627 0, /* CAL_SERASTRING */
4628 LOCALE_SSHORTDATE,
4629 LOCALE_SLONGDATE,
4630 LOCALE_SDAYNAME1,
4631 LOCALE_SDAYNAME2,
4632 LOCALE_SDAYNAME3,
4633 LOCALE_SDAYNAME4,
4634 LOCALE_SDAYNAME5,
4635 LOCALE_SDAYNAME6,
4636 LOCALE_SDAYNAME7,
4637 LOCALE_SABBREVDAYNAME1,
4638 LOCALE_SABBREVDAYNAME2,
4639 LOCALE_SABBREVDAYNAME3,
4640 LOCALE_SABBREVDAYNAME4,
4641 LOCALE_SABBREVDAYNAME5,
4642 LOCALE_SABBREVDAYNAME6,
4643 LOCALE_SABBREVDAYNAME7,
4644 LOCALE_SMONTHNAME1,
4645 LOCALE_SMONTHNAME2,
4646 LOCALE_SMONTHNAME3,
4647 LOCALE_SMONTHNAME4,
4648 LOCALE_SMONTHNAME5,
4649 LOCALE_SMONTHNAME6,
4650 LOCALE_SMONTHNAME7,
4651 LOCALE_SMONTHNAME8,
4652 LOCALE_SMONTHNAME9,
4653 LOCALE_SMONTHNAME10,
4654 LOCALE_SMONTHNAME11,
4655 LOCALE_SMONTHNAME12,
4656 LOCALE_SMONTHNAME13,
4657 LOCALE_SABBREVMONTHNAME1,
4658 LOCALE_SABBREVMONTHNAME2,
4659 LOCALE_SABBREVMONTHNAME3,
4660 LOCALE_SABBREVMONTHNAME4,
4661 LOCALE_SABBREVMONTHNAME5,
4662 LOCALE_SABBREVMONTHNAME6,
4663 LOCALE_SABBREVMONTHNAME7,
4664 LOCALE_SABBREVMONTHNAME8,
4665 LOCALE_SABBREVMONTHNAME9,
4666 LOCALE_SABBREVMONTHNAME10,
4667 LOCALE_SABBREVMONTHNAME11,
4668 LOCALE_SABBREVMONTHNAME12,
4669 LOCALE_SABBREVMONTHNAME13,
4670 LOCALE_SYEARMONTH,
4671 0, /* CAL_ITWODIGITYEARMAX */
4672 LOCALE_SSHORTESTDAYNAME1,
4673 LOCALE_SSHORTESTDAYNAME2,
4674 LOCALE_SSHORTESTDAYNAME3,
4675 LOCALE_SSHORTESTDAYNAME4,
4676 LOCALE_SSHORTESTDAYNAME5,
4677 LOCALE_SSHORTESTDAYNAME6,
4678 LOCALE_SSHORTESTDAYNAME7,
4679 LOCALE_SMONTHDAY,
4680 0, /* CAL_SABBREVERASTRING */
4682 DWORD flags = 0;
4683 CALTYPE calinfo = type & 0xffff;
4685 if (type & CAL_NOUSEROVERRIDE) FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
4686 if (type & CAL_USE_CP_ACP) FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
4688 if ((type & CAL_RETURN_NUMBER) && !value)
4690 SetLastError( ERROR_INVALID_PARAMETER );
4691 return 0;
4694 if (type & CAL_RETURN_GENITIVE_NAMES) flags |= LOCALE_RETURN_GENITIVE_NAMES;
4696 switch (calinfo)
4698 case CAL_ICALINTVALUE:
4699 if (type & CAL_RETURN_NUMBER)
4700 return GetLocaleInfoW( lcid, LOCALE_RETURN_NUMBER | LOCALE_ICALENDARTYPE,
4701 (WCHAR *)value, sizeof(*value) / sizeof(WCHAR) );
4702 return GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE, data, count );
4704 case CAL_SCALNAME:
4705 FIXME( "Unimplemented caltype %ld\n", calinfo );
4706 if (data) *data = 0;
4707 return 1;
4709 case CAL_IYEAROFFSETRANGE:
4710 case CAL_SERASTRING:
4711 case CAL_SABBREVERASTRING:
4712 FIXME( "Unimplemented caltype %ld\n", calinfo );
4713 return 0;
4715 case CAL_SSHORTDATE:
4716 case CAL_SLONGDATE:
4717 case CAL_SDAYNAME1:
4718 case CAL_SDAYNAME2:
4719 case CAL_SDAYNAME3:
4720 case CAL_SDAYNAME4:
4721 case CAL_SDAYNAME5:
4722 case CAL_SDAYNAME6:
4723 case CAL_SDAYNAME7:
4724 case CAL_SABBREVDAYNAME1:
4725 case CAL_SABBREVDAYNAME2:
4726 case CAL_SABBREVDAYNAME3:
4727 case CAL_SABBREVDAYNAME4:
4728 case CAL_SABBREVDAYNAME5:
4729 case CAL_SABBREVDAYNAME6:
4730 case CAL_SABBREVDAYNAME7:
4731 case CAL_SMONTHNAME1:
4732 case CAL_SMONTHNAME2:
4733 case CAL_SMONTHNAME3:
4734 case CAL_SMONTHNAME4:
4735 case CAL_SMONTHNAME5:
4736 case CAL_SMONTHNAME6:
4737 case CAL_SMONTHNAME7:
4738 case CAL_SMONTHNAME8:
4739 case CAL_SMONTHNAME9:
4740 case CAL_SMONTHNAME10:
4741 case CAL_SMONTHNAME11:
4742 case CAL_SMONTHNAME12:
4743 case CAL_SMONTHNAME13:
4744 case CAL_SABBREVMONTHNAME1:
4745 case CAL_SABBREVMONTHNAME2:
4746 case CAL_SABBREVMONTHNAME3:
4747 case CAL_SABBREVMONTHNAME4:
4748 case CAL_SABBREVMONTHNAME5:
4749 case CAL_SABBREVMONTHNAME6:
4750 case CAL_SABBREVMONTHNAME7:
4751 case CAL_SABBREVMONTHNAME8:
4752 case CAL_SABBREVMONTHNAME9:
4753 case CAL_SABBREVMONTHNAME10:
4754 case CAL_SABBREVMONTHNAME11:
4755 case CAL_SABBREVMONTHNAME12:
4756 case CAL_SABBREVMONTHNAME13:
4757 case CAL_SMONTHDAY:
4758 case CAL_SYEARMONTH:
4759 case CAL_SSHORTESTDAYNAME1:
4760 case CAL_SSHORTESTDAYNAME2:
4761 case CAL_SSHORTESTDAYNAME3:
4762 case CAL_SSHORTESTDAYNAME4:
4763 case CAL_SSHORTESTDAYNAME5:
4764 case CAL_SSHORTESTDAYNAME6:
4765 case CAL_SSHORTESTDAYNAME7:
4766 return GetLocaleInfoW( lcid, lctype_map[calinfo] | flags, data, count );
4768 case CAL_ITWODIGITYEARMAX:
4769 if (type & CAL_RETURN_NUMBER)
4771 *value = CALINFO_MAX_YEAR;
4772 return sizeof(DWORD) / sizeof(WCHAR);
4774 else
4776 WCHAR buffer[10];
4777 int ret = swprintf( buffer, ARRAY_SIZE(buffer), L"%u", CALINFO_MAX_YEAR ) + 1;
4778 if (!data) return ret;
4779 if (ret <= count)
4781 lstrcpyW( data, buffer );
4782 return ret;
4784 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4785 return 0;
4787 break;
4788 default:
4789 FIXME( "Unknown caltype %ld\n", calinfo );
4790 SetLastError( ERROR_INVALID_FLAGS );
4791 return 0;
4793 return 0;
4797 /***********************************************************************
4798 * GetCalendarInfoEx (kernelbase.@)
4800 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *locale, CALID calendar, const WCHAR *reserved,
4801 CALTYPE type, WCHAR *data, INT count, DWORD *value )
4803 LCID lcid = LocaleNameToLCID( locale, 0 );
4804 return GetCalendarInfoW( lcid, calendar, type, data, count, value );
4807 static CRITICAL_SECTION tzname_section;
4808 static CRITICAL_SECTION_DEBUG tzname_section_debug =
4810 0, 0, &tzname_section,
4811 { &tzname_section_debug.ProcessLocksList, &tzname_section_debug.ProcessLocksList },
4812 0, 0, { (DWORD_PTR)(__FILE__ ": tzname_section") }
4814 static CRITICAL_SECTION tzname_section = { &tzname_section_debug, -1, 0, 0, 0, 0 };
4815 static struct {
4816 LCID lcid;
4817 WCHAR key_name[128];
4818 WCHAR standard_name[32];
4819 WCHAR daylight_name[32];
4820 } cached_tzname;
4822 /***********************************************************************
4823 * GetDynamicTimeZoneInformation (kernelbase.@)
4825 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info )
4827 HKEY key;
4828 LARGE_INTEGER now;
4830 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info )))
4831 return TIME_ZONE_ID_INVALID;
4833 RtlEnterCriticalSection( &tzname_section );
4834 if (cached_tzname.lcid == GetThreadLocale() &&
4835 !wcscmp( info->TimeZoneKeyName, cached_tzname.key_name ))
4837 wcscpy( info->StandardName, cached_tzname.standard_name );
4838 wcscpy( info->DaylightName, cached_tzname.daylight_name );
4839 RtlLeaveCriticalSection( &tzname_section );
4841 else
4843 RtlLeaveCriticalSection( &tzname_section );
4844 if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))
4846 RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
4847 sizeof(info->StandardName), NULL, 0, system_dir );
4848 RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
4849 sizeof(info->DaylightName), NULL, 0, system_dir );
4850 RegCloseKey( key );
4852 else return TIME_ZONE_ID_INVALID;
4854 RtlEnterCriticalSection( &tzname_section );
4855 cached_tzname.lcid = GetThreadLocale();
4856 wcscpy( cached_tzname.key_name, info->TimeZoneKeyName );
4857 wcscpy( cached_tzname.standard_name, info->StandardName );
4858 wcscpy( cached_tzname.daylight_name, info->DaylightName );
4859 RtlLeaveCriticalSection( &tzname_section );
4862 NtQuerySystemTime( &now );
4863 return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE );
4867 /******************************************************************************
4868 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
4870 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info,
4871 DWORD *first, DWORD *last )
4873 HKEY key, dst_key = 0;
4874 DWORD type, count, ret = ERROR_FILE_NOT_FOUND;
4876 if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret;
4878 if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done;
4879 count = sizeof(DWORD);
4880 if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done;
4881 if (type != REG_DWORD) goto done;
4882 count = sizeof(DWORD);
4883 if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done;
4884 if (type != REG_DWORD) goto done;
4885 ret = 0;
4887 done:
4888 RegCloseKey( dst_key );
4889 RegCloseKey( key );
4890 return ret;
4894 /******************************************************************************
4895 * GetFileMUIInfo (kernelbase.@)
4897 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIInfo( DWORD flags, const WCHAR *path,
4898 FILEMUIINFO *info, DWORD *size )
4900 FIXME( "stub: %lu, %s, %p, %p\n", flags, debugstr_w(path), info, size );
4901 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4902 return FALSE;
4906 /******************************************************************************
4907 * GetFileMUIPath (kernelbase.@)
4909 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags, const WCHAR *filepath,
4910 WCHAR *language, ULONG *languagelen,
4911 WCHAR *muipath, ULONG *muipathlen,
4912 ULONGLONG *enumerator )
4914 FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
4915 debugstr_w(language), languagelen, muipath, muipathlen, enumerator );
4916 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4917 return FALSE;
4921 /******************************************************************************
4922 * GetGeoInfoW (kernelbase.@)
4924 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang )
4926 const struct geo_id *ptr = find_geo_id_entry( id );
4928 TRACE( "%ld %ld %p %d %d\n", id, type, data, count, lang );
4930 if (!ptr)
4932 SetLastError( ERROR_INVALID_PARAMETER );
4933 return 0;
4935 return get_geo_info( ptr, type, data, count, lang );
4939 /******************************************************************************
4940 * GetLocaleInfoA (kernelbase.@)
4942 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
4944 WCHAR *bufferW;
4945 INT lenW, ret;
4947 TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid, lctype, buffer, len );
4949 if (len < 0 || (len && !buffer))
4951 SetLastError( ERROR_INVALID_PARAMETER );
4952 return 0;
4954 if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
4956 SetLastError( ERROR_INVALID_FLAGS );
4957 return 0;
4960 if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
4961 return GetLocaleInfoW( lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) ) * sizeof(WCHAR);
4963 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
4965 if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
4967 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4968 return 0;
4970 ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW );
4971 if (ret) ret = WideCharToMultiByte( get_lcid_codepage( lcid, lctype ), 0,
4972 bufferW, ret, buffer, len, NULL, NULL );
4973 RtlFreeHeap( GetProcessHeap(), 0, bufferW );
4974 return ret;
4978 /******************************************************************************
4979 * GetLocaleInfoW (kernelbase.@)
4981 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
4983 const NLS_LOCALE_DATA *locale;
4985 if (len < 0 || (len && !buffer))
4987 SetLastError( ERROR_INVALID_PARAMETER );
4988 return 0;
4991 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
4993 if (!(locale = get_locale_by_id( &lcid, 0 )))
4995 SetLastError( ERROR_INVALID_PARAMETER );
4996 return 0;
4998 return get_locale_info( locale, lcid, lctype, buffer, len );
5002 /******************************************************************************
5003 * GetLocaleInfoEx (kernelbase.@)
5005 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *name, LCTYPE info, WCHAR *buffer, INT len )
5007 LCID lcid;
5008 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5010 TRACE( "%s 0x%lx %p %d\n", debugstr_w(name), info, buffer, len );
5012 if (!locale)
5014 SetLastError( ERROR_INVALID_PARAMETER );
5015 return 0;
5017 return get_locale_info( locale, lcid, info, buffer, len );
5021 /******************************************************************************
5022 * GetNLSVersion (kernelbase.@)
5024 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info )
5026 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
5028 if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId ))
5030 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5031 return FALSE;
5033 if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES ))
5035 SetLastError( ERROR_INVALID_PARAMETER );
5036 return FALSE;
5038 return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info );
5042 /******************************************************************************
5043 * GetNLSVersionEx (kernelbase.@)
5045 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale,
5046 NLSVERSIONINFOEX *info )
5048 LCID lcid = 0;
5050 if (func != COMPARE_STRING)
5052 SetLastError( ERROR_INVALID_FLAGS );
5053 return FALSE;
5055 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5056 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5058 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5059 return FALSE;
5062 if (!(lcid = LocaleNameToLCID( locale, 0 ))) return FALSE;
5064 info->dwNLSVersion = info->dwDefinedVersion = sort.version;
5065 if (info->dwNLSVersionInfoSize >= sizeof(*info))
5067 const struct sortguid *sortid = get_language_sort( locale );
5068 info->dwEffectiveId = lcid;
5069 info->guidCustomVersion = sortid ? sortid->id : default_sort_guid;
5071 return TRUE;
5075 /******************************************************************************
5076 * GetOEMCP (kernelbase.@)
5078 UINT WINAPI GetOEMCP(void)
5080 return nls_info.OemTableInfo.CodePage;
5084 /***********************************************************************
5085 * GetProcessPreferredUILanguages (kernelbase.@)
5087 BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPreferredUILanguages( DWORD flags, ULONG *count,
5088 WCHAR *buffer, ULONG *size )
5090 return set_ntstatus( RtlGetProcessPreferredUILanguages( flags, count, buffer, size ));
5094 /***********************************************************************
5095 * GetStringTypeA (kernelbase.@)
5097 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
5098 WORD *chartype )
5100 UINT cp;
5101 INT countW;
5102 LPWSTR srcW;
5103 BOOL ret = FALSE;
5105 if (count == -1) count = strlen(src) + 1;
5107 cp = get_lcid_codepage( locale, 0 );
5108 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
5109 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5111 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
5113 * NOTE: the target buffer has 1 word for each CHARACTER in the source
5114 * string, with multibyte characters there maybe be more bytes in count
5115 * than character space in the buffer!
5117 ret = GetStringTypeW(type, srcW, countW, chartype);
5118 HeapFree(GetProcessHeap(), 0, srcW);
5120 return ret;
5124 /***********************************************************************
5125 * GetStringTypeW (kernelbase.@)
5127 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
5129 if (!src)
5131 SetLastError( ERROR_INVALID_PARAMETER );
5132 return FALSE;
5134 if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3)
5136 SetLastError( ERROR_INVALID_PARAMETER );
5137 return FALSE;
5140 if (count == -1) count = lstrlenW(src) + 1;
5142 while (count--) *chartype++ = get_char_type( type, *src++ );
5144 return TRUE;
5148 /***********************************************************************
5149 * GetStringTypeExW (kernelbase.@)
5151 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
5152 WORD *chartype )
5154 /* locale is ignored for Unicode */
5155 return GetStringTypeW( type, src, count, chartype );
5159 /***********************************************************************
5160 * GetSystemDefaultLCID (kernelbase.@)
5162 LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
5164 return system_lcid;
5168 /***********************************************************************
5169 * GetSystemDefaultLangID (kernelbase.@)
5171 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
5173 return LANGIDFROMLCID( GetSystemDefaultLCID() );
5177 /***********************************************************************
5178 * GetSystemDefaultLocaleName (kernelbase.@)
5180 INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT count )
5182 return get_locale_info( system_locale, system_lcid, LOCALE_SNAME, name, count );
5186 /***********************************************************************
5187 * GetSystemDefaultUILanguage (kernelbase.@)
5189 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
5191 LANGID lang;
5192 NtQueryInstallUILanguage( &lang );
5193 return lang;
5197 /***********************************************************************
5198 * GetSystemPreferredUILanguages (kernelbase.@)
5200 BOOL WINAPI DECLSPEC_HOTPATCH GetSystemPreferredUILanguages( DWORD flags, ULONG *count,
5201 WCHAR *buffer, ULONG *size )
5203 return set_ntstatus( RtlGetSystemPreferredUILanguages( flags, 0, count, buffer, size ));
5207 /***********************************************************************
5208 * GetThreadPreferredUILanguages (kernelbase.@)
5210 BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPreferredUILanguages( DWORD flags, ULONG *count,
5211 WCHAR *buffer, ULONG *size )
5213 return set_ntstatus( RtlGetThreadPreferredUILanguages( flags, count, buffer, size ));
5217 /***********************************************************************
5218 * GetTimeZoneInformation (kernelbase.@)
5220 DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info )
5222 DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
5223 DWORD ret = GetDynamicTimeZoneInformation( &tzinfo );
5225 memcpy( info, &tzinfo, sizeof(*info) );
5226 return ret;
5230 /***********************************************************************
5231 * GetTimeZoneInformationForYear (kernelbase.@)
5233 BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year,
5234 DYNAMIC_TIME_ZONE_INFORMATION *dynamic,
5235 TIME_ZONE_INFORMATION *info )
5237 DYNAMIC_TIME_ZONE_INFORMATION local_info;
5238 HKEY key = 0, dst_key;
5239 DWORD count;
5240 LRESULT ret;
5241 struct
5243 LONG bias;
5244 LONG std_bias;
5245 LONG dlt_bias;
5246 SYSTEMTIME std_date;
5247 SYSTEMTIME dlt_date;
5248 } data;
5250 TRACE( "(%u,%p)\n", year, info );
5252 if (!dynamic)
5254 if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE;
5255 dynamic = &local_info;
5258 if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done;
5259 if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
5260 sizeof(info->StandardName), NULL, 0, system_dir ))
5262 count = sizeof(info->StandardName);
5263 if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count )))
5264 goto done;
5266 if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
5267 sizeof(info->DaylightName), NULL, 0, system_dir ))
5269 count = sizeof(info->DaylightName);
5270 if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count )))
5271 goto done;
5274 ret = ERROR_FILE_NOT_FOUND;
5275 if (!dynamic->DynamicDaylightTimeDisabled &&
5276 !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key ))
5278 WCHAR yearW[16];
5279 swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year );
5280 count = sizeof(data);
5281 ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count );
5282 RegCloseKey( dst_key );
5284 if (ret)
5286 count = sizeof(data);
5287 ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count );
5290 if (!ret)
5292 info->Bias = data.bias;
5293 info->StandardBias = data.std_bias;
5294 info->DaylightBias = data.dlt_bias;
5295 info->StandardDate = data.std_date;
5296 info->DaylightDate = data.dlt_date;
5299 done:
5300 RegCloseKey( key );
5301 if (ret) SetLastError( ret );
5302 return !ret;
5306 /***********************************************************************
5307 * GetUserDefaultLCID (kernelbase.@)
5309 LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
5311 return user_lcid;
5315 /***********************************************************************
5316 * GetUserDefaultLangID (kernelbase.@)
5318 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
5320 return LANGIDFROMLCID( GetUserDefaultLCID() );
5324 /***********************************************************************
5325 * GetUserDefaultLocaleName (kernelbase.@)
5327 INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
5329 return get_locale_info( user_locale, user_lcid, LOCALE_SNAME, name, len );
5333 /***********************************************************************
5334 * GetUserDefaultUILanguage (kernelbase.@)
5336 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
5338 LANGID lang;
5339 NtQueryDefaultUILanguage( &lang );
5340 return lang;
5344 /******************************************************************************
5345 * GetUserGeoID (kernelbase.@)
5347 GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass )
5349 GEOID ret = 39070;
5350 const WCHAR *name;
5351 WCHAR bufferW[40];
5352 HKEY hkey;
5354 switch (geoclass)
5356 case GEOCLASS_NATION:
5357 name = L"Nation";
5358 break;
5359 case GEOCLASS_REGION:
5360 name = L"Region";
5361 break;
5362 default:
5363 WARN("Unknown geoclass %ld\n", geoclass);
5364 return GEOID_NOT_AVAILABLE;
5366 if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey ))
5368 DWORD count = sizeof(bufferW);
5369 if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count ))
5370 ret = wcstol( bufferW, NULL, 10 );
5371 RegCloseKey( hkey );
5373 return ret;
5377 /******************************************************************************
5378 * GetUserPreferredUILanguages (kernelbase.@)
5380 BOOL WINAPI DECLSPEC_HOTPATCH GetUserPreferredUILanguages( DWORD flags, ULONG *count,
5381 WCHAR *buffer, ULONG *size )
5383 return set_ntstatus( RtlGetUserPreferredUILanguages( flags, 0, count, buffer, size ));
5387 /******************************************************************************
5388 * IdnToAscii (kernelbase.@)
5390 INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen,
5391 WCHAR *dst, INT dstlen )
5393 NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
5394 if (!set_ntstatus( status )) return 0;
5395 return dstlen;
5399 /******************************************************************************
5400 * IdnToNameprepUnicode (kernelbase.@)
5402 INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
5403 WCHAR *dst, INT dstlen )
5405 NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen );
5406 if (!set_ntstatus( status )) return 0;
5407 return dstlen;
5411 /******************************************************************************
5412 * IdnToUnicode (kernelbase.@)
5414 INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen,
5415 WCHAR *dst, INT dstlen )
5417 NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen );
5418 if (!set_ntstatus( status )) return 0;
5419 return dstlen;
5423 /******************************************************************************
5424 * IsCharAlphaA (kernelbase.@)
5426 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
5428 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5429 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5433 /******************************************************************************
5434 * IsCharAlphaW (kernelbase.@)
5436 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
5438 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5442 /******************************************************************************
5443 * IsCharAlphaNumericA (kernelbase.@)
5445 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
5447 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5448 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5452 /******************************************************************************
5453 * IsCharAlphaNumericW (kernelbase.@)
5455 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
5457 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5461 /******************************************************************************
5462 * IsCharBlankW (kernelbase.@)
5464 BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
5466 return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK);
5470 /******************************************************************************
5471 * IsCharCntrlW (kernelbase.@)
5473 BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
5475 return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL);
5479 /******************************************************************************
5480 * IsCharDigitW (kernelbase.@)
5482 BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
5484 return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT);
5488 /******************************************************************************
5489 * IsCharLowerA (kernelbase.@)
5491 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
5493 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5494 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5498 /******************************************************************************
5499 * IsCharLowerW (kernelbase.@)
5501 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
5503 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5507 /******************************************************************************
5508 * IsCharPunctW (kernelbase.@)
5510 BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
5512 return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT);
5516 /******************************************************************************
5517 * IsCharSpaceA (kernelbase.@)
5519 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
5521 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5522 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5526 /******************************************************************************
5527 * IsCharSpaceW (kernelbase.@)
5529 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
5531 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5535 /******************************************************************************
5536 * IsCharUpperA (kernelbase.@)
5538 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
5540 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5541 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5545 /******************************************************************************
5546 * IsCharUpperW (kernelbase.@)
5548 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
5550 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5554 /******************************************************************************
5555 * IsCharXDigitW (kernelbase.@)
5557 BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
5559 return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT);
5563 /******************************************************************************
5564 * IsDBCSLeadByte (kernelbase.@)
5566 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
5568 return nls_info.AnsiTableInfo.DBCSCodePage && nls_info.AnsiTableInfo.DBCSOffsets[testchar];
5572 /******************************************************************************
5573 * IsDBCSLeadByteEx (kernelbase.@)
5575 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
5577 const CPTABLEINFO *table = get_codepage_table( codepage );
5578 return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
5582 /******************************************************************************
5583 * IsNormalizedString (kernelbase.@)
5585 BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
5587 BOOLEAN res;
5588 if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
5589 return res;
5593 /******************************************************************************
5594 * IsValidCodePage (kernelbase.@)
5596 BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
5598 switch (codepage)
5600 case CP_ACP:
5601 case CP_OEMCP:
5602 case CP_MACCP:
5603 case CP_THREAD_ACP:
5604 return FALSE;
5605 case CP_UTF7:
5606 case CP_UTF8:
5607 return TRUE;
5608 default:
5609 return get_codepage_table( codepage ) != NULL;
5614 /******************************************************************************
5615 * IsValidLanguageGroup (kernelbase.@)
5617 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
5619 WCHAR name[10], value[10];
5620 DWORD type, value_len = sizeof(value);
5621 BOOL ret = FALSE;
5622 HKEY key;
5624 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
5626 swprintf( name, ARRAY_SIZE(name), L"%x", id );
5627 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
5628 ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
5630 RegCloseKey( key );
5631 return ret;
5635 /******************************************************************************
5636 * IsValidLocale (kernelbase.@)
5638 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
5640 return !!get_locale_by_id( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES );
5644 /******************************************************************************
5645 * IsValidLocaleName (kernelbase.@)
5647 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
5649 if (locale == LOCALE_NAME_USER_DEFAULT) return FALSE;
5650 return !!find_lcname_entry( locale );
5654 /******************************************************************************
5655 * IsValidNLSVersion (kernelbase.@)
5657 DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale,
5658 NLSVERSIONINFOEX *info )
5660 static const GUID GUID_NULL;
5661 NLSVERSIONINFOEX infoex;
5662 DWORD ret;
5664 if (func != COMPARE_STRING)
5666 SetLastError( ERROR_INVALID_PARAMETER );
5667 return FALSE;
5669 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5670 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5672 SetLastError( ERROR_INVALID_PARAMETER );
5673 return FALSE;
5675 infoex.dwNLSVersionInfoSize = sizeof(infoex);
5676 if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE;
5678 ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff);
5679 if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL ))
5680 ret = find_sortguid( &info->guidCustomVersion ) != NULL;
5682 if (!ret) SetLastError( ERROR_SUCCESS );
5683 return ret;
5687 /***********************************************************************
5688 * LCIDToLocaleName (kernelbase.@)
5690 INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
5692 const NLS_LOCALE_DATA *locale = get_locale_by_id( &lcid, flags );
5694 if (!locale)
5696 SetLastError( ERROR_INVALID_PARAMETER );
5697 return 0;
5699 return get_locale_info( locale, lcid, LOCALE_SNAME, name, count );
5703 /***********************************************************************
5704 * LCMapStringEx (kernelbase.@)
5706 INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
5707 WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
5708 void *reserved, LPARAM handle )
5710 const struct sortguid *sortid = NULL;
5711 LPWSTR dst_ptr;
5712 INT len;
5714 if (version) FIXME( "unsupported version structure %p\n", version );
5715 if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
5716 if (handle)
5718 static int once;
5719 if (!once++) FIXME( "unsupported lparam %Ix\n", handle );
5722 if (!src || !srclen || dstlen < 0)
5724 SetLastError( ERROR_INVALID_PARAMETER );
5725 return 0;
5728 /* mutually exclusive flags */
5729 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
5730 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
5731 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
5732 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
5733 !flags)
5735 SetLastError( ERROR_INVALID_FLAGS );
5736 return 0;
5739 if (!dstlen) dst = NULL;
5741 if (flags & LCMAP_LINGUISTIC_CASING && !(sortid = get_language_sort( locale )))
5743 FIXME( "unknown locale %s\n", debugstr_w(locale) );
5744 SetLastError( ERROR_INVALID_PARAMETER );
5745 return 0;
5748 if (flags & LCMAP_SORTKEY)
5750 INT ret;
5752 if (src == dst)
5754 SetLastError( ERROR_INVALID_FLAGS );
5755 return 0;
5757 if (srclen < 0) srclen = lstrlenW(src);
5759 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5760 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5762 if ((ret = get_sortkey( flags, src, srclen, (char *)dst, dstlen ))) ret++;
5763 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
5764 return ret;
5767 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
5768 if (flags & SORT_STRINGSORT)
5770 SetLastError( ERROR_INVALID_FLAGS );
5771 return 0;
5773 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
5774 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
5775 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
5776 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
5778 SetLastError( ERROR_INVALID_FLAGS );
5779 return 0;
5782 if (srclen < 0) srclen = lstrlenW(src) + 1;
5784 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5785 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5787 if (!dst) /* return required string length */
5789 if (flags & NORM_IGNORESYMBOLS)
5791 for (len = 0; srclen; src++, srclen--)
5792 if (!(get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE))) len++;
5794 else if (flags & LCMAP_FULLWIDTH)
5796 for (len = 0; srclen; src++, srclen--, len++)
5798 if (compose_katakana( src, srclen, NULL ) == 2)
5800 src++;
5801 srclen--;
5805 else if (flags & LCMAP_HALFWIDTH)
5807 for (len = 0; srclen; src++, srclen--, len++)
5809 WCHAR wch = *src;
5810 /* map Hiragana to Katakana before decomposition if needed */
5811 if ((flags & LCMAP_KATAKANA) &&
5812 ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E))
5813 wch += 0x60;
5815 if (decompose_katakana( wch, NULL, 0 ) == 2) len++;
5818 else len = srclen;
5819 return len;
5822 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
5824 SetLastError( ERROR_INVALID_FLAGS );
5825 return 0;
5828 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
5830 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
5832 if ((flags & NORM_IGNORESYMBOLS) && (get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE)))
5833 continue;
5834 *dst_ptr++ = *src;
5835 len--;
5837 goto done;
5840 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
5842 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
5844 WCHAR wch;
5845 if (flags & LCMAP_FULLWIDTH)
5847 /* map half-width character to full-width one,
5848 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
5849 if (map_to_fullwidth( src, srclen, &wch ) == 2)
5851 src++;
5852 srclen--;
5855 else wch = *src;
5857 if (flags & LCMAP_KATAKANA)
5859 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
5860 we can't use C3_HIRAGANA as some characters can't map to katakana */
5861 if ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E) wch += 0x60;
5863 else if (flags & LCMAP_HIRAGANA)
5865 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
5866 we can't use C3_KATAKANA as some characters can't map to hiragana */
5867 if ((wch >= 0x30A1 && wch <= 0x30F6) || wch == 0x30FD || wch == 0x30FE) wch -= 0x60;
5870 if (flags & LCMAP_HALFWIDTH)
5872 /* map full-width character to half-width one,
5873 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
5874 if (map_to_halfwidth(wch, dst_ptr, len) == 2)
5876 len--;
5877 dst_ptr++;
5878 if (!len) break;
5881 else *dst_ptr = wch;
5883 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen) goto done;
5885 srclen = dst_ptr - dst;
5886 src = dst;
5889 if (flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
5891 const USHORT *table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
5892 table = table + 2 + (flags & LCMAP_LOWERCASE ? table[1] : 0);
5893 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--, len--)
5894 *dst_ptr++ = casemap( table, *src );
5896 else
5898 len = min( srclen, dstlen );
5899 memcpy( dst, src, len * sizeof(WCHAR) );
5900 dst_ptr = dst + len;
5901 srclen -= len;
5904 done:
5905 if (srclen)
5907 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5908 return 0;
5911 return dst_ptr - dst;
5915 /***********************************************************************
5916 * LCMapStringA (kernelbase.@)
5918 INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
5919 char *dst, int dstlen )
5921 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
5922 LPWSTR srcW, dstW;
5923 INT ret = 0, srclenW, dstlenW;
5924 UINT locale_cp = CP_ACP;
5926 if (!src || !srclen || dstlen < 0)
5928 SetLastError( ERROR_INVALID_PARAMETER );
5929 return 0;
5932 locale_cp = get_lcid_codepage( lcid, flags );
5934 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
5935 if (srclenW) srcW = bufW;
5936 else
5938 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
5939 srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
5940 if (!srcW)
5942 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5943 return 0;
5945 MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
5948 if (flags & LCMAP_SORTKEY)
5950 if (src == dst)
5952 SetLastError( ERROR_INVALID_FLAGS );
5953 goto done;
5955 ret = LCMapStringEx( NULL, flags, srcW, srclenW, (WCHAR *)dst, dstlen, NULL, NULL, 0 );
5956 goto done;
5959 if (flags & SORT_STRINGSORT)
5961 SetLastError( ERROR_INVALID_FLAGS );
5962 goto done;
5965 dstlenW = LCMapStringEx( NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0 );
5966 if (!dstlenW) goto done;
5968 dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
5969 if (!dstW)
5971 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5972 goto done;
5974 LCMapStringEx( NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0 );
5975 ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
5976 HeapFree( GetProcessHeap(), 0, dstW );
5978 done:
5979 if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
5980 return ret;
5984 /***********************************************************************
5985 * LCMapStringW (kernelbase.@)
5987 INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
5988 WCHAR *dst, int dstlen )
5990 return LCMapStringEx( NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
5994 /***********************************************************************
5995 * LocaleNameToLCID (kernelbase.@)
5997 LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
5999 LCID lcid;
6000 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6002 if (!locale)
6004 SetLastError( ERROR_INVALID_PARAMETER );
6005 return 0;
6007 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
6008 lcid = locale->idefaultlanguage;
6009 return lcid;
6013 /******************************************************************************
6014 * MultiByteToWideChar (kernelbase.@)
6016 INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
6017 WCHAR *dst, INT dstlen )
6019 int ret;
6021 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6023 SetLastError( ERROR_INVALID_PARAMETER );
6024 return 0;
6026 if (srclen < 0) srclen = strlen(src) + 1;
6028 switch (codepage)
6030 case CP_SYMBOL:
6031 ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
6032 break;
6033 case CP_UTF7:
6034 ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
6035 break;
6036 case CP_UTF8:
6037 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6038 break;
6039 case CP_UNIXCP:
6040 if (unix_cp == CP_UTF8)
6042 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6043 break;
6045 codepage = unix_cp;
6046 /* fall through */
6047 default:
6048 ret = mbstowcs_codepage( codepage, flags, src, srclen, dst, dstlen );
6049 break;
6051 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
6052 return ret;
6056 /******************************************************************************
6057 * NormalizeString (kernelbase.@)
6059 INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
6060 WCHAR *dst, INT dst_len)
6062 NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len );
6064 switch (status)
6066 case STATUS_OBJECT_NAME_NOT_FOUND:
6067 status = STATUS_INVALID_PARAMETER;
6068 break;
6069 case STATUS_BUFFER_TOO_SMALL:
6070 case STATUS_NO_UNICODE_TRANSLATION:
6071 dst_len = -dst_len;
6072 break;
6074 SetLastError( RtlNtStatusToDosError( status ));
6075 return dst_len;
6079 /******************************************************************************
6080 * ResolveLocaleName (kernelbase.@)
6082 INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
6084 FIXME( "stub: %s, %p, %d\n", wine_dbgstr_w(name), buffer, len );
6086 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
6087 return 0;
6091 /******************************************************************************
6092 * SetLocaleInfoW (kernelbase.@)
6094 BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
6096 WCHAR *str, tmp[80];
6098 if (!data)
6100 SetLastError( ERROR_INVALID_PARAMETER );
6101 return FALSE;
6104 switch (LOWORD(lctype))
6106 case LOCALE_ICALENDARTYPE: return set_registry_entry( &entry_icalendartype, data );
6107 case LOCALE_ICURRDIGITS: return set_registry_entry( &entry_icurrdigits, data );
6108 case LOCALE_ICURRENCY: return set_registry_entry( &entry_icurrency, data );
6109 case LOCALE_IDIGITS: return set_registry_entry( &entry_idigits, data );
6110 case LOCALE_IDIGITSUBSTITUTION: return set_registry_entry( &entry_idigitsubstitution, data );
6111 case LOCALE_IFIRSTDAYOFWEEK: return set_registry_entry( &entry_ifirstdayofweek, data );
6112 case LOCALE_IFIRSTWEEKOFYEAR: return set_registry_entry( &entry_ifirstweekofyear, data );
6113 case LOCALE_ILZERO: return set_registry_entry( &entry_ilzero, data );
6114 case LOCALE_IMEASURE: return set_registry_entry( &entry_imeasure, data );
6115 case LOCALE_INEGCURR: return set_registry_entry( &entry_inegcurr, data );
6116 case LOCALE_INEGNUMBER: return set_registry_entry( &entry_inegnumber, data );
6117 case LOCALE_IPAPERSIZE: return set_registry_entry( &entry_ipapersize, data );
6118 case LOCALE_S1159: return set_registry_entry( &entry_s1159, data );
6119 case LOCALE_S2359: return set_registry_entry( &entry_s2359, data );
6120 case LOCALE_SCURRENCY: return set_registry_entry( &entry_scurrency, data );
6121 case LOCALE_SDECIMAL: return set_registry_entry( &entry_sdecimal, data );
6122 case LOCALE_SGROUPING: return set_registry_entry( &entry_sgrouping, data );
6123 case LOCALE_SLIST: return set_registry_entry( &entry_slist, data );
6124 case LOCALE_SLONGDATE: return set_registry_entry( &entry_slongdate, data );
6125 case LOCALE_SMONDECIMALSEP: return set_registry_entry( &entry_smondecimalsep, data );
6126 case LOCALE_SMONGROUPING: return set_registry_entry( &entry_smongrouping, data );
6127 case LOCALE_SMONTHOUSANDSEP: return set_registry_entry( &entry_smonthousandsep, data );
6128 case LOCALE_SNATIVEDIGITS: return set_registry_entry( &entry_snativedigits, data );
6129 case LOCALE_SNEGATIVESIGN: return set_registry_entry( &entry_snegativesign, data );
6130 case LOCALE_SPOSITIVESIGN: return set_registry_entry( &entry_spositivesign, data );
6131 case LOCALE_SSHORTTIME: return set_registry_entry( &entry_sshorttime, data );
6132 case LOCALE_STHOUSAND: return set_registry_entry( &entry_sthousand, data );
6133 case LOCALE_SYEARMONTH: return set_registry_entry( &entry_syearmonth, data );
6135 case LOCALE_SDATE:
6136 if (!get_locale_info( user_locale, user_lcid, LOCALE_SSHORTDATE, tmp, ARRAY_SIZE(tmp) )) break;
6137 data = locale_replace_separator( tmp, data );
6138 /* fall through */
6139 case LOCALE_SSHORTDATE:
6140 if (!set_registry_entry( &entry_sshortdate, data )) return FALSE;
6141 update_registry_value( LOCALE_IDATE, L"iDate" );
6142 update_registry_value( LOCALE_SDATE, L"sDate" );
6143 return TRUE;
6145 case LOCALE_STIME:
6146 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6147 data = locale_replace_separator( tmp, data );
6148 /* fall through */
6149 case LOCALE_STIMEFORMAT:
6150 if (!set_registry_entry( &entry_stimeformat, data )) return FALSE;
6151 update_registry_value( LOCALE_ITIME, L"iTime" );
6152 update_registry_value( LOCALE_ITIMEMARKPOSN, L"iTimePrefix" );
6153 update_registry_value( LOCALE_ITLZERO, L"iTLZero" );
6154 update_registry_value( LOCALE_STIME, L"sTime" );
6155 return TRUE;
6157 case LOCALE_ITIME:
6158 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6159 if (!(str = find_format( tmp, L"Hh" ))) break;
6160 while (*str == 'h' || *str == 'H') *str++ = (*data == '0' ? 'h' : 'H');
6161 if (!set_registry_entry( &entry_stimeformat, tmp )) break;
6162 update_registry_value( LOCALE_ITIME, L"iTime" );
6163 return TRUE;
6165 case LOCALE_SINTLSYMBOL:
6166 /* FIXME: also store sintlsymbol */
6167 set_registry_entry( &entry_scurrency, data );
6168 return TRUE;
6170 SetLastError( ERROR_INVALID_FLAGS );
6171 return FALSE;
6175 /***********************************************************************
6176 * SetCalendarInfoW (kernelbase.@)
6178 INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
6180 FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
6181 return 0;
6185 /***********************************************************************
6186 * SetProcessPreferredUILanguages (kernelbase.@)
6188 BOOL WINAPI DECLSPEC_HOTPATCH SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6190 return set_ntstatus( RtlSetProcessPreferredUILanguages( flags, buffer, count ));
6194 /***********************************************************************
6195 * SetThreadPreferredUILanguages (kernelbase.@)
6197 BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6199 return set_ntstatus( RtlSetThreadPreferredUILanguages( flags, buffer, count ));
6203 /***********************************************************************
6204 * SetTimeZoneInformation (kernelbase.@)
6206 BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info )
6208 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info ));
6212 /******************************************************************************
6213 * SetUserGeoID (kernelbase.@)
6215 BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id )
6217 const struct geo_id *geo = find_geo_id_entry( id );
6218 WCHAR bufferW[10];
6219 HKEY hkey;
6221 if (!geo)
6223 SetLastError( ERROR_INVALID_PARAMETER );
6224 return FALSE;
6226 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
6228 const WCHAR *name = geo->class == GEOCLASS_NATION ? L"Nation" : L"Region";
6229 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geo->id );
6230 RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6232 if (geo->class == GEOCLASS_NATION || wcscmp( geo->iso2, L"XX" ))
6233 lstrcpyW( bufferW, geo->iso2 );
6234 else
6235 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", geo->uncode );
6236 RegSetValueExW( hkey, L"Name", 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6237 RegCloseKey( hkey );
6239 return TRUE;
6243 /***********************************************************************
6244 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
6246 BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info,
6247 const SYSTEMTIME *system,
6248 SYSTEMTIME *local )
6250 TIME_ZONE_INFORMATION tzinfo;
6251 LARGE_INTEGER ft;
6253 if (!info)
6255 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6256 info = &tzinfo;
6259 if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE;
6260 switch (get_timezone_id( info, ft, FALSE ))
6262 case TIME_ZONE_ID_UNKNOWN:
6263 ft.QuadPart -= info->Bias * (LONGLONG)600000000;
6264 break;
6265 case TIME_ZONE_ID_STANDARD:
6266 ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6267 break;
6268 case TIME_ZONE_ID_DAYLIGHT:
6269 ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6270 break;
6271 default:
6272 return FALSE;
6274 return FileTimeToSystemTime( (FILETIME *)&ft, local );
6278 /***********************************************************************
6279 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
6281 BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info,
6282 const SYSTEMTIME *local,
6283 SYSTEMTIME *system )
6285 TIME_ZONE_INFORMATION tzinfo;
6286 LARGE_INTEGER ft;
6288 if (!info)
6290 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6291 info = &tzinfo;
6294 if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE;
6295 switch (get_timezone_id( info, ft, TRUE ))
6297 case TIME_ZONE_ID_UNKNOWN:
6298 ft.QuadPart += info->Bias * (LONGLONG)600000000;
6299 break;
6300 case TIME_ZONE_ID_STANDARD:
6301 ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6302 break;
6303 case TIME_ZONE_ID_DAYLIGHT:
6304 ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6305 break;
6306 default:
6307 return FALSE;
6309 return FileTimeToSystemTime( (FILETIME *)&ft, system );
6313 /***********************************************************************
6314 * VerLanguageNameA (kernelbase.@)
6316 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
6318 return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6322 /***********************************************************************
6323 * VerLanguageNameW (kernelbase.@)
6325 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
6327 return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6331 /***********************************************************************
6332 * WideCharToMultiByte (kernelbase.@)
6334 INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
6335 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
6337 int ret;
6339 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6341 SetLastError( ERROR_INVALID_PARAMETER );
6342 return 0;
6345 if (srclen < 0) srclen = lstrlenW(src) + 1;
6347 switch (codepage)
6349 case CP_SYMBOL:
6350 ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
6351 break;
6352 case CP_UTF7:
6353 ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
6354 break;
6355 case CP_UTF8:
6356 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
6357 break;
6358 case CP_UNIXCP:
6359 if (unix_cp == CP_UTF8)
6361 if (used) *used = FALSE;
6362 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, NULL, NULL );
6363 break;
6365 codepage = unix_cp;
6366 /* fall through */
6367 default:
6368 ret = wcstombs_codepage( codepage, flags, src, srclen, dst, dstlen, defchar, used );
6369 break;
6371 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
6372 return ret;
6376 /***********************************************************************
6377 * GetUserDefaultGeoName (kernelbase.@)
6379 INT WINAPI GetUserDefaultGeoName(LPWSTR geo_name, int count)
6381 WCHAR buffer[32];
6382 LSTATUS status;
6383 DWORD size;
6384 HKEY key;
6386 TRACE( "geo_name %p, count %d.\n", geo_name, count );
6388 if (count && !geo_name)
6390 SetLastError( ERROR_INVALID_PARAMETER );
6391 return 0;
6393 if (!(status = RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &key )))
6395 size = sizeof(buffer);
6396 status = RegQueryValueExW( key, L"Name", NULL, NULL, (BYTE *)buffer, &size );
6397 RegCloseKey( key );
6399 if (status)
6401 const struct geo_id *geo = find_geo_id_entry( GetUserGeoID( GEOCLASS_NATION ));
6402 if (geo && geo->id != 39070)
6403 lstrcpyW( buffer, geo->iso2 );
6404 else
6405 lstrcpyW( buffer, L"001" );
6407 size = lstrlenW( buffer ) + 1;
6408 if (count < size)
6410 if (!count)
6411 return size;
6412 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6413 return 0;
6415 lstrcpyW( geo_name, buffer );
6416 return size;
6420 /***********************************************************************
6421 * SetUserDefaultGeoName (kernelbase.@)
6423 BOOL WINAPI SetUserGeoName(PWSTR geo_name)
6425 const struct geo_id *geo;
6427 TRACE( "geo_name %s.\n", debugstr_w( geo_name ));
6429 if (!geo_name || !(geo = find_geo_name_entry( geo_name )))
6431 SetLastError( ERROR_INVALID_PARAMETER );
6432 return FALSE;
6434 return SetUserGeoID( geo->id );