kernelbase: Support UTF-8 as default Ansi codepage in MultiByteToWideChar().
[wine.git] / dlls / kernelbase / locale.c
blobc807b1e0124479ef5270cd5a9e2a241bd16966fc
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"65000 (UTF-7)" },
168 { 65001, L"65001 (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 },
211 struct calendar
213 USHORT icalintvalue; /* 00 */
214 USHORT itwodigityearmax; /* 02 */
215 UINT sshortdate; /* 04 */
216 UINT syearmonth; /* 08 */
217 UINT slongdate; /* 0c */
218 UINT serastring; /* 10 */
219 UINT iyearoffsetrange; /* 14 */
220 UINT sdayname; /* 18 */
221 UINT sabbrevdayname; /* 1c */
222 UINT smonthname; /* 20 */
223 UINT sabbrevmonthname; /* 24 */
224 UINT scalname; /* 28 */
225 UINT smonthday; /* 2c */
226 UINT sabbreverastring; /* 30 */
227 UINT sshortestdayname; /* 34 */
228 UINT srelativelongdate; /* 38 */
229 UINT unused[3]; /* 3c */
232 static const struct geo_id
234 GEOID id;
235 WCHAR latitude[12];
236 WCHAR longitude[12];
237 GEOCLASS class;
238 GEOID parent;
239 WCHAR iso2[4];
240 WCHAR iso3[4];
241 USHORT uncode;
242 USHORT dialcode;
243 WCHAR currcode[4];
244 WCHAR currsymbol[8];
245 } *geo_ids;
247 static const struct geo_index
249 WCHAR name[4];
250 UINT idx;
251 } *geo_index;
253 static unsigned int geo_ids_count;
254 static unsigned int geo_index_count;
256 /* NLS normalization file */
257 struct norm_table
259 WCHAR name[13]; /* 00 file name */
260 USHORT checksum[3]; /* 1a checksum? */
261 USHORT version[4]; /* 20 Unicode version */
262 USHORT form; /* 28 normalization form */
263 USHORT len_factor; /* 2a factor for length estimates */
264 USHORT unknown1; /* 2c */
265 USHORT decomp_size; /* 2e decomposition hash size */
266 USHORT comp_size; /* 30 composition hash size */
267 USHORT unknown2; /* 32 */
268 USHORT classes; /* 34 combining classes table offset */
269 USHORT props_level1; /* 36 char properties table level 1 offset */
270 USHORT props_level2; /* 38 char properties table level 2 offset */
271 USHORT decomp_hash; /* 3a decomposition hash table offset */
272 USHORT decomp_map; /* 3c decomposition character map table offset */
273 USHORT decomp_seq; /* 3e decomposition character sequences offset */
274 USHORT comp_hash; /* 40 composition hash table offset */
275 USHORT comp_seq; /* 42 composition character sequences offset */
276 /* BYTE[] combining class values */
277 /* BYTE[0x2200] char properties index level 1 */
278 /* BYTE[] char properties index level 2 */
279 /* WORD[] decomposition hash table */
280 /* WORD[] decomposition character map */
281 /* WORD[] decomposition character sequences */
282 /* WORD[] composition hash table */
283 /* WORD[] composition character sequences */
286 static NLSTABLEINFO nls_info;
287 static UINT unix_cp = CP_UTF8;
288 static UINT mac_cp = 10000;
289 static LCID system_lcid;
290 static LCID user_lcid;
291 static HKEY intl_key;
292 static HKEY nls_key;
293 static HKEY tz_key;
294 static const NLS_LOCALE_LCID_INDEX *lcids_index;
295 static const NLS_LOCALE_LCNAME_INDEX *lcnames_index;
296 static const NLS_LOCALE_HEADER *locale_table;
297 static const WCHAR *locale_strings;
298 static const NLS_LOCALE_DATA *system_locale;
299 static const NLS_LOCALE_DATA *user_locale;
301 static CPTABLEINFO codepages[128];
302 static unsigned int nb_codepages;
304 static struct norm_table *norm_info;
306 struct sortguid
308 GUID id; /* sort GUID */
309 DWORD flags; /* flags */
310 DWORD compr; /* offset to compression table */
311 DWORD except; /* exception table offset in sortkey table */
312 DWORD ling_except; /* exception table offset for linguistic casing */
313 DWORD casemap; /* linguistic casemap table offset */
316 #define FLAG_HAS_3_BYTE_WEIGHTS 0x01
317 #define FLAG_REVERSEDIACRITICS 0x10
318 #define FLAG_DOUBLECOMPRESSION 0x20
319 #define FLAG_INVERSECASING 0x40
321 static const struct sortguid *current_locale_sort;
323 static const GUID default_sort_guid = { 0x00000001, 0x57ee, 0x1e5c, { 0x00, 0xb4, 0xd0, 0x00, 0x0b, 0xb1, 0xe1, 0x1e }};
325 static struct
327 DWORD *keys; /* sortkey table, indexed by char */
328 USHORT *casemap; /* casemap table, in l_intl.nls format */
329 WORD *ctypes; /* CT_CTYPE1,2,3 values */
330 BYTE *ctype_idx; /* index to map char to ctypes array entry */
331 DWORD version; /* NLS version */
332 DWORD guid_count; /* number of sort GUIDs */
333 struct sortguid *guids; /* table of sort GUIDs */
334 } sort;
336 static CRITICAL_SECTION locale_section;
337 static CRITICAL_SECTION_DEBUG critsect_debug =
339 0, 0, &locale_section,
340 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
341 0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") }
343 static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
346 static void load_locale_nls(void)
348 struct
350 UINT ctypes;
351 UINT unknown1;
352 UINT unknown2;
353 UINT unknown3;
354 UINT locales;
355 UINT charmaps;
356 UINT geoids;
357 UINT scripts;
358 } *header;
359 struct geo_header
361 WCHAR signature[4]; /* L"geo" */
362 UINT total_size;
363 UINT ids_offset;
364 UINT ids_count;
365 UINT index_offset;
366 UINT index_count;
367 } *geo_header;
369 LCID lcid;
370 LARGE_INTEGER dummy;
372 RtlGetLocaleFileMappingAddress( (void **)&header, &lcid, &dummy );
373 locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales);
374 lcids_index = (const NLS_LOCALE_LCID_INDEX *)((char *)locale_table + locale_table->lcids_offset);
375 lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset);
376 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
377 geo_header = (struct geo_header *)((char *)header + header->geoids);
378 geo_ids = (const struct geo_id *)((char *)geo_header + geo_header->ids_offset);
379 geo_index = (const struct geo_index *)((char *)geo_header + geo_header->index_offset);
380 geo_ids_count = geo_header->ids_count;
381 geo_index_count = geo_header->index_count;
385 static void init_sortkeys( DWORD *ptr )
387 WORD *ctype;
388 DWORD *table;
390 sort.keys = (DWORD *)((char *)ptr + ptr[0]);
391 sort.casemap = (USHORT *)((char *)ptr + ptr[1]);
393 ctype = (WORD *)((char *)ptr + ptr[2]);
394 sort.ctypes = ctype + 2;
395 sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2;
397 table = (DWORD *)((char *)ptr + ptr[3]);
398 sort.version = table[0];
399 sort.guid_count = table[1];
400 sort.guids = (struct sortguid *)(table + 2);
404 static const struct sortguid *find_sortguid( const GUID *guid )
406 int pos, ret, min = 0, max = sort.guid_count - 1;
408 while (min <= max)
410 pos = (min + max) / 2;
411 ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) );
412 if (!ret) return &sort.guids[pos];
413 if (ret > 0) min = pos + 1;
414 else max = pos - 1;
416 ERR( "no sort found for %s\n", debugstr_guid( guid ));
417 return NULL;
421 static const struct sortguid *get_language_sort( const WCHAR *locale )
423 WCHAR *p, *end, buffer[LOCALE_NAME_MAX_LENGTH], guidstr[39];
424 const struct sortguid *ret;
425 UNICODE_STRING str;
426 GUID guid;
427 HKEY key = 0;
428 DWORD size, type;
430 if (locale == LOCALE_NAME_USER_DEFAULT)
432 if (current_locale_sort) return current_locale_sort;
433 GetUserDefaultLocaleName( buffer, ARRAY_SIZE( buffer ));
435 else lstrcpynW( buffer, locale, LOCALE_NAME_MAX_LENGTH );
437 if (buffer[0] && !RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key ))
439 for (;;)
441 size = sizeof(guidstr);
442 if (!RegQueryValueExW( key, buffer, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ)
444 RtlInitUnicodeString( &str, guidstr );
445 if (!RtlGUIDFromString( &str, &guid ))
447 ret = find_sortguid( &guid );
448 goto done;
450 break;
452 for (p = end = buffer; *p; p++) if (*p == '-' || *p == '_') end = p;
453 if (end == buffer) break;
454 *end = 0;
457 ret = find_sortguid( &default_sort_guid );
458 done:
459 RegCloseKey( key );
460 return ret;
464 static const NLS_LOCALE_DATA *get_locale_data( UINT idx )
466 ULONG offset = locale_table->locales_offset + idx * locale_table->locale_size;
467 return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
471 static const struct calendar *get_calendar_data( const NLS_LOCALE_DATA *locale, UINT id )
473 if (id == CAL_HIJRI) id = locale->islamic_cal[0];
474 else if (id == CAL_PERSIAN) id = locale->islamic_cal[1];
476 if (!id || id > locale_table->nb_calendars) return NULL;
477 return (const struct calendar *)((const char *)locale_table + locale_table->calendars_offset +
478 (id - 1) * locale_table->calendar_size);
482 static int compare_locale_names( const WCHAR *n1, const WCHAR *n2 )
484 for (;;)
486 WCHAR ch1 = *n1++;
487 WCHAR ch2 = *n2++;
488 if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A';
489 else if (ch1 == '_') ch1 = '-';
490 if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A';
491 else if (ch2 == '_') ch2 = '-';
492 if (!ch1 || ch1 != ch2) return ch1 - ch2;
497 static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry( const WCHAR *name )
499 int min = 0, max = locale_table->nb_lcnames - 1;
501 while (min <= max)
503 int res, pos = (min + max) / 2;
504 const WCHAR *str = locale_strings + lcnames_index[pos].name;
505 res = compare_locale_names( name, str + 1 );
506 if (res < 0) max = pos - 1;
507 else if (res > 0) min = pos + 1;
508 else return &lcnames_index[pos];
510 return NULL;
514 static const NLS_LOCALE_LCID_INDEX *find_lcid_entry( LCID lcid )
516 int min = 0, max = locale_table->nb_lcids - 1;
518 while (min <= max)
520 int pos = (min + max) / 2;
521 if (lcid < lcids_index[pos].id) max = pos - 1;
522 else if (lcid > lcids_index[pos].id) min = pos + 1;
523 else return &lcids_index[pos];
525 return NULL;
529 static const struct geo_id *find_geo_id_entry( GEOID id )
531 int min = 0, max = geo_ids_count - 1;
533 while (min <= max)
535 int pos = (min + max) / 2;
536 if (id < geo_ids[pos].id) max = pos - 1;
537 else if (id > geo_ids[pos].id) min = pos + 1;
538 else return &geo_ids[pos];
540 return NULL;
544 static const struct geo_id *find_geo_name_entry( const WCHAR *name )
546 int min = 0, max = geo_index_count - 1;
548 while (min <= max)
550 int res, pos = (min + max) / 2;
551 res = wcsicmp( name, geo_index[pos].name );
552 if (res < 0) max = pos - 1;
553 else if (res > 0) min = pos + 1;
554 else return &geo_ids[geo_index[pos].idx];
556 return NULL;
560 static const NLS_LOCALE_DATA *get_locale_by_name( const WCHAR *name, LCID *lcid )
562 const NLS_LOCALE_LCNAME_INDEX *entry;
564 if (name == LOCALE_NAME_USER_DEFAULT)
566 *lcid = user_lcid;
567 return user_locale;
569 if (!(entry = find_lcname_entry( name ))) return NULL;
570 *lcid = entry->id;
571 return get_locale_data( entry->idx );
575 /******************************************************************************
576 * NlsValidateLocale (kernelbase.@)
578 * Note: it seems to return some internal data on Windows, we simply return the locale.nls data pointer.
580 const NLS_LOCALE_DATA * WINAPI NlsValidateLocale( LCID *lcid, ULONG flags )
582 const NLS_LOCALE_LCNAME_INDEX *name_entry;
583 const NLS_LOCALE_LCID_INDEX *entry;
584 const NLS_LOCALE_DATA *locale;
586 switch (*lcid)
588 case LOCALE_SYSTEM_DEFAULT:
589 *lcid = system_lcid;
590 return system_locale;
591 case LOCALE_NEUTRAL:
592 case LOCALE_USER_DEFAULT:
593 case LOCALE_CUSTOM_DEFAULT:
594 case LOCALE_CUSTOM_UNSPECIFIED:
595 case LOCALE_CUSTOM_UI_DEFAULT:
596 *lcid = user_lcid;
597 return user_locale;
598 default:
599 if (!(entry = find_lcid_entry( *lcid ))) return NULL;
600 locale = get_locale_data( entry->idx );
601 if ((flags & LOCALE_ALLOW_NEUTRAL_NAMES) || locale->inotneutral) return locale;
602 if ((name_entry = find_lcname_entry( locale_strings + locale->ssortlocale + 1 )))
603 locale = get_locale_data( name_entry->idx );
604 return locale;
609 static int locale_return_data( const WCHAR *data, int datalen, LCTYPE type, WCHAR *buffer, int len )
611 if (type & LOCALE_RETURN_NUMBER)
613 SetLastError( ERROR_INVALID_FLAGS );
614 return 0;
617 if (!len) return datalen;
618 if (datalen > len)
620 SetLastError( ERROR_INSUFFICIENT_BUFFER );
621 return 0;
623 memcpy( buffer, data, datalen * sizeof(WCHAR) );
624 return datalen;
628 static BOOL set_registry_entry( struct registry_entry *entry, const WCHAR *data )
630 DWORD size = (wcslen(data) + 1) * sizeof(WCHAR);
631 LSTATUS ret;
633 if (size > sizeof(entry->data))
635 SetLastError( ERROR_INVALID_FLAGS );
636 return FALSE;
638 TRACE( "setting %s to %s\n", debugstr_w(entry->value), debugstr_w(data) );
640 RtlEnterCriticalSection( &locale_section );
641 if (!(ret = RegSetValueExW( intl_key, entry->value, 0, REG_SZ, (BYTE *)data, size )))
643 wcscpy( entry->data, data );
644 entry->status = CACHED;
646 RtlLeaveCriticalSection( &locale_section );
647 if (ret) SetLastError( ret );
648 return !ret;
652 static int locale_return_reg_string( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
654 DWORD size;
655 LRESULT res;
656 int ret = -1;
658 if (type & LOCALE_NOUSEROVERRIDE) return -1;
660 RtlEnterCriticalSection( &locale_section );
661 switch (entry->status)
663 case NOT_CACHED:
664 size = sizeof(entry->data);
665 res = RegQueryValueExW( intl_key, entry->value, NULL, NULL, (BYTE *)entry->data, &size );
666 if (res)
668 entry->status = MISSING;
669 break;
671 entry->status = CACHED;
672 /* fall through */
673 case CACHED:
674 ret = locale_return_data( entry->data, wcslen(entry->data) + 1, type, buffer, len );
675 break;
676 case MISSING:
677 break;
679 RtlLeaveCriticalSection( &locale_section );
680 return ret;
684 static int locale_return_string( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
686 return locale_return_data( locale_strings + pos + 1, locale_strings[pos] + 1, type, buffer, len );
690 static int locale_return_number( UINT val, LCTYPE type, WCHAR *buffer, int len )
692 int ret;
693 WCHAR tmp[80];
695 if (!(type & LOCALE_RETURN_NUMBER))
697 switch (LOWORD(type))
699 case LOCALE_ILANGUAGE:
700 case LOCALE_IDEFAULTLANGUAGE:
701 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%04x", val ) + 1;
702 break;
703 case LOCALE_IDEFAULTEBCDICCODEPAGE:
704 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", val ) + 1;
705 break;
706 default:
707 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val ) + 1;
708 break;
711 else ret = sizeof(UINT) / sizeof(WCHAR);
713 if (!len) return ret;
714 if (ret > len)
716 SetLastError( ERROR_INSUFFICIENT_BUFFER );
717 return 0;
720 if (type & LOCALE_RETURN_NUMBER) memcpy( buffer, &val, sizeof(val) );
721 else wcscpy( buffer, tmp );
723 return ret;
727 static int locale_return_reg_number( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
729 int ret, val;
730 WCHAR *end, tmp[80];
732 if (type & LOCALE_RETURN_NUMBER)
734 ret = locale_return_reg_string( entry, type & ~LOCALE_RETURN_NUMBER, tmp, ARRAY_SIZE( tmp ));
735 if (ret == -1) return ret;
736 val = wcstol( tmp, &end, 10 );
737 if (*end) /* invalid number */
739 SetLastError( ERROR_INVALID_FLAGS );
740 return 0;
742 return locale_return_number( val, type, buffer, len );
744 return locale_return_reg_string( entry, type, buffer, len );
748 static int locale_return_grouping( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
750 WORD i, count = locale_strings[pos];
751 const WCHAR *str = locale_strings + pos + 1;
752 int ret;
754 if (type & LOCALE_RETURN_NUMBER)
756 SetLastError( ERROR_INVALID_FLAGS );
757 return 0;
759 ret = 2 * count;
760 if (str[count - 1]) ret += 2; /* for final zero */
762 if (!len) return ret;
763 if (ret > len)
765 SetLastError( ERROR_INSUFFICIENT_BUFFER );
766 return 0;
768 for (i = 0; i < count; i++)
770 if (!str[i]) /* explicit null termination */
772 buffer[-1] = 0;
773 return ret;
775 *buffer++ = '0' + str[i];
776 *buffer++ = ';';
778 *buffer++ = '0';
779 *buffer = 0;
780 return ret;
784 static int locale_return_strarray( DWORD pos, WORD idx, LCTYPE type, WCHAR *buffer, int len )
786 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
787 WORD count = locale_strings[pos];
789 return locale_return_string( idx < count ? array[idx] : 0, type, buffer, len );
793 static int locale_return_strarray_concat( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
795 WORD i, count = locale_strings[pos];
796 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
797 int ret;
799 if (type & LOCALE_RETURN_NUMBER)
801 SetLastError( ERROR_INVALID_FLAGS );
802 return 0;
804 for (i = 0, ret = 1; i < count; i++) ret += locale_strings[array[i]];
806 if (!len) return ret;
807 if (ret > len)
809 SetLastError( ERROR_INSUFFICIENT_BUFFER );
810 return 0;
812 for (i = 0; i < count; i++)
814 memcpy( buffer, locale_strings + array[i] + 1, locale_strings[array[i]] * sizeof(WCHAR) );
815 buffer += locale_strings[array[i]];
817 *buffer = 0;
818 return ret;
822 static int cal_return_number( UINT val, CALTYPE type, WCHAR *buffer, int len, DWORD *value )
824 WCHAR tmp[12];
825 int ret;
827 if (type & CAL_RETURN_NUMBER)
829 *value = val;
830 return sizeof(UINT) / sizeof(WCHAR);
832 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val );
833 return locale_return_data( tmp, ret + 1, 0, buffer, len );
837 /* find the first format char in a format string */
838 static WCHAR *find_format( WCHAR *str, const WCHAR *accept )
840 for ( ; *str; str++)
842 if (*str == '\'')
844 if (!(str = wcschr( str + 1, '\'' ))) return NULL;
846 else if (wcschr( accept, *str ))
848 /* ignore "ddd" and "dddd" */
849 if (str[0] != 'd' || str[1] != 'd' || str[2] != 'd') return str;
850 str += 2;
851 while (str[1] == 'd') str++;
854 return NULL;
858 /* replace the separator in a date/time format string */
859 static WCHAR *locale_replace_separator( WCHAR *buffer, const WCHAR *sep )
861 UINT pos = 0;
862 WCHAR res[80];
863 WCHAR *next, *str = find_format( buffer, L"dMyHhms" );
865 if (!str) return buffer;
866 pos = str - buffer;
867 memcpy( res, buffer, pos * sizeof(WCHAR) );
868 for (;;)
870 res[pos++] = *str++;
871 while (str[0] == str[-1]) res[pos++] = *str++; /* copy repeated chars */
872 if (!(next = find_format( str, L"dMyHhms" ))) break;
873 wcscpy( res + pos, sep );
874 pos += wcslen(sep);
875 str = next;
877 wcscpy( res + pos, str );
878 return wcscpy( buffer, res );
882 /* FIXME: hardcoded, sortname is apparently not available in locale.nls */
883 static const WCHAR *get_locale_sortname( LCID lcid )
885 switch (PRIMARYLANGID( lcid ))
887 case LANG_CHINESE:
888 switch (SORTIDFROMLCID( lcid ))
890 case SORT_CHINESE_PRCP:
891 switch (SUBLANGID( lcid ))
893 case SUBLANG_CHINESE_TRADITIONAL:
894 case SUBLANG_CHINESE_HONGKONG:
895 case 0x1f:
896 return L"Stroke Count";
897 default:
898 return L"Pronunciation";
900 case SORT_CHINESE_UNICODE: return L"Unicode";
901 case SORT_CHINESE_PRC: return L"Stroke Count";
902 case SORT_CHINESE_BOPOMOFO: return L"Bopomofo";
903 case SORT_CHINESE_RADICALSTROKE: return L"Radical/Stroke";
904 case 5: return L"Surname";
906 break;
908 case LANG_GEORGIAN:
909 if (SORTIDFROMLCID( lcid ) == SORT_GEORGIAN_MODERN) return L"Modern";
910 return L"Traditional";
912 case LANG_GERMAN:
913 switch (SUBLANGID( lcid ))
915 case SUBLANG_NEUTRAL:
916 case SUBLANG_DEFAULT:
917 if (SORTIDFROMLCID( lcid ) == SORT_GERMAN_PHONE_BOOK) return L"Phone Book (DIN)";
918 return L"Dictionary";
920 break;
922 case LANG_HUNGARIAN:
923 if (SORTIDFROMLCID( lcid ) == SORT_HUNGARIAN_TECHNICAL) return L"Technical";
924 break;
926 case LANG_INVARIANT:
927 if (SORTIDFROMLCID( lcid ) == SORT_INVARIANT_MATH) return L"Default";
928 return L"Maths Alphanumerics";
930 case LANG_JAPANESE:
931 switch (SORTIDFROMLCID( lcid ))
933 case SORT_JAPANESE_XJIS: return L"XJIS";
934 case SORT_JAPANESE_UNICODE: return L"Unicode";
935 case SORT_JAPANESE_RADICALSTROKE: return L"Radical/Stroke";
937 break;
939 case LANG_KOREAN:
940 if (SORTIDFROMLCID( lcid ) == SORT_KOREAN_UNICODE) return L"Unicode";
941 return L"Dictionary";
943 case LANG_SPANISH:
944 switch (SUBLANGID( lcid ))
946 case SUBLANG_NEUTRAL:
947 case SUBLANG_SPANISH_MODERN:
948 return L"International";
949 case SUBLANG_DEFAULT:
950 return L"Traditional";
952 break;
954 return L"Default";
958 /* get locale information from the locale.nls file */
959 static int get_locale_info( const NLS_LOCALE_DATA *locale, LCID lcid, LCTYPE type,
960 WCHAR *buffer, int len )
962 static const WCHAR spermille[] = { 0x2030, 0 }; /* this one seems hardcoded */
963 static const BYTE ipossignposn[] = { 3, 3, 4, 2, 1, 1, 3, 4, 1, 3, 4, 2, 4, 3, 3, 1 };
964 static const BYTE inegsignposn[] = { 0, 3, 4, 2, 0, 1, 3, 4, 1, 3, 4, 2, 4, 3, 0, 0 };
965 static const BYTE inegsymprecedes[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, };
966 const WCHAR *sort;
967 WCHAR *str, *end, tmp[80];
968 UINT val;
969 int ret;
971 if (locale != user_locale) type |= LOCALE_NOUSEROVERRIDE;
973 switch (LOWORD(type))
975 case LOCALE_ILANGUAGE:
976 /* return default language for neutral locales */
977 val = locale->inotneutral ? locale->ilanguage : locale->idefaultlanguage;
978 return locale_return_number( val, type, buffer, len );
980 case LOCALE_SLOCALIZEDDISPLAYNAME:
981 /* FIXME: localization */
982 return locale_return_string( locale->sengdisplayname, type, buffer, len );
984 case LOCALE_SABBREVLANGNAME:
985 return locale_return_string( locale->sabbrevlangname, type, buffer, len );
987 case LOCALE_SNATIVELANGNAME:
988 return locale_return_string( locale->snativelangname, type, buffer, len );
990 case LOCALE_ICOUNTRY:
991 if ((ret = locale_return_reg_number( &entry_icountry, type, buffer, len )) != -1) return ret;
992 return locale_return_number( locale->icountry, type, buffer, len );
994 case LOCALE_SLOCALIZEDCOUNTRYNAME:
995 /* FIXME: localization */
996 return locale_return_string( locale->sengcountry, type, buffer, len );
998 case LOCALE_SABBREVCTRYNAME:
999 return locale_return_string( locale->sabbrevctryname, type, buffer, len );
1001 case LOCALE_SNATIVECTRYNAME:
1002 return locale_return_string( locale->snativectryname, type, buffer, len );
1004 case LOCALE_IDEFAULTLANGUAGE:
1005 return locale_return_number( locale->idefaultlanguage, type, buffer, len );
1007 case LOCALE_IDEFAULTCOUNTRY:
1008 return locale_return_number( locale->icountry, type, buffer, len );
1010 case LOCALE_IDEFAULTCODEPAGE:
1011 val = locale->idefaultcodepage == CP_UTF8 ? CP_OEMCP : locale->idefaultcodepage;
1012 return locale_return_number( val, type, buffer, len );
1014 case LOCALE_SLIST:
1015 if ((ret = locale_return_reg_string( &entry_slist, type, buffer, len )) != -1) return ret;
1016 return locale_return_string( locale->slist, type, buffer, len );
1018 case LOCALE_IMEASURE:
1019 if ((ret = locale_return_reg_number( &entry_imeasure, type, buffer, len )) != -1) return ret;
1020 return locale_return_number( locale->imeasure, type, buffer, len );
1022 case LOCALE_SDECIMAL:
1023 if ((ret = locale_return_reg_string( &entry_sdecimal, type, buffer, len )) != -1) return ret;
1024 return locale_return_string( locale->sdecimal, type, buffer, len );
1026 case LOCALE_STHOUSAND:
1027 if ((ret = locale_return_reg_string( &entry_sthousand, type, buffer, len )) != -1) return ret;
1028 return locale_return_string( locale->sthousand, type, buffer, len );
1030 case LOCALE_SGROUPING:
1031 if ((ret = locale_return_reg_string( &entry_sgrouping, type, buffer, len )) != -1) return ret;
1032 return locale_return_grouping( locale->sgrouping, type, buffer, len );
1034 case LOCALE_IDIGITS:
1035 if ((ret = locale_return_reg_number( &entry_idigits, type, buffer, len )) != -1) return ret;
1036 return locale_return_number( locale->idigits, type, buffer, len );
1038 case LOCALE_ILZERO:
1039 if ((ret = locale_return_reg_number( &entry_ilzero, type, buffer, len )) != -1) return ret;
1040 return locale_return_number( locale->ilzero, type, buffer, len );
1042 case LOCALE_SNATIVEDIGITS:
1043 if ((ret = locale_return_reg_string( &entry_snativedigits, type, buffer, len )) != -1) return ret;
1044 return locale_return_strarray_concat( locale->snativedigits, type, buffer, len );
1046 case LOCALE_SCURRENCY:
1047 if ((ret = locale_return_reg_string( &entry_scurrency, type, buffer, len )) != -1) return ret;
1048 return locale_return_string( locale->scurrency, type, buffer, len );
1050 case LOCALE_SINTLSYMBOL:
1051 return locale_return_string( locale->sintlsymbol, type, buffer, len );
1053 case LOCALE_SMONDECIMALSEP:
1054 if ((ret = locale_return_reg_string( &entry_smondecimalsep, type, buffer, len )) != -1) return ret;
1055 return locale_return_string( locale->smondecimalsep, type, buffer, len );
1057 case LOCALE_SMONTHOUSANDSEP:
1058 if ((ret = locale_return_reg_string( &entry_smonthousandsep, type, buffer, len )) != -1) return ret;
1059 return locale_return_string( locale->smonthousandsep, type, buffer, len );
1061 case LOCALE_SMONGROUPING:
1062 if ((ret = locale_return_reg_string( &entry_smongrouping, type, buffer, len )) != -1) return ret;
1063 return locale_return_grouping( locale->smongrouping, type, buffer, len );
1065 case LOCALE_ICURRDIGITS:
1066 case LOCALE_IINTLCURRDIGITS:
1067 if ((ret = locale_return_reg_number( &entry_icurrdigits, type, buffer, len )) != -1) return ret;
1068 return locale_return_number( locale->icurrdigits, type, buffer, len );
1070 case LOCALE_ICURRENCY:
1071 if ((ret = locale_return_reg_number( &entry_icurrency, type, buffer, len )) != -1) return ret;
1072 return locale_return_number( locale->icurrency, type, buffer, len );
1074 case LOCALE_INEGCURR:
1075 if ((ret = locale_return_reg_number( &entry_inegcurr, type, buffer, len )) != -1) return ret;
1076 return locale_return_number( locale->inegcurr, type, buffer, len );
1078 case LOCALE_SDATE:
1079 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1080 tmp, ARRAY_SIZE( tmp ))) break;
1081 if (!(str = find_format( tmp, L"dMy" ))) break;
1082 while (str[1] == str[0]) str++; /* skip repeated chars */
1083 if (!(end = find_format( ++str, L"dMy" ))) break;
1084 *end++ = 0;
1085 return locale_return_data( str, end - str, type, buffer, len );
1087 case LOCALE_STIME:
1088 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1089 tmp, ARRAY_SIZE( tmp ))) break;
1090 if (!(str = find_format( tmp, L"Hhms" ))) break;
1091 while (str[1] == str[0]) str++; /* skip repeated chars */
1092 if (!(end = find_format( ++str, L"Hhms" ))) break;
1093 *end++ = 0;
1094 return locale_return_data( str, end - str, type, buffer, len );
1096 case LOCALE_SSHORTDATE:
1097 if ((ret = locale_return_reg_string( &entry_sshortdate, type, buffer, len )) != -1) return ret;
1098 return locale_return_strarray( locale->sshortdate, 0, type, buffer, len );
1100 case LOCALE_SLONGDATE:
1101 if ((ret = locale_return_reg_string( &entry_slongdate, type, buffer, len )) != -1) return ret;
1102 return locale_return_strarray( locale->slongdate, 0, type, buffer, len );
1104 case LOCALE_IDATE:
1105 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1106 tmp, ARRAY_SIZE( tmp ))) break;
1107 /* if both year and day are found before month, the last one takes precedence */
1108 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1110 if (*str == 'M') break;
1111 val = (*str == 'y' ? 2 : 1);
1113 return locale_return_number( val, type, buffer, len );
1115 case LOCALE_ILDATE:
1116 if (!get_locale_info( locale, lcid, LOCALE_SLONGDATE | (type & LOCALE_NOUSEROVERRIDE),
1117 tmp, ARRAY_SIZE( tmp ))) break;
1118 /* if both year and day are found before month, the last one takes precedence */
1119 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1121 if (*str == 'M') break;
1122 val = (*str == 'y' ? 2 : 1);
1124 return locale_return_number( val, type, buffer, len );
1126 case LOCALE_ITIME:
1127 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1128 tmp, ARRAY_SIZE( tmp ))) break;
1129 if (!(str = find_format( tmp, L"Hh" ))) break;
1130 return locale_return_number( *str == 'H', type, buffer, len );
1132 case LOCALE_ICENTURY:
1133 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1134 tmp, ARRAY_SIZE( tmp ))) break;
1135 if (!(str = find_format( tmp, L"y" ))) break;
1136 return locale_return_number( !wcsncmp( str, L"yyyy", 4 ), type, buffer, len );
1138 case LOCALE_ITLZERO:
1139 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1140 tmp, ARRAY_SIZE( tmp ))) break;
1141 if (!(str = find_format( tmp, L"Hh" ))) break;
1142 return locale_return_number( str[1] == str[0], type, buffer, len );
1144 case LOCALE_IDAYLZERO:
1145 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1146 tmp, ARRAY_SIZE( tmp ))) break;
1147 if (!(str = find_format( tmp, L"d" ))) break;
1148 return locale_return_number( str[1] == 'd', type, buffer, len );
1150 case LOCALE_IMONLZERO:
1151 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1152 tmp, ARRAY_SIZE( tmp ))) break;
1153 if (!(str = find_format( tmp, L"M" ))) break;
1154 return locale_return_number( str[1] == 'M', type, buffer, len );
1156 case LOCALE_S1159:
1157 if ((ret = locale_return_reg_string( &entry_s1159, type, buffer, len )) != -1) return ret;
1158 return locale_return_string( locale->s1159, type, buffer, len );
1160 case LOCALE_S2359:
1161 if ((ret = locale_return_reg_string( &entry_s2359, type, buffer, len )) != -1) return ret;
1162 return locale_return_string( locale->s2359, type, buffer, len );
1164 case LOCALE_SDAYNAME1:
1165 case LOCALE_SDAYNAME2:
1166 case LOCALE_SDAYNAME3:
1167 case LOCALE_SDAYNAME4:
1168 case LOCALE_SDAYNAME5:
1169 case LOCALE_SDAYNAME6:
1170 case LOCALE_SDAYNAME7:
1171 return locale_return_strarray( locale->sdayname,
1172 LOWORD(type - LOCALE_SDAYNAME1 + 1) % 7, type, buffer, len );
1174 case LOCALE_SABBREVDAYNAME1:
1175 case LOCALE_SABBREVDAYNAME2:
1176 case LOCALE_SABBREVDAYNAME3:
1177 case LOCALE_SABBREVDAYNAME4:
1178 case LOCALE_SABBREVDAYNAME5:
1179 case LOCALE_SABBREVDAYNAME6:
1180 case LOCALE_SABBREVDAYNAME7:
1181 return locale_return_strarray( locale->sabbrevdayname,
1182 LOWORD(type - LOCALE_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1184 case LOCALE_SMONTHNAME1:
1185 case LOCALE_SMONTHNAME2:
1186 case LOCALE_SMONTHNAME3:
1187 case LOCALE_SMONTHNAME4:
1188 case LOCALE_SMONTHNAME5:
1189 case LOCALE_SMONTHNAME6:
1190 case LOCALE_SMONTHNAME7:
1191 case LOCALE_SMONTHNAME8:
1192 case LOCALE_SMONTHNAME9:
1193 case LOCALE_SMONTHNAME10:
1194 case LOCALE_SMONTHNAME11:
1195 case LOCALE_SMONTHNAME12:
1196 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1197 locale->sgenitivemonth : locale->smonthname,
1198 type - LOCALE_SMONTHNAME1, type, buffer, len );
1200 case LOCALE_SABBREVMONTHNAME1:
1201 case LOCALE_SABBREVMONTHNAME2:
1202 case LOCALE_SABBREVMONTHNAME3:
1203 case LOCALE_SABBREVMONTHNAME4:
1204 case LOCALE_SABBREVMONTHNAME5:
1205 case LOCALE_SABBREVMONTHNAME6:
1206 case LOCALE_SABBREVMONTHNAME7:
1207 case LOCALE_SABBREVMONTHNAME8:
1208 case LOCALE_SABBREVMONTHNAME9:
1209 case LOCALE_SABBREVMONTHNAME10:
1210 case LOCALE_SABBREVMONTHNAME11:
1211 case LOCALE_SABBREVMONTHNAME12:
1212 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1213 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1214 type - LOCALE_SABBREVMONTHNAME1, type, buffer, len );
1216 case LOCALE_SPOSITIVESIGN:
1217 if ((ret = locale_return_reg_string( &entry_spositivesign, type, buffer, len )) != -1) return ret;
1218 return locale_return_string( locale->spositivesign, type, buffer, len );
1220 case LOCALE_SNEGATIVESIGN:
1221 if ((ret = locale_return_reg_string( &entry_snegativesign, type, buffer, len )) != -1) return ret;
1222 return locale_return_string( locale->snegativesign, type, buffer, len );
1224 case LOCALE_IPOSSIGNPOSN:
1225 if (!get_locale_info( locale, lcid,
1226 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1227 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1228 return locale_return_number( ipossignposn[val], type, buffer, len );
1230 case LOCALE_INEGSIGNPOSN:
1231 if (!get_locale_info( locale, lcid,
1232 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1233 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1234 return locale_return_number( inegsignposn[val], type, buffer, len );
1236 case LOCALE_IPOSSYMPRECEDES:
1237 if (!get_locale_info( locale, lcid,
1238 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1239 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1240 return locale_return_number( !(val & 1), type, buffer, len );
1242 case LOCALE_IPOSSEPBYSPACE:
1243 if (!get_locale_info( locale, lcid,
1244 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1245 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1246 return locale_return_number( !!(val & 2), type, buffer, len );
1248 case LOCALE_INEGSYMPRECEDES:
1249 if (!get_locale_info( locale, lcid,
1250 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1251 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1252 return locale_return_number( inegsymprecedes[val], type, buffer, len );
1254 case LOCALE_INEGSEPBYSPACE:
1255 if (!get_locale_info( locale, lcid,
1256 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1257 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1258 return locale_return_number( (val >= 8), type, buffer, len );
1260 case LOCALE_FONTSIGNATURE:
1261 return locale_return_data( locale_strings + locale->fontsignature + 1,
1262 locale_strings[locale->fontsignature], type, buffer, len );
1264 case LOCALE_SISO639LANGNAME:
1265 return locale_return_string( locale->siso639langname, type, buffer, len );
1267 case LOCALE_SISO3166CTRYNAME:
1268 return locale_return_string( locale->siso3166ctryname, type, buffer, len );
1270 case LOCALE_IGEOID:
1271 return locale_return_number( locale->igeoid, type, buffer, len );
1273 case LOCALE_SNAME:
1274 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1276 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1277 if (entry) return locale_return_string( entry->name, type, buffer, len );
1279 return locale_return_string( locale->sname, type, buffer, len );
1281 case LOCALE_SDURATION:
1282 return locale_return_strarray( locale->sduration, 0, type, buffer, len );
1284 case LOCALE_SKEYBOARDSTOINSTALL:
1285 return locale_return_string( locale->skeyboardstoinstall, type, buffer, len );
1287 case LOCALE_SSHORTESTDAYNAME1:
1288 case LOCALE_SSHORTESTDAYNAME2:
1289 case LOCALE_SSHORTESTDAYNAME3:
1290 case LOCALE_SSHORTESTDAYNAME4:
1291 case LOCALE_SSHORTESTDAYNAME5:
1292 case LOCALE_SSHORTESTDAYNAME6:
1293 case LOCALE_SSHORTESTDAYNAME7:
1294 return locale_return_strarray( locale->sshortestdayname,
1295 LOWORD(type - LOCALE_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1297 case LOCALE_SISO639LANGNAME2:
1298 return locale_return_string( locale->siso639langname2, type, buffer, len );
1300 case LOCALE_SISO3166CTRYNAME2:
1301 return locale_return_string( locale->siso3166ctryname2, type, buffer, len );
1303 case LOCALE_SNAN:
1304 return locale_return_string( locale->snan, type, buffer, len );
1306 case LOCALE_SPOSINFINITY:
1307 return locale_return_string( locale->sposinfinity, type, buffer, len );
1309 case LOCALE_SNEGINFINITY:
1310 return locale_return_string( locale->sneginfinity, type, buffer, len );
1312 case LOCALE_SSCRIPTS:
1313 return locale_return_string( locale->sscripts, type, buffer, len );
1315 case LOCALE_SPARENT:
1316 return locale_return_string( locale->sparent, type, buffer, len );
1318 case LOCALE_SCONSOLEFALLBACKNAME:
1319 return locale_return_string( locale->sconsolefallbackname, type, buffer, len );
1321 case LOCALE_SLOCALIZEDLANGUAGENAME:
1322 /* FIXME: localization */
1323 return locale_return_string( locale->senglanguage, type, buffer, len );
1325 case LOCALE_IREADINGLAYOUT:
1326 return locale_return_number( locale->ireadinglayout, type, buffer, len );
1328 case LOCALE_INEUTRAL:
1329 return locale_return_number( !locale->inotneutral, type, buffer, len );
1331 case LOCALE_SENGLISHDISPLAYNAME:
1332 return locale_return_string( locale->sengdisplayname, type, buffer, len );
1334 case LOCALE_SNATIVEDISPLAYNAME:
1335 return locale_return_string( locale->snativedisplayname, type, buffer, len );
1337 case LOCALE_INEGATIVEPERCENT:
1338 return locale_return_number( locale->inegativepercent, type, buffer, len );
1340 case LOCALE_IPOSITIVEPERCENT:
1341 return locale_return_number( locale->ipositivepercent, type, buffer, len );
1343 case LOCALE_SPERCENT:
1344 return locale_return_string( locale->spercent, type, buffer, len );
1346 case LOCALE_SPERMILLE:
1347 return locale_return_data( spermille, ARRAY_SIZE(spermille), type, buffer, len );
1349 case LOCALE_SMONTHDAY:
1350 return locale_return_strarray( locale->smonthday, 0, type, buffer, len );
1352 case LOCALE_SSHORTTIME:
1353 if ((ret = locale_return_reg_string( &entry_sshorttime, type, buffer, len )) != -1) return ret;
1354 return locale_return_strarray( locale->sshorttime, 0, type, buffer, len );
1356 case LOCALE_SOPENTYPELANGUAGETAG:
1357 return locale_return_string( locale->sopentypelanguagetag, type, buffer, len );
1359 case LOCALE_SSORTLOCALE:
1360 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1362 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1363 if (entry) return locale_return_string( entry->name, type, buffer, len );
1365 return locale_return_string( locale->ssortlocale, type, buffer, len );
1367 case LOCALE_SRELATIVELONGDATE:
1368 return locale_return_string( locale->srelativelongdate, type, buffer, len );
1370 case 0x007d: /* undocumented */
1371 return locale_return_number( 0, type, buffer, len );
1373 case LOCALE_SSHORTESTAM:
1374 return locale_return_string( locale->sshortestam, type, buffer, len );
1376 case LOCALE_SSHORTESTPM:
1377 return locale_return_string( locale->sshortestpm, type, buffer, len );
1379 case LOCALE_SENGLANGUAGE:
1380 return locale_return_string( locale->senglanguage, type, buffer, len );
1382 case LOCALE_SENGCOUNTRY:
1383 return locale_return_string( locale->sengcountry, type, buffer, len );
1385 case LOCALE_STIMEFORMAT:
1386 if ((ret = locale_return_reg_string( &entry_stimeformat, type, buffer, len )) != -1) return ret;
1387 return locale_return_strarray( locale->stimeformat, 0, type, buffer, len );
1389 case LOCALE_IDEFAULTANSICODEPAGE:
1390 val = locale->idefaultansicodepage == CP_UTF8 ? CP_ACP : locale->idefaultansicodepage;
1391 return locale_return_number( val, type, buffer, len );
1393 case LOCALE_ITIMEMARKPOSN:
1394 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1395 tmp, ARRAY_SIZE( tmp ))) break;
1396 if (!(str = find_format( tmp, L"Hhmst" ))) break;
1397 return locale_return_number( *str == 't', type, buffer, len );
1399 case LOCALE_SYEARMONTH:
1400 if ((ret = locale_return_reg_string( &entry_syearmonth, type, buffer, len )) != -1) return ret;
1401 return locale_return_strarray( locale->syearmonth, 0, type, buffer, len );
1403 case LOCALE_SENGCURRNAME:
1404 return locale_return_string( locale->sengcurrname, type, buffer, len );
1406 case LOCALE_SNATIVECURRNAME:
1407 return locale_return_string( locale->snativecurrname, type, buffer, len );
1409 case LOCALE_ICALENDARTYPE:
1410 if ((ret = locale_return_reg_number( &entry_icalendartype, type, buffer, len )) != -1) return ret;
1411 return locale_return_number( locale_strings[locale->scalendartype + 1], type, buffer, len );
1413 case LOCALE_IPAPERSIZE:
1414 if ((ret = locale_return_reg_number( &entry_ipapersize, type, buffer, len )) != -1) return ret;
1415 return locale_return_number( locale->ipapersize, type, buffer, len );
1417 case LOCALE_IOPTIONALCALENDAR:
1418 return locale_return_number( locale_strings[locale->scalendartype + 2], type, buffer, len );
1420 case LOCALE_IFIRSTDAYOFWEEK:
1421 if ((ret = locale_return_reg_number( &entry_ifirstdayofweek, type, buffer, len )) != -1) return ret;
1422 return locale_return_number( (locale->ifirstdayofweek + 6) % 7, type, buffer, len );
1424 case LOCALE_IFIRSTWEEKOFYEAR:
1425 if ((ret = locale_return_reg_number( &entry_ifirstweekofyear, type, buffer, len )) != -1) return ret;
1426 return locale_return_number( locale->ifirstweekofyear, type, buffer, len );
1428 case LOCALE_SMONTHNAME13:
1429 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1430 locale->sgenitivemonth : locale->smonthname,
1431 12, type, buffer, len );
1433 case LOCALE_SABBREVMONTHNAME13:
1434 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1435 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1436 12, type, buffer, len );
1438 case LOCALE_INEGNUMBER:
1439 if ((ret = locale_return_reg_number( &entry_inegnumber, type, buffer, len )) != -1) return ret;
1440 return locale_return_number( locale->inegnumber, type, buffer, len );
1442 case LOCALE_IDEFAULTMACCODEPAGE:
1443 val = locale->idefaultmaccodepage == CP_UTF8 ? CP_MACCP : locale->idefaultmaccodepage;
1444 return locale_return_number( val, type, buffer, len );
1446 case LOCALE_IDEFAULTEBCDICCODEPAGE:
1447 return locale_return_number( locale->idefaultebcdiccodepage, type, buffer, len );
1449 case LOCALE_SSORTNAME:
1450 sort = get_locale_sortname( lcid );
1451 return locale_return_data( sort, wcslen(sort) + 1, type, buffer, len );
1453 case LOCALE_IDIGITSUBSTITUTION:
1454 if ((ret = locale_return_reg_number( &entry_idigitsubstitution, type, buffer, len )) != -1) return ret;
1455 return locale_return_number( locale->idigitsubstitution, type, buffer, len );
1457 SetLastError( ERROR_INVALID_FLAGS );
1458 return 0;
1462 /* get calendar information from the locale.nls file */
1463 static int get_calendar_info( const NLS_LOCALE_DATA *locale, CALID id, CALTYPE type,
1464 WCHAR *buffer, int len, DWORD *value )
1466 unsigned int i, val = 0;
1467 const struct calendar *cal;
1469 if (type & CAL_RETURN_NUMBER)
1471 if (buffer || len || !value) goto invalid;
1473 else if (len < 0 || value) goto invalid;
1475 if (id != CAL_GREGORIAN)
1477 const USHORT *ids = locale_strings + locale->scalendartype;
1478 for (i = 0; i < ids[0]; i++) if (ids[1 + i] == id) break;
1479 if (i == ids[0]) goto invalid;
1481 if (!(cal = get_calendar_data( locale, id ))) goto invalid;
1483 switch (LOWORD(type))
1485 case CAL_ICALINTVALUE:
1486 return cal_return_number( cal->icalintvalue, type, buffer, len, value );
1488 case CAL_SCALNAME:
1489 return locale_return_strarray( locale->calnames, id - 1, type, buffer, len );
1491 case CAL_IYEAROFFSETRANGE:
1492 if (cal->iyearoffsetrange)
1494 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1495 const short *info = (const short *)locale_strings + array[0];
1496 val = (info[5] < 0) ? -info[5] : info[5] + 1; /* year zero */
1498 return cal_return_number( val, type, buffer, len, value );
1500 case CAL_SERASTRING:
1501 if (id == CAL_GREGORIAN) return locale_return_string( locale->serastring, type, buffer, len );
1502 if (cal->iyearoffsetrange)
1504 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1505 const short *info = (const short *)locale_strings + array[0];
1506 val = info[1] - 1;
1508 return locale_return_strarray( cal->serastring, val, type, buffer, len );
1510 case CAL_SSHORTDATE:
1511 val = (id == CAL_GREGORIAN) ? locale->sshortdate : cal->sshortdate;
1512 return locale_return_strarray( val, 0, type, buffer, len );
1514 case CAL_SLONGDATE:
1515 val = (id == CAL_GREGORIAN) ? locale->slongdate : cal->slongdate;
1516 return locale_return_strarray( val, 0, type, buffer, len );
1518 case CAL_SDAYNAME1:
1519 case CAL_SDAYNAME2:
1520 case CAL_SDAYNAME3:
1521 case CAL_SDAYNAME4:
1522 case CAL_SDAYNAME5:
1523 case CAL_SDAYNAME6:
1524 case CAL_SDAYNAME7:
1525 val = (id == CAL_GREGORIAN) ? locale->sdayname : cal->sdayname;
1526 return locale_return_strarray( val, (LOWORD(type) - CAL_SDAYNAME1 + 1) % 7, type, buffer, len );
1528 case CAL_SABBREVDAYNAME1:
1529 case CAL_SABBREVDAYNAME2:
1530 case CAL_SABBREVDAYNAME3:
1531 case CAL_SABBREVDAYNAME4:
1532 case CAL_SABBREVDAYNAME5:
1533 case CAL_SABBREVDAYNAME6:
1534 case CAL_SABBREVDAYNAME7:
1535 val = (id == CAL_GREGORIAN) ? locale->sabbrevdayname : cal->sabbrevdayname;
1536 return locale_return_strarray( val, (LOWORD(type) - CAL_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1537 case CAL_SMONTHNAME1:
1538 case CAL_SMONTHNAME2:
1539 case CAL_SMONTHNAME3:
1540 case CAL_SMONTHNAME4:
1541 case CAL_SMONTHNAME5:
1542 case CAL_SMONTHNAME6:
1543 case CAL_SMONTHNAME7:
1544 case CAL_SMONTHNAME8:
1545 case CAL_SMONTHNAME9:
1546 case CAL_SMONTHNAME10:
1547 case CAL_SMONTHNAME11:
1548 case CAL_SMONTHNAME12:
1549 case CAL_SMONTHNAME13:
1550 if (id != CAL_GREGORIAN) val = cal->smonthname;
1551 else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) val = locale->sgenitivemonth;
1552 else val = locale->smonthname;
1553 return locale_return_strarray( val, LOWORD(type) - CAL_SMONTHNAME1, type, buffer, len );
1555 case CAL_SABBREVMONTHNAME1:
1556 case CAL_SABBREVMONTHNAME2:
1557 case CAL_SABBREVMONTHNAME3:
1558 case CAL_SABBREVMONTHNAME4:
1559 case CAL_SABBREVMONTHNAME5:
1560 case CAL_SABBREVMONTHNAME6:
1561 case CAL_SABBREVMONTHNAME7:
1562 case CAL_SABBREVMONTHNAME8:
1563 case CAL_SABBREVMONTHNAME9:
1564 case CAL_SABBREVMONTHNAME10:
1565 case CAL_SABBREVMONTHNAME11:
1566 case CAL_SABBREVMONTHNAME12:
1567 case CAL_SABBREVMONTHNAME13:
1568 if (id != CAL_GREGORIAN) val = cal->sabbrevmonthname;
1569 else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) val = locale->sabbrevgenitivemonth;
1570 else val = locale->sabbrevmonthname;
1571 return locale_return_strarray( val, LOWORD(type) - CAL_SABBREVMONTHNAME1, type, buffer, len );
1573 case CAL_SYEARMONTH:
1574 val = (id == CAL_GREGORIAN) ? locale->syearmonth : cal->syearmonth;
1575 return locale_return_strarray( val, 0, type, buffer, len );
1577 case CAL_ITWODIGITYEARMAX:
1578 return cal_return_number( cal->itwodigityearmax, type, buffer, len, value );
1580 case CAL_SSHORTESTDAYNAME1:
1581 case CAL_SSHORTESTDAYNAME2:
1582 case CAL_SSHORTESTDAYNAME3:
1583 case CAL_SSHORTESTDAYNAME4:
1584 case CAL_SSHORTESTDAYNAME5:
1585 case CAL_SSHORTESTDAYNAME6:
1586 case CAL_SSHORTESTDAYNAME7:
1587 val = (id == CAL_GREGORIAN) ? locale->sshortestdayname : cal->sshortestdayname;
1588 return locale_return_strarray( val, (LOWORD(type) - CAL_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1590 case CAL_SMONTHDAY:
1591 val = (id == CAL_GREGORIAN) ? locale->smonthday : cal->smonthday;
1592 return locale_return_strarray( val, 0, type, buffer, len );
1594 case CAL_SABBREVERASTRING:
1595 if (id == CAL_GREGORIAN) return locale_return_string( locale->sabbreverastring, type, buffer, len );
1596 if (cal->iyearoffsetrange)
1598 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1599 const short *info = (const short *)locale_strings + array[0];
1600 val = info[1] - 1;
1602 return locale_return_strarray( cal->sabbreverastring, val, type, buffer, len );
1604 case CAL_SRELATIVELONGDATE:
1605 val = (id == CAL_GREGORIAN) ? locale->srelativelongdate : cal->srelativelongdate;
1606 return locale_return_string( val, type, buffer, len );
1608 case CAL_SENGLISHERANAME:
1609 case CAL_SENGLISHABBREVERANAME:
1610 /* not supported on Windows */
1611 break;
1613 SetLastError( ERROR_INVALID_FLAGS );
1614 return 0;
1616 invalid:
1617 SetLastError( ERROR_INVALID_PARAMETER );
1618 return 0;
1622 /* get geo information from the locale.nls file */
1623 static int get_geo_info( const struct geo_id *geo, enum SYSGEOTYPE type,
1624 WCHAR *buffer, int len, LANGID lang )
1626 WCHAR tmp[12];
1627 const WCHAR *str = tmp;
1628 int ret;
1630 switch (type)
1632 case GEO_NATION:
1633 if (geo->class != GEOCLASS_NATION) return 0;
1634 /* fall through */
1635 case GEO_ID:
1636 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->id );
1637 break;
1638 case GEO_ISO_UN_NUMBER:
1639 swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", geo->uncode );
1640 break;
1641 case GEO_PARENT:
1642 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->parent );
1643 break;
1644 case GEO_DIALINGCODE:
1645 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->dialcode );
1646 break;
1647 case GEO_ISO2:
1648 str = geo->iso2;
1649 break;
1650 case GEO_ISO3:
1651 str = geo->iso3;
1652 break;
1653 case GEO_LATITUDE:
1654 str = geo->latitude;
1655 break;
1656 case GEO_LONGITUDE:
1657 str = geo->longitude;
1658 break;
1659 case GEO_CURRENCYCODE:
1660 str = geo->currcode;
1661 break;
1662 case GEO_CURRENCYSYMBOL:
1663 str = geo->currsymbol;
1664 break;
1665 case GEO_RFC1766:
1666 case GEO_LCID:
1667 case GEO_FRIENDLYNAME:
1668 case GEO_OFFICIALNAME:
1669 case GEO_TIMEZONES:
1670 case GEO_OFFICIALLANGUAGES:
1671 case GEO_NAME:
1672 FIXME( "type %u is not supported\n", type );
1673 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1674 return 0;
1675 default:
1676 SetLastError( ERROR_INVALID_FLAGS );
1677 return 0;
1680 ret = lstrlenW(str) + 1;
1681 if (!buffer || !len) return ret;
1683 memcpy( buffer, str, min( ret, len ) * sizeof(WCHAR) );
1684 if (len < ret) SetLastError( ERROR_INSUFFICIENT_BUFFER );
1685 return len < ret ? 0 : ret;
1689 /* update a registry value based on the current user locale info */
1690 static void update_registry_value( UINT type, const WCHAR *value )
1692 WCHAR buffer[80];
1693 UINT len = get_locale_info( user_locale, user_lcid, type, buffer, ARRAY_SIZE(buffer) );
1694 if (len) RegSetValueExW( intl_key, value, 0, REG_SZ, (BYTE *)buffer, len * sizeof(WCHAR) );
1698 /* update all registry values upon user locale change */
1699 static void update_locale_registry(void)
1701 WCHAR buffer[80];
1702 UINT len;
1704 len = swprintf( buffer, ARRAY_SIZE(buffer), L"%08x", GetUserDefaultLCID() );
1705 RegSetValueExW( intl_key, L"Locale", 0, REG_SZ, (BYTE *)buffer, (len + 1) * sizeof(WCHAR) );
1707 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICALENDARTYPE, entry_icalendartype.value );
1708 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICOUNTRY, entry_icountry.value );
1709 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICURRDIGITS, entry_icurrdigits.value );
1710 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ICURRENCY, entry_icurrency.value );
1711 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDIGITS, entry_idigits.value );
1712 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDIGITSUBSTITUTION, entry_idigitsubstitution.value );
1713 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IFIRSTDAYOFWEEK, entry_ifirstdayofweek.value );
1714 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IFIRSTWEEKOFYEAR, entry_ifirstweekofyear.value );
1715 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ILZERO, entry_ilzero.value );
1716 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IMEASURE, entry_imeasure.value );
1717 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_INEGCURR, entry_inegcurr.value );
1718 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_INEGNUMBER, entry_inegnumber.value );
1719 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IPAPERSIZE, entry_ipapersize.value );
1720 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_S1159, entry_s1159.value );
1721 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_S2359, entry_s2359.value );
1722 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SCURRENCY, entry_scurrency.value );
1723 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SDECIMAL, entry_sdecimal.value );
1724 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SGROUPING, entry_sgrouping.value );
1725 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SLIST, entry_slist.value );
1726 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SLONGDATE, entry_slongdate.value );
1727 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SMONDECIMALSEP, entry_smondecimalsep.value );
1728 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SMONGROUPING, entry_smongrouping.value );
1729 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SMONTHOUSANDSEP, entry_smonthousandsep.value );
1730 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNATIVEDIGITS, entry_snativedigits.value );
1731 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNEGATIVESIGN, entry_snegativesign.value );
1732 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SPOSITIVESIGN, entry_spositivesign.value );
1733 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SSHORTDATE, entry_sshortdate.value );
1734 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SSHORTTIME, entry_sshorttime.value );
1735 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STHOUSAND, entry_sthousand.value );
1736 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STIMEFORMAT, entry_stimeformat.value );
1737 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SYEARMONTH, entry_syearmonth.value );
1738 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDATE, L"iDate" );
1739 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIME, L"iTime" );
1740 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIMEMARKPOSN, L"iTimePrefix" );
1741 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITLZERO, L"iTLZero" );
1742 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SDATE, L"sDate" );
1743 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STIME, L"sTime" );
1744 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SABBREVLANGNAME, L"sLanguage" );
1745 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SCOUNTRY, L"sCountry" );
1746 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNAME, L"LocaleName" );
1747 SetUserGeoID( user_locale->igeoid );
1751 /***********************************************************************
1752 * init_locale
1754 void init_locale( HMODULE module )
1756 UINT ansi_cp = 0, oem_cp = 0;
1757 USHORT *ansi_ptr, *oem_ptr;
1758 void *sort_ptr;
1759 WCHAR bufferW[LOCALE_NAME_MAX_LENGTH];
1760 DYNAMIC_TIME_ZONE_INFORMATION timezone;
1761 const WCHAR *user_locale_name;
1762 DWORD count;
1763 SIZE_T size;
1764 HKEY hkey;
1766 kernelbase_handle = module;
1767 load_locale_nls();
1769 NtQueryDefaultLocale( FALSE, &system_lcid );
1770 NtQueryDefaultLocale( FALSE, &user_lcid );
1771 if (!(system_locale = NlsValidateLocale( &system_lcid, 0 )))
1773 if (GetEnvironmentVariableW( L"WINELOCALE", bufferW, ARRAY_SIZE(bufferW) ))
1774 system_locale = get_locale_by_name( bufferW, &system_lcid );
1775 if (!system_locale) system_locale = get_locale_by_name( L"en-US", &system_lcid );
1777 system_lcid = system_locale->ilanguage;
1778 if (system_lcid == LOCALE_CUSTOM_UNSPECIFIED) system_lcid = LOCALE_CUSTOM_DEFAULT;
1780 if (!(user_locale = NlsValidateLocale( &user_lcid, 0 )))
1782 if (GetEnvironmentVariableW( L"WINEUSERLOCALE", bufferW, ARRAY_SIZE(bufferW) ))
1783 user_locale = get_locale_by_name( bufferW, &user_lcid );
1784 if (!user_locale) user_locale = system_locale;
1786 user_lcid = user_locale->ilanguage;
1787 if (user_lcid == LOCALE_CUSTOM_UNSPECIFIED) user_lcid = LOCALE_CUSTOM_DEFAULT;
1789 if (GetEnvironmentVariableW( L"WINEUNIXCP", bufferW, ARRAY_SIZE(bufferW) ))
1790 unix_cp = wcstoul( bufferW, NULL, 10 );
1792 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
1793 (WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) );
1794 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
1795 (WCHAR *)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
1796 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
1797 (WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
1799 NtGetNlsSectionPtr( 9, 0, NULL, &sort_ptr, &size );
1800 NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size );
1801 init_sortkeys( sort_ptr );
1803 if (!ansi_cp || NtGetNlsSectionPtr( 11, ansi_cp, NULL, (void **)&ansi_ptr, &size ))
1804 NtGetNlsSectionPtr( 11, 1252, NULL, (void **)&ansi_ptr, &size );
1805 if (!oem_cp || NtGetNlsSectionPtr( 11, oem_cp, 0, (void **)&oem_ptr, &size ))
1806 NtGetNlsSectionPtr( 11, 437, NULL, (void **)&oem_ptr, &size );
1807 NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr;
1808 NtCurrentTeb()->Peb->OemCodePageData = oem_ptr;
1809 NtCurrentTeb()->Peb->UnicodeCaseTableData = sort.casemap;
1810 RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info );
1811 RtlResetRtlTranslations( &nls_info );
1813 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
1814 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
1815 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1816 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tz_key, NULL );
1817 RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International",
1818 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL );
1820 current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT );
1822 if (GetDynamicTimeZoneInformation( &timezone ) != TIME_ZONE_ID_INVALID &&
1823 !RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\TimeZoneInformation",
1824 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1826 RegSetValueExW( hkey, L"StandardName", 0, REG_SZ, (BYTE *)timezone.StandardName,
1827 (lstrlenW(timezone.StandardName) + 1) * sizeof(WCHAR) );
1828 RegSetValueExW( hkey, L"TimeZoneKeyName", 0, REG_SZ, (BYTE *)timezone.TimeZoneKeyName,
1829 (lstrlenW(timezone.TimeZoneKeyName) + 1) * sizeof(WCHAR) );
1830 RegCloseKey( hkey );
1833 /* Update registry contents if the user locale has changed.
1834 * This simulates the action of the Windows control panel. */
1836 user_locale_name = locale_strings + user_locale->sname + 1;
1837 count = sizeof(bufferW);
1838 if (!RegQueryValueExW( intl_key, L"LocaleName", NULL, NULL, (BYTE *)bufferW, &count ))
1840 if (!wcscmp( bufferW, user_locale_name )) return; /* unchanged */
1841 TRACE( "updating registry, locale changed %s -> %s\n",
1842 debugstr_w(bufferW), debugstr_w(user_locale_name) );
1844 else TRACE( "updating registry, locale changed none -> %s\n", debugstr_w(user_locale_name) );
1846 update_locale_registry();
1848 if (!RegCreateKeyExW( nls_key, L"Codepage",
1849 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1851 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", ansi_cp );
1852 RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1853 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", oem_cp );
1854 RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1855 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", mac_cp );
1856 RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1857 RegCloseKey( hkey );
1862 static inline USHORT get_table_entry( const USHORT *table, WCHAR ch )
1864 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
1868 static inline WCHAR casemap( const USHORT *table, WCHAR ch )
1870 return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
1874 static inline WORD get_char_type( DWORD type, WCHAR ch )
1876 const BYTE *ptr = sort.ctype_idx + ((const WORD *)sort.ctype_idx)[ch >> 8];
1877 ptr = sort.ctype_idx + ((const WORD *)ptr)[(ch >> 4) & 0x0f] + (ch & 0x0f);
1878 return sort.ctypes[*ptr * 3 + type / 2];
1882 static BYTE rol( BYTE val, BYTE count )
1884 return (val << count) | (val >> (8 - count));
1888 static BYTE get_char_props( const struct norm_table *info, unsigned int ch )
1890 const BYTE *level1 = (const BYTE *)((const USHORT *)info + info->props_level1);
1891 const BYTE *level2 = (const BYTE *)((const USHORT *)info + info->props_level2);
1892 BYTE off = level1[ch / 128];
1894 if (!off || off >= 0xfb) return rol( off, 5 );
1895 return level2[(off - 1) * 128 + ch % 128];
1899 static const WCHAR *get_decomposition( WCHAR ch, unsigned int *ret_len )
1901 const struct pair { WCHAR src; USHORT dst; } *pairs;
1902 const USHORT *hash_table = (const USHORT *)norm_info + norm_info->decomp_hash;
1903 const WCHAR *ret;
1904 unsigned int i, pos, end, len, hash;
1906 *ret_len = 1;
1907 hash = ch % norm_info->decomp_size;
1908 pos = hash_table[hash];
1909 if (pos >> 13)
1911 if (get_char_props( norm_info, ch ) != 0xbf) return NULL;
1912 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pos & 0x1fff);
1913 len = pos >> 13;
1915 else
1917 pairs = (const struct pair *)((const USHORT *)norm_info + norm_info->decomp_map);
1919 /* find the end of the hash bucket */
1920 for (i = hash + 1; i < norm_info->decomp_size; i++) if (!(hash_table[i] >> 13)) break;
1921 if (i < norm_info->decomp_size) end = hash_table[i];
1922 else for (end = pos; pairs[end].src; end++) ;
1924 for ( ; pos < end; pos++)
1926 if (pairs[pos].src != (WCHAR)ch) continue;
1927 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pairs[pos].dst & 0x1fff);
1928 len = pairs[pos].dst >> 13;
1929 break;
1931 if (pos >= end) return NULL;
1934 if (len == 7) while (ret[len]) len++;
1935 if (!ret[0]) len = 0; /* ignored char */
1936 *ret_len = len;
1937 return ret;
1941 static WCHAR compose_chars( WCHAR ch1, WCHAR ch2 )
1943 const USHORT *table = (const USHORT *)norm_info + norm_info->comp_hash;
1944 const WCHAR *chars = (const USHORT *)norm_info + norm_info->comp_seq;
1945 unsigned int hash, start, end, i;
1946 WCHAR ch[3];
1948 hash = (ch1 + 95 * ch2) % norm_info->comp_size;
1949 start = table[hash];
1950 end = table[hash + 1];
1951 while (start < end)
1953 for (i = 0; i < 3; i++, start++)
1955 ch[i] = chars[start];
1956 if (IS_HIGH_SURROGATE( ch[i] )) start++;
1958 if (ch[0] == ch1 && ch[1] == ch2) return ch[2];
1960 return 0;
1964 static UINT get_locale_codepage( const NLS_LOCALE_DATA *locale, ULONG flags )
1966 UINT ret = locale->idefaultansicodepage;
1967 if ((flags & LOCALE_USE_CP_ACP) || ret == CP_UTF8) ret = nls_info.AnsiTableInfo.CodePage;
1968 return ret;
1972 static UINT get_lcid_codepage( LCID lcid, ULONG flags )
1974 UINT ret = nls_info.AnsiTableInfo.CodePage;
1976 if (!(flags & LOCALE_USE_CP_ACP) && lcid != system_lcid)
1978 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
1979 if (locale) ret = locale->idefaultansicodepage;
1981 return ret;
1985 static const CPTABLEINFO *get_codepage_table( UINT codepage )
1987 static const CPTABLEINFO utf7_cpinfo = { CP_UTF7, 5, '?', 0xfffd, '?', '?' };
1988 static const CPTABLEINFO utf8_cpinfo = { CP_UTF8, 4, '?', 0xfffd, '?', '?' };
1989 unsigned int i;
1990 USHORT *ptr;
1991 SIZE_T size;
1993 switch (codepage)
1995 case CP_ACP:
1996 return &nls_info.AnsiTableInfo;
1997 case CP_OEMCP:
1998 return &nls_info.OemTableInfo;
1999 case CP_MACCP:
2000 codepage = mac_cp;
2001 break;
2002 case CP_THREAD_ACP:
2003 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 );
2004 break;
2006 if (codepage == nls_info.AnsiTableInfo.CodePage) return &nls_info.AnsiTableInfo;
2007 if (codepage == nls_info.OemTableInfo.CodePage) return &nls_info.OemTableInfo;
2008 if (codepage == CP_UTF8) return &utf8_cpinfo;
2009 if (codepage == CP_UTF7) return &utf7_cpinfo;
2011 RtlEnterCriticalSection( &locale_section );
2013 for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done;
2015 if (i == ARRAY_SIZE( codepages ))
2017 RtlLeaveCriticalSection( &locale_section );
2018 ERR( "too many codepages\n" );
2019 return NULL;
2021 if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size ))
2023 RtlLeaveCriticalSection( &locale_section );
2024 SetLastError( ERROR_INVALID_PARAMETER );
2025 return NULL;
2027 RtlInitCodePageTable( ptr, &codepages[i] );
2028 nb_codepages++;
2029 done:
2030 RtlLeaveCriticalSection( &locale_section );
2031 return &codepages[i];
2035 static const WCHAR *get_ligature( WCHAR wc )
2037 int low = 0, high = ARRAY_SIZE( ligatures ) -1;
2038 while (low <= high)
2040 int pos = (low + high) / 2;
2041 if (ligatures[pos][0] < wc) low = pos + 1;
2042 else if (ligatures[pos][0] > wc) high = pos - 1;
2043 else return ligatures[pos] + 1;
2045 return NULL;
2049 static NTSTATUS expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2051 int i, len, pos = 0;
2052 NTSTATUS ret = STATUS_SUCCESS;
2053 const WCHAR *expand;
2055 for (i = 0; i < srclen; i++)
2057 if (!(expand = get_ligature( src[i] )))
2059 expand = src + i;
2060 len = 1;
2062 else len = lstrlenW( expand );
2064 if (*dstlen && ret == STATUS_SUCCESS)
2066 if (pos + len <= *dstlen) memcpy( dst + pos, expand, len * sizeof(WCHAR) );
2067 else ret = STATUS_BUFFER_TOO_SMALL;
2069 pos += len;
2071 *dstlen = pos;
2072 return ret;
2076 static NTSTATUS fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2078 extern const WCHAR wine_digitmap[] DECLSPEC_HIDDEN;
2079 int i, len = *dstlen;
2081 *dstlen = srclen;
2082 if (!len) return STATUS_SUCCESS;
2083 if (srclen > len) return STATUS_BUFFER_TOO_SMALL;
2084 for (i = 0; i < srclen; i++)
2086 WCHAR digit = get_table_entry( wine_digitmap, src[i] );
2087 dst[i] = digit ? digit : src[i];
2089 return STATUS_SUCCESS;
2093 static NTSTATUS fold_string( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2095 NTSTATUS ret;
2096 WCHAR *tmp;
2098 switch (flags)
2100 case MAP_PRECOMPOSED:
2101 return RtlNormalizeString( NormalizationC, src, srclen, dst, dstlen );
2102 case MAP_FOLDCZONE:
2103 case MAP_PRECOMPOSED | MAP_FOLDCZONE:
2104 return RtlNormalizeString( NormalizationKC, src, srclen, dst, dstlen );
2105 case MAP_COMPOSITE:
2106 return RtlNormalizeString( NormalizationD, src, srclen, dst, dstlen );
2107 case MAP_COMPOSITE | MAP_FOLDCZONE:
2108 return RtlNormalizeString( NormalizationKD, src, srclen, dst, dstlen );
2109 case MAP_FOLDDIGITS:
2110 return fold_digits( src, srclen, dst, dstlen );
2111 case MAP_EXPAND_LIGATURES:
2112 case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
2113 return expand_ligatures( src, srclen, dst, dstlen );
2114 case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
2115 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2116 return STATUS_NO_MEMORY;
2117 fold_digits( src, srclen, tmp, &srclen );
2118 ret = RtlNormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
2119 break;
2120 case MAP_FOLDDIGITS | MAP_FOLDCZONE:
2121 case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
2122 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2123 return STATUS_NO_MEMORY;
2124 fold_digits( src, srclen, tmp, &srclen );
2125 ret = RtlNormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
2126 break;
2127 case MAP_FOLDDIGITS | MAP_COMPOSITE:
2128 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2129 return STATUS_NO_MEMORY;
2130 fold_digits( src, srclen, tmp, &srclen );
2131 ret = RtlNormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
2132 break;
2133 case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
2134 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2135 return STATUS_NO_MEMORY;
2136 fold_digits( src, srclen, tmp, &srclen );
2137 ret = RtlNormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
2138 break;
2139 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
2140 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
2141 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2142 return STATUS_NO_MEMORY;
2143 fold_digits( src, srclen, tmp, &srclen );
2144 ret = expand_ligatures( tmp, srclen, dst, dstlen );
2145 break;
2146 default:
2147 return STATUS_INVALID_PARAMETER_1;
2149 RtlFreeHeap( GetProcessHeap(), 0, tmp );
2150 return ret;
2154 static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2156 int len, i;
2158 if (flags)
2160 SetLastError( ERROR_INVALID_FLAGS );
2161 return 0;
2163 if (!dstlen) return srclen;
2164 len = min( srclen, dstlen );
2165 for (i = 0; i < len; i++)
2167 unsigned char c = src[i];
2168 dst[i] = (c < 0x20) ? c : c + 0xf000;
2170 if (len < srclen)
2172 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2173 return 0;
2175 return len;
2179 static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2181 static const signed char base64_decoding_table[] =
2183 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2184 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2185 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2186 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2187 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2188 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2189 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2190 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2193 const char *source_end = src + srclen;
2194 int offset = 0, pos = 0;
2195 DWORD byte_pair = 0;
2197 if (flags)
2199 SetLastError( ERROR_INVALID_FLAGS );
2200 return 0;
2202 #define OUTPUT(ch) \
2203 do { \
2204 if (dstlen > 0) \
2206 if (pos >= dstlen) goto overflow; \
2207 dst[pos] = (ch); \
2209 pos++; \
2210 } while(0)
2212 while (src < source_end)
2214 if (*src == '+')
2216 src++;
2217 if (src >= source_end) break;
2218 if (*src == '-')
2220 /* just a plus sign escaped as +- */
2221 OUTPUT( '+' );
2222 src++;
2223 continue;
2228 signed char sextet = *src;
2229 if (sextet == '-')
2231 /* skip over the dash and end base64 decoding
2232 * the current, unfinished byte pair is discarded */
2233 src++;
2234 offset = 0;
2235 break;
2237 if (sextet < 0)
2239 /* the next character of src is < 0 and therefore not part of a base64 sequence
2240 * the current, unfinished byte pair is NOT discarded in this case
2241 * this is probably a bug in Windows */
2242 break;
2244 sextet = base64_decoding_table[sextet];
2245 if (sextet == -1)
2247 /* -1 means that the next character of src is not part of a base64 sequence
2248 * in other words, all sextets in this base64 sequence have been processed
2249 * the current, unfinished byte pair is discarded */
2250 offset = 0;
2251 break;
2254 byte_pair = (byte_pair << 6) | sextet;
2255 offset += 6;
2256 if (offset >= 16)
2258 /* this byte pair is done */
2259 OUTPUT( byte_pair >> (offset - 16) );
2260 offset -= 16;
2262 src++;
2264 while (src < source_end);
2266 else
2268 OUTPUT( (unsigned char)*src );
2269 src++;
2272 return pos;
2274 overflow:
2275 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2276 return 0;
2277 #undef OUTPUT
2281 static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2283 DWORD reslen;
2284 NTSTATUS status;
2286 if (!dstlen) dst = NULL;
2287 status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen );
2288 if (status == STATUS_SOME_NOT_MAPPED)
2290 if (flags & MB_ERR_INVALID_CHARS)
2292 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2293 return 0;
2296 else if (!set_ntstatus( status )) reslen = 0;
2298 return reslen / sizeof(WCHAR);
2302 static inline int is_private_use_area_char( WCHAR code )
2304 return (code >= 0xe000 && code <= 0xf8ff);
2308 static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen )
2310 if (info->DBCSOffsets)
2312 for ( ; srclen; src++, srclen-- )
2314 USHORT off = info->DBCSOffsets[*src];
2315 if (off)
2317 if (srclen == 1) break; /* partial char, error */
2318 if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar &&
2319 ((src[0] << 8) | src[1]) != info->TransUniDefaultChar) break;
2320 src++;
2321 srclen--;
2322 continue;
2324 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2325 break;
2326 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2329 else
2331 for ( ; srclen; src++, srclen-- )
2333 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2334 break;
2335 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2338 return !!srclen;
2343 static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2344 WCHAR *dst, int dstlen )
2346 WCHAR ch;
2347 USHORT off;
2348 int len;
2349 const WCHAR *decomp;
2350 unsigned int decomp_len;
2352 if (info->DBCSOffsets)
2354 if (!dstlen) /* compute length */
2356 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2358 if ((off = info->DBCSOffsets[*src]))
2360 if (srclen > 1 && src[1])
2362 src++;
2363 srclen--;
2364 ch = info->DBCSOffsets[off + *src];
2366 else ch = info->UniDefaultChar;
2368 else ch = info->MultiByteTable[*src];
2369 get_decomposition( ch, &decomp_len );
2371 return len;
2374 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2376 if ((off = info->DBCSOffsets[*src]))
2378 if (srclen > 1 && src[1])
2380 src++;
2381 srclen--;
2382 ch = info->DBCSOffsets[off + *src];
2384 else ch = info->UniDefaultChar;
2386 else ch = info->MultiByteTable[*src];
2388 if ((decomp = get_decomposition( ch, &decomp_len )))
2390 if (len < decomp_len) break;
2391 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2393 else *dst = ch;
2396 else
2398 if (!dstlen) /* compute length */
2400 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2401 get_decomposition( info->MultiByteTable[*src], &decomp_len );
2402 return len;
2405 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2407 ch = info->MultiByteTable[*src];
2408 if ((decomp = get_decomposition( ch, &decomp_len )))
2410 if (len < decomp_len) break;
2411 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2413 else *dst = ch;
2417 if (srclen)
2419 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2420 return 0;
2422 return dstlen - len;
2426 static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2427 WCHAR *dst, int dstlen )
2429 const USHORT *table = info->MultiByteTable;
2430 int ret = srclen;
2432 if (!dstlen) return srclen;
2434 if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */
2436 srclen = dstlen;
2437 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2438 ret = 0;
2441 while (srclen >= 16)
2443 dst[0] = table[src[0]];
2444 dst[1] = table[src[1]];
2445 dst[2] = table[src[2]];
2446 dst[3] = table[src[3]];
2447 dst[4] = table[src[4]];
2448 dst[5] = table[src[5]];
2449 dst[6] = table[src[6]];
2450 dst[7] = table[src[7]];
2451 dst[8] = table[src[8]];
2452 dst[9] = table[src[9]];
2453 dst[10] = table[src[10]];
2454 dst[11] = table[src[11]];
2455 dst[12] = table[src[12]];
2456 dst[13] = table[src[13]];
2457 dst[14] = table[src[14]];
2458 dst[15] = table[src[15]];
2459 src += 16;
2460 dst += 16;
2461 srclen -= 16;
2464 /* now handle the remaining characters */
2465 src += srclen;
2466 dst += srclen;
2467 switch (srclen)
2469 case 15: dst[-15] = table[src[-15]];
2470 case 14: dst[-14] = table[src[-14]];
2471 case 13: dst[-13] = table[src[-13]];
2472 case 12: dst[-12] = table[src[-12]];
2473 case 11: dst[-11] = table[src[-11]];
2474 case 10: dst[-10] = table[src[-10]];
2475 case 9: dst[-9] = table[src[-9]];
2476 case 8: dst[-8] = table[src[-8]];
2477 case 7: dst[-7] = table[src[-7]];
2478 case 6: dst[-6] = table[src[-6]];
2479 case 5: dst[-5] = table[src[-5]];
2480 case 4: dst[-4] = table[src[-4]];
2481 case 3: dst[-3] = table[src[-3]];
2482 case 2: dst[-2] = table[src[-2]];
2483 case 1: dst[-1] = table[src[-1]];
2484 case 0: break;
2486 return ret;
2490 static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2491 WCHAR *dst, int dstlen )
2493 USHORT off;
2494 int i;
2496 if (!dstlen)
2498 for (i = 0; srclen; i++, src++, srclen--)
2499 if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; }
2500 return i;
2503 for (i = dstlen; srclen && i; i--, srclen--, src++, dst++)
2505 if ((off = info->DBCSOffsets[*src]))
2507 if (srclen > 1 && src[1])
2509 src++;
2510 srclen--;
2511 *dst = info->DBCSOffsets[off + *src];
2513 else *dst = info->UniDefaultChar;
2515 else *dst = info->MultiByteTable[*src];
2517 if (srclen)
2519 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2520 return 0;
2522 return dstlen - i;
2526 static int mbstowcs_codepage( const CPTABLEINFO *info, DWORD flags, const char *src, int srclen,
2527 WCHAR *dst, int dstlen )
2529 CPTABLEINFO local_info;
2530 const unsigned char *str = (const unsigned char *)src;
2532 if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256)
2534 local_info = *info;
2535 local_info.MultiByteTable += 257;
2536 info = &local_info;
2538 if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen ))
2540 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2541 return 0;
2544 if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen );
2546 if (info->DBCSOffsets)
2547 return mbstowcs_dbcs( info, str, srclen, dst, dstlen );
2548 else
2549 return mbstowcs_sbcs( info, str, srclen, dst, dstlen );
2553 static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2554 const char *defchar, BOOL *used )
2556 int len, i;
2558 if (flags)
2560 SetLastError( ERROR_INVALID_FLAGS );
2561 return 0;
2563 if (defchar || used)
2565 SetLastError( ERROR_INVALID_PARAMETER );
2566 return 0;
2568 if (!dstlen) return srclen;
2569 len = min( srclen, dstlen );
2570 for (i = 0; i < len; i++)
2572 if (src[i] < 0x20) dst[i] = src[i];
2573 else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000;
2574 else
2576 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2577 return 0;
2580 if (srclen > len)
2582 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2583 return 0;
2585 return len;
2589 static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2590 const char *defchar, BOOL *used )
2592 static const char directly_encodable[] =
2594 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
2595 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
2596 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
2597 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
2598 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
2599 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
2600 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
2601 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
2603 #define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
2605 static const char base64_encoding_table[] =
2606 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2608 const WCHAR *source_end = src + srclen;
2609 int pos = 0;
2611 if (defchar || used)
2613 SetLastError( ERROR_INVALID_PARAMETER );
2614 return 0;
2616 if (flags)
2618 SetLastError( ERROR_INVALID_FLAGS );
2619 return 0;
2622 #define OUTPUT(ch) \
2623 do { \
2624 if (dstlen > 0) \
2626 if (pos >= dstlen) goto overflow; \
2627 dst[pos] = (ch); \
2629 pos++; \
2630 } while (0)
2632 while (src < source_end)
2634 if (*src == '+')
2636 OUTPUT( '+' );
2637 OUTPUT( '-' );
2638 src++;
2640 else if (ENCODABLE(*src))
2642 OUTPUT( *src );
2643 src++;
2645 else
2647 unsigned int offset = 0, byte_pair = 0;
2649 OUTPUT( '+' );
2650 while (src < source_end && !ENCODABLE(*src))
2652 byte_pair = (byte_pair << 16) | *src;
2653 offset += 16;
2654 while (offset >= 6)
2656 offset -= 6;
2657 OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] );
2659 src++;
2661 if (offset)
2663 /* Windows won't create a padded base64 character if there's no room for the - sign
2664 * as well ; this is probably a bug in Windows */
2665 if (dstlen > 0 && pos + 1 >= dstlen) goto overflow;
2666 byte_pair <<= (6 - offset);
2667 OUTPUT( base64_encoding_table[byte_pair & 0x3f] );
2669 /* Windows always explicitly terminates the base64 sequence
2670 even though RFC 2152 (page 3, rule 2) does not require this */
2671 OUTPUT( '-' );
2674 return pos;
2676 overflow:
2677 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2678 return 0;
2679 #undef OUTPUT
2680 #undef ENCODABLE
2684 static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2685 const char *defchar, BOOL *used )
2687 DWORD reslen;
2688 NTSTATUS status;
2690 if (defchar || used)
2692 SetLastError( ERROR_INVALID_PARAMETER );
2693 return 0;
2695 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
2696 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
2698 SetLastError( ERROR_INVALID_FLAGS );
2699 return 0;
2701 if (!dstlen) dst = NULL;
2702 status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
2703 if (status == STATUS_SOME_NOT_MAPPED)
2705 if (flags & WC_ERR_INVALID_CHARS)
2707 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2708 return 0;
2711 else if (!set_ntstatus( status )) reslen = 0;
2712 return reslen;
2716 static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2717 char *dst, unsigned int dstlen )
2719 const char *table = info->WideCharTable;
2720 int ret = srclen;
2722 if (!dstlen) return srclen;
2724 if (dstlen < srclen)
2726 /* buffer too small: fill it up to dstlen and return error */
2727 srclen = dstlen;
2728 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2729 ret = 0;
2732 while (srclen >= 16)
2734 dst[0] = table[src[0]];
2735 dst[1] = table[src[1]];
2736 dst[2] = table[src[2]];
2737 dst[3] = table[src[3]];
2738 dst[4] = table[src[4]];
2739 dst[5] = table[src[5]];
2740 dst[6] = table[src[6]];
2741 dst[7] = table[src[7]];
2742 dst[8] = table[src[8]];
2743 dst[9] = table[src[9]];
2744 dst[10] = table[src[10]];
2745 dst[11] = table[src[11]];
2746 dst[12] = table[src[12]];
2747 dst[13] = table[src[13]];
2748 dst[14] = table[src[14]];
2749 dst[15] = table[src[15]];
2750 src += 16;
2751 dst += 16;
2752 srclen -= 16;
2755 /* now handle remaining characters */
2756 src += srclen;
2757 dst += srclen;
2758 switch(srclen)
2760 case 15: dst[-15] = table[src[-15]];
2761 case 14: dst[-14] = table[src[-14]];
2762 case 13: dst[-13] = table[src[-13]];
2763 case 12: dst[-12] = table[src[-12]];
2764 case 11: dst[-11] = table[src[-11]];
2765 case 10: dst[-10] = table[src[-10]];
2766 case 9: dst[-9] = table[src[-9]];
2767 case 8: dst[-8] = table[src[-8]];
2768 case 7: dst[-7] = table[src[-7]];
2769 case 6: dst[-6] = table[src[-6]];
2770 case 5: dst[-5] = table[src[-5]];
2771 case 4: dst[-4] = table[src[-4]];
2772 case 3: dst[-3] = table[src[-3]];
2773 case 2: dst[-2] = table[src[-2]];
2774 case 1: dst[-1] = table[src[-1]];
2775 case 0: break;
2777 return ret;
2781 static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2782 char *dst, unsigned int dstlen )
2784 const USHORT *table = info->WideCharTable;
2785 int i;
2787 if (!dstlen)
2789 for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++;
2790 return i;
2793 for (i = dstlen; srclen && i; i--, srclen--, src++)
2795 if (table[*src] & 0xff00)
2797 if (i == 1) break; /* do not output a partial char */
2798 i--;
2799 *dst++ = table[*src] >> 8;
2801 *dst++ = (char)table[*src];
2803 if (srclen)
2805 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2806 return 0;
2808 return dstlen - i;
2812 static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2814 const unsigned char *table = info->WideCharTable;
2816 if (wch >= 0x10000) return 0;
2817 if ((flags & WC_NO_BEST_FIT_CHARS) || table[wch] == info->DefaultChar)
2818 return (info->MultiByteTable[table[wch]] == wch);
2819 return 1;
2823 static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2825 const unsigned short *table = info->WideCharTable;
2826 unsigned short ch;
2828 if (wch >= 0x10000) return 0;
2829 ch = table[wch];
2830 if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
2832 if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch;
2833 return info->MultiByteTable[ch] == wch;
2835 return 1;
2839 static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2840 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2842 const char *table = info->WideCharTable;
2843 const char def = defchar ? *defchar : (char)info->DefaultChar;
2844 int i;
2845 BOOL tmp;
2846 WCHAR wch;
2847 unsigned int composed;
2849 if (!used) used = &tmp; /* avoid checking on every char */
2850 *used = FALSE;
2852 if (!dstlen)
2854 for (i = 0; srclen; i++, src++, srclen--)
2856 wch = *src;
2857 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2859 /* now check if we can use the composed char */
2860 if (is_valid_sbcs_mapping( info, flags, composed ))
2862 /* we have a good mapping, use it */
2863 src++;
2864 srclen--;
2865 continue;
2867 /* no mapping for the composed char, check the other flags */
2868 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2870 *used = TRUE;
2871 src++; /* skip the non-spacing char */
2872 srclen--;
2873 continue;
2875 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2877 src++;
2878 srclen--;
2880 /* WC_SEPCHARS is the default */
2882 if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch );
2884 return i;
2887 for (i = dstlen; srclen && i; dst++, i--, src++, srclen--)
2889 wch = *src;
2890 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2892 /* now check if we can use the composed char */
2893 if (is_valid_sbcs_mapping( info, flags, composed ))
2895 /* we have a good mapping, use it */
2896 *dst = table[composed];
2897 src++;
2898 srclen--;
2899 continue;
2901 /* no mapping for the composed char, check the other flags */
2902 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2904 *dst = def;
2905 *used = TRUE;
2906 src++; /* skip the non-spacing char */
2907 srclen--;
2908 continue;
2910 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2912 src++;
2913 srclen--;
2915 /* WC_SEPCHARS is the default */
2918 *dst = table[wch];
2919 if (!is_valid_sbcs_mapping( info, flags, wch ))
2921 *dst = def;
2922 *used = TRUE;
2925 if (srclen)
2927 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2928 return 0;
2930 return dstlen - i;
2934 static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2935 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2937 const USHORT *table = info->WideCharTable;
2938 WCHAR wch, defchar_value;
2939 unsigned int composed;
2940 unsigned short res;
2941 BOOL tmp;
2942 int i;
2944 if (!defchar[1]) defchar_value = (unsigned char)defchar[0];
2945 else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1];
2947 if (!used) used = &tmp; /* avoid checking on every char */
2948 *used = FALSE;
2950 if (!dstlen)
2952 if (!defchar && !used && !(flags & WC_COMPOSITECHECK))
2954 for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++;
2955 return i;
2957 for (i = 0; srclen; srclen--, src++, i++)
2959 wch = *src;
2960 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2962 /* now check if we can use the composed char */
2963 if (is_valid_dbcs_mapping( info, flags, composed ))
2965 /* we have a good mapping for the composed char, use it */
2966 res = table[composed];
2967 if (res & 0xff00) i++;
2968 src++;
2969 srclen--;
2970 continue;
2972 /* no mapping for the composed char, check the other flags */
2973 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2975 if (defchar_value & 0xff00) i++;
2976 *used = TRUE;
2977 src++; /* skip the non-spacing char */
2978 srclen--;
2979 continue;
2981 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2983 src++;
2984 srclen--;
2986 /* WC_SEPCHARS is the default */
2989 res = table[wch];
2990 if (!is_valid_dbcs_mapping( info, flags, wch ))
2992 res = defchar_value;
2993 *used = TRUE;
2995 if (res & 0xff00) i++;
2997 return i;
3001 for (i = dstlen; srclen && i; i--, srclen--, src++)
3003 wch = *src;
3004 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
3006 /* now check if we can use the composed char */
3007 if (is_valid_dbcs_mapping( info, flags, composed ))
3009 /* we have a good mapping for the composed char, use it */
3010 res = table[composed];
3011 src++;
3012 srclen--;
3013 goto output_char;
3015 /* no mapping for the composed char, check the other flags */
3016 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
3018 res = defchar_value;
3019 *used = TRUE;
3020 src++; /* skip the non-spacing char */
3021 srclen--;
3022 goto output_char;
3024 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
3026 src++;
3027 srclen--;
3029 /* WC_SEPCHARS is the default */
3032 res = table[wch];
3033 if (!is_valid_dbcs_mapping( info, flags, wch ))
3035 res = defchar_value;
3036 *used = TRUE;
3039 output_char:
3040 if (res & 0xff00)
3042 if (i == 1) break; /* do not output a partial char */
3043 i--;
3044 *dst++ = res >> 8;
3046 *dst++ = (char)res;
3048 if (srclen)
3050 SetLastError( ERROR_INSUFFICIENT_BUFFER );
3051 return 0;
3053 return dstlen - i;
3057 static int wcstombs_codepage( UINT codepage, DWORD flags, const WCHAR *src, int srclen,
3058 char *dst, int dstlen, const char *defchar, BOOL *used )
3060 const CPTABLEINFO *info = get_codepage_table( codepage );
3062 if (!info)
3064 SetLastError( ERROR_INVALID_PARAMETER );
3065 return 0;
3067 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
3068 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
3070 SetLastError( ERROR_INVALID_FLAGS );
3071 return 0;
3073 if (flags || defchar || used)
3075 if (!defchar) defchar = (const char *)&info->DefaultChar;
3076 if (info->DBCSOffsets)
3077 return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
3078 else
3079 return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
3081 if (info->DBCSOffsets)
3082 return wcstombs_dbcs( info, src, srclen, dst, dstlen );
3083 else
3084 return wcstombs_sbcs( info, src, srclen, dst, dstlen );
3088 static int get_sortkey( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen )
3090 WCHAR dummy[4]; /* no decomposition is larger than 4 chars */
3091 int key_len[4];
3092 char *key_ptr[4];
3093 const WCHAR *src_save = src;
3094 int srclen_save = srclen;
3096 key_len[0] = key_len[1] = key_len[2] = key_len[3] = 0;
3097 for (; srclen; srclen--, src++)
3099 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
3100 dummy[0] = *src;
3101 if (decomposed_len)
3103 for (i = 0; i < decomposed_len; i++)
3105 WCHAR wch = dummy[i];
3106 unsigned int ce;
3108 if ((flags & NORM_IGNORESYMBOLS) &&
3109 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
3110 continue;
3112 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
3114 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
3115 if (ce != (unsigned int)-1)
3117 if (ce >> 16) key_len[0] += 2;
3118 if ((ce >> 8) & 0xff) key_len[1]++;
3119 if ((ce >> 4) & 0x0f) key_len[2]++;
3120 if (ce & 1)
3122 if (wch >> 8) key_len[3]++;
3123 key_len[3]++;
3126 else
3128 key_len[0] += 2;
3129 if (wch >> 8) key_len[0]++;
3130 if (wch & 0xff) key_len[0]++;
3136 if (!dstlen) /* compute length */
3137 /* 4 * '\1' + key length */
3138 return key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4;
3140 if (dstlen < key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4 + 1)
3141 return 0; /* overflow */
3143 src = src_save;
3144 srclen = srclen_save;
3146 key_ptr[0] = dst;
3147 key_ptr[1] = key_ptr[0] + key_len[0] + 1;
3148 key_ptr[2] = key_ptr[1] + key_len[1] + 1;
3149 key_ptr[3] = key_ptr[2] + key_len[2] + 1;
3151 for (; srclen; srclen--, src++)
3153 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
3154 dummy[0] = *src;
3155 if (decomposed_len)
3157 for (i = 0; i < decomposed_len; i++)
3159 WCHAR wch = dummy[i];
3160 unsigned int ce;
3162 if ((flags & NORM_IGNORESYMBOLS) &&
3163 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
3164 continue;
3166 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
3168 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
3169 if (ce != (unsigned int)-1)
3171 WCHAR key;
3172 if ((key = ce >> 16))
3174 *key_ptr[0]++ = key >> 8;
3175 *key_ptr[0]++ = key & 0xff;
3177 /* make key 1 start from 2 */
3178 if ((key = (ce >> 8) & 0xff)) *key_ptr[1]++ = key + 1;
3179 /* make key 2 start from 2 */
3180 if ((key = (ce >> 4) & 0x0f)) *key_ptr[2]++ = key + 1;
3181 /* key 3 is always a character code */
3182 if (ce & 1)
3184 if (wch >> 8) *key_ptr[3]++ = wch >> 8;
3185 if (wch & 0xff) *key_ptr[3]++ = wch & 0xff;
3188 else
3190 *key_ptr[0]++ = 0xff;
3191 *key_ptr[0]++ = 0xfe;
3192 if (wch >> 8) *key_ptr[0]++ = wch >> 8;
3193 if (wch & 0xff) *key_ptr[0]++ = wch & 0xff;
3199 *key_ptr[0] = 1;
3200 *key_ptr[1] = 1;
3201 *key_ptr[2] = 1;
3202 *key_ptr[3]++ = 1;
3203 *key_ptr[3] = 0;
3204 return key_ptr[3] - dst;
3208 /* compose a full-width katakana. return consumed source characters. */
3209 static int compose_katakana( const WCHAR *src, int srclen, WCHAR *dst )
3211 static const BYTE katakana_map[] =
3213 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3214 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3215 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3216 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3217 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3218 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3219 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3220 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3222 WCHAR dummy;
3223 int shift;
3225 if (!dst) dst = &dummy;
3227 switch (*src)
3229 case 0x309b:
3230 case 0x309c:
3231 *dst = *src - 2;
3232 return 1;
3233 case 0x30f0:
3234 case 0x30f1:
3235 case 0x30fd:
3236 *dst = *src;
3237 break;
3238 default:
3239 shift = *src - 0xff61;
3240 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
3241 *dst = katakana_map[shift] | 0x3000;
3242 break;
3245 if (srclen <= 1) return 1;
3247 switch (src[1])
3249 case 0xff9e: /* datakuten (voiced sound) */
3250 if ((*src >= 0xff76 && *src <= 0xff84) || (*src >= 0xff8a && *src <= 0xff8e) || *src == 0x30fd)
3251 *dst += 1;
3252 else if (*src == 0xff73)
3253 *dst = 0x30f4; /* KATAKANA LETTER VU */
3254 else if (*src == 0xff9c)
3255 *dst = 0x30f7; /* KATAKANA LETTER VA */
3256 else if (*src == 0x30f0)
3257 *dst = 0x30f8; /* KATAKANA LETTER VI */
3258 else if (*src == 0x30f1)
3259 *dst = 0x30f9; /* KATAKANA LETTER VE */
3260 else if (*src == 0xff66)
3261 *dst = 0x30fa; /* KATAKANA LETTER VO */
3262 else
3263 return 1;
3264 break;
3265 case 0xff9f: /* handakuten (semi-voiced sound) */
3266 if (*src >= 0xff8a && *src <= 0xff8e)
3267 *dst += 2;
3268 else
3269 return 1;
3270 break;
3271 default:
3272 return 1;
3274 return 2;
3277 /* map one or two half-width characters to one full-width character */
3278 static int map_to_fullwidth( const WCHAR *src, int srclen, WCHAR *dst )
3280 INT n;
3282 if (*src <= '~' && *src > ' ' && *src != '\\')
3283 *dst = *src - 0x20 + 0xff00;
3284 else if (*src == ' ')
3285 *dst = 0x3000;
3286 else if (*src <= 0x00af && *src >= 0x00a2)
3288 static const BYTE misc_symbols_table[] =
3290 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3291 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3293 if (misc_symbols_table[*src - 0x00a2])
3294 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3295 else
3296 *dst = *src;
3298 else if (*src == 0x20a9) /* WON SIGN */
3299 *dst = 0xffe6;
3300 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3301 return n;
3302 else if (*src >= 0xffa0 && *src <= 0xffdc)
3304 static const BYTE hangul_mapping_table[] =
3306 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3307 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3308 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3309 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3310 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3311 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3312 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3313 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3316 if (hangul_mapping_table[*src - 0xffa0])
3317 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3318 else
3319 *dst = *src;
3321 else
3322 *dst = *src;
3324 return 1;
3327 /* decompose a full-width katakana character into one or two half-width characters. */
3328 static int decompose_katakana( WCHAR c, WCHAR *dst, int dstlen )
3330 static const BYTE katakana_map[] =
3332 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3333 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3334 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3335 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3336 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3337 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3338 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3339 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3340 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3341 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3342 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3343 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3344 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3346 int len = 0, shift = c - 0x3099;
3347 BYTE k;
3349 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
3351 k = katakana_map[shift];
3352 if (!k)
3354 if (dstlen > 0) *dst = c;
3355 len++;
3357 else if (k > 0x60)
3359 if (dstlen > 0) *dst = k | 0xff00;
3360 len++;
3362 else
3364 if (dstlen >= 2)
3366 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3367 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3369 len += 2;
3371 return len;
3374 /* map single full-width character to single or double half-width characters. */
3375 static int map_to_halfwidth( WCHAR c, WCHAR *dst, int dstlen )
3377 int n = decompose_katakana( c, dst, dstlen );
3378 if (n > 0) return n;
3380 if (c == 0x3000)
3381 *dst = ' ';
3382 else if (c == 0x3001)
3383 *dst = 0xff64;
3384 else if (c == 0x3002)
3385 *dst = 0xff61;
3386 else if (c == 0x300c || c == 0x300d)
3387 *dst = (c - 0x300c) + 0xff62;
3388 else if (c >= 0x3131 && c <= 0x3163)
3390 *dst = c - 0x3131 + 0xffa1;
3391 if (*dst >= 0xffbf) *dst += 3;
3392 if (*dst >= 0xffc8) *dst += 2;
3393 if (*dst >= 0xffd0) *dst += 2;
3394 if (*dst >= 0xffd8) *dst += 2;
3396 else if (c == 0x3164)
3397 *dst = 0xffa0;
3398 else if (c == 0x2019)
3399 *dst = '\'';
3400 else if (c == 0x201d)
3401 *dst = '"';
3402 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3403 *dst = c - 0xff00 + 0x20;
3404 else if (c >= 0xffe0 && c <= 0xffe6)
3406 static const WCHAR misc_symbol_map[] = { 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9 };
3407 *dst = misc_symbol_map[c - 0xffe0];
3409 else
3410 *dst = c;
3412 return 1;
3416 /* 32-bit collation element table format:
3417 * unicode weight - high 16 bit, diacritic weight - high 8 bit of low 16 bit,
3418 * case weight - high 4 bit of low 8 bit.
3421 enum weight { UNICODE_WEIGHT, DIACRITIC_WEIGHT, CASE_WEIGHT };
3423 static unsigned int get_weight( WCHAR ch, enum weight type )
3425 unsigned int ret;
3427 ret = collation_table[collation_table[collation_table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
3428 if (ret == ~0u) return ch;
3430 switch (type)
3432 case UNICODE_WEIGHT: return ret >> 16;
3433 case DIACRITIC_WEIGHT: return (ret >> 8) & 0xff;
3434 case CASE_WEIGHT: return (ret >> 4) & 0x0f;
3435 default: return 0;
3440 static void inc_str_pos( const WCHAR **str, int *len, unsigned int *dpos, unsigned int *dlen )
3442 (*dpos)++;
3443 if (*dpos == *dlen)
3445 *dpos = *dlen = 0;
3446 (*str)++;
3447 (*len)--;
3452 static int compare_weights(int flags, const WCHAR *str1, int len1,
3453 const WCHAR *str2, int len2, enum weight type )
3455 unsigned int ce1, ce2, dpos1 = 0, dpos2 = 0, dlen1 = 0, dlen2 = 0;
3456 const WCHAR *dstr1 = NULL, *dstr2 = NULL;
3458 while (len1 > 0 && len2 > 0)
3460 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3461 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3463 if (flags & NORM_IGNORESYMBOLS)
3465 int skip = 0;
3466 /* FIXME: not tested */
3467 if (get_char_type( CT_CTYPE1, dstr1[dpos1] ) & (C1_PUNCT | C1_SPACE))
3469 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3470 skip = 1;
3472 if (get_char_type( CT_CTYPE1, dstr2[dpos2] ) & (C1_PUNCT | C1_SPACE))
3474 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3475 skip = 1;
3477 if (skip) continue;
3480 /* hyphen and apostrophe are treated differently depending on
3481 * whether SORT_STRINGSORT specified or not
3483 if (type == UNICODE_WEIGHT && !(flags & SORT_STRINGSORT))
3485 if (dstr1[dpos1] == '-' || dstr1[dpos1] == '\'')
3487 if (dstr2[dpos2] != '-' && dstr2[dpos2] != '\'')
3489 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3490 continue;
3493 else if (dstr2[dpos2] == '-' || dstr2[dpos2] == '\'')
3495 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3496 continue;
3500 ce1 = get_weight( dstr1[dpos1], type );
3501 if (!ce1)
3503 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3504 continue;
3506 ce2 = get_weight( dstr2[dpos2], type );
3507 if (!ce2)
3509 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3510 continue;
3513 if (ce1 - ce2) return ce1 - ce2;
3515 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3516 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3518 while (len1)
3520 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3521 ce1 = get_weight( dstr1[dpos1], type );
3522 if (ce1) break;
3523 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3525 while (len2)
3527 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3528 ce2 = get_weight( dstr2[dpos2], type );
3529 if (ce2) break;
3530 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3532 return len1 - len2;
3536 static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare )
3538 static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
3539 int first, last, limit, dayinsecs;
3541 if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */
3542 if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */
3544 /* if year is 0 then date is in day-of-week format, otherwise
3545 * it's absolute date.
3547 if (!compare->wYear)
3549 /* wDay is interpreted as number of the week in the month
3550 * 5 means: the last week in the month */
3551 /* calculate the day of the first DayOfWeek in the month */
3552 first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1;
3553 /* check needed for the 5th weekday of the month */
3554 last = month_lengths[tf->Month - 1] +
3555 (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400))));
3556 limit = first + 7 * (compare->wDay - 1);
3557 if (limit > last) limit -= 7;
3559 else limit = compare->wDay;
3561 limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60;
3562 dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second;
3563 return dayinsecs - limit;
3567 static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local )
3569 int year;
3570 BOOL before_standard_date, after_daylight_date;
3571 LARGE_INTEGER t2;
3572 TIME_FIELDS tf;
3574 if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN;
3576 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
3577 if (info->StandardDate.wMonth == 0 ||
3578 (info->StandardDate.wYear == 0 &&
3579 (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 ||
3580 info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5)))
3582 SetLastError( ERROR_INVALID_PARAMETER );
3583 return TIME_ZONE_ID_INVALID;
3586 if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000;
3587 RtlTimeToTimeFields( &time, &tf );
3588 year = tf.Year;
3589 if (!is_local)
3591 t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000;
3592 RtlTimeToTimeFields( &t2, &tf );
3594 if (tf.Year == year)
3595 before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0;
3596 else
3597 before_standard_date = tf.Year < year;
3599 if (!is_local)
3601 t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000;
3602 RtlTimeToTimeFields( &t2, &tf );
3604 if (tf.Year == year)
3605 after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0;
3606 else
3607 after_daylight_date = tf.Year > year;
3609 if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */
3611 if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3613 else /* Down south */
3615 if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3617 return TIME_ZONE_ID_STANDARD;
3621 /* Note: the Internal_ functions are not documented. The number of parameters
3622 * should be correct, but their exact meaning may not.
3625 /******************************************************************************
3626 * Internal_EnumCalendarInfo (kernelbase.@)
3628 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc,
3629 const NLS_LOCALE_DATA *locale, CALID id,
3630 CALTYPE type, BOOL unicode, BOOL ex,
3631 BOOL exex, LPARAM lparam )
3633 const USHORT *calendars;
3634 USHORT cal = id;
3635 WCHAR buffer[256];
3636 INT ret, i, count = 1;
3638 if (!proc || !locale)
3640 SetLastError( ERROR_INVALID_PARAMETER );
3641 return FALSE;
3644 if (id == ENUM_ALL_CALENDARS)
3646 count = locale_strings[locale->scalendartype];
3647 calendars = locale_strings + locale->scalendartype + 1;
3649 else if (id <= CAL_UMALQURA)
3651 calendars = &cal;
3652 count = 1;
3654 else
3656 SetLastError( ERROR_INVALID_PARAMETER );
3657 return FALSE;
3660 for (i = 0; i < count; i++)
3662 id = calendars[i];
3663 if (type & CAL_RETURN_NUMBER)
3664 ret = get_calendar_info( locale, id, type, NULL, 0, (LPDWORD)buffer );
3665 else if (unicode)
3666 ret = get_calendar_info( locale, id, type, buffer, ARRAY_SIZE(buffer), NULL );
3667 else
3669 WCHAR bufW[256];
3670 ret = get_calendar_info( locale, id, type, bufW, ARRAY_SIZE(bufW), NULL );
3671 if (ret) WideCharToMultiByte( get_locale_codepage( locale, type ), 0,
3672 bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
3675 if (ret)
3677 if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
3678 else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
3679 else ret = proc( buffer );
3681 if (!ret) break;
3683 return TRUE;
3687 static BOOL call_enum_date_func( DATEFMT_ENUMPROCW proc, const NLS_LOCALE_DATA *locale, DWORD flags,
3688 DWORD str, WCHAR *buffer, CALID id, BOOL unicode,
3689 BOOL ex, BOOL exex, LPARAM lparam )
3691 char buffA[256];
3693 if (str) memcpy( buffer, locale_strings + str + 1, (locale_strings[str] + 1) * sizeof(WCHAR) );
3694 if (exex) return ((DATEFMT_ENUMPROCEXEX)proc)( buffer, id, lparam );
3695 if (ex) return ((DATEFMT_ENUMPROCEXW)proc)( buffer, id );
3696 if (unicode) return proc( buffer );
3697 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
3698 buffA, ARRAY_SIZE(buffA), NULL, NULL );
3699 return proc( (WCHAR *)buffA );
3703 /**************************************************************************
3704 * Internal_EnumDateFormats (kernelbase.@)
3706 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc,
3707 const NLS_LOCALE_DATA *locale, DWORD flags,
3708 BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
3710 WCHAR buffer[256];
3711 INT i, j, ret;
3712 DWORD pos;
3713 const struct calendar *cal;
3714 const USHORT *calendars = locale_strings + locale->scalendartype;
3715 const DWORD *array;
3717 if (!proc || !locale)
3719 SetLastError( ERROR_INVALID_PARAMETER );
3720 return FALSE;
3723 switch (flags & ~LOCALE_USE_CP_ACP)
3725 case 0:
3726 case DATE_SHORTDATE:
3727 if (!get_locale_info( locale, 0, LOCALE_SSHORTDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3728 pos = locale->sshortdate;
3729 break;
3730 case DATE_LONGDATE:
3731 if (!get_locale_info( locale, 0, LOCALE_SLONGDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3732 pos = locale->slongdate;
3733 break;
3734 case DATE_YEARMONTH:
3735 if (!get_locale_info( locale, 0, LOCALE_SYEARMONTH, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3736 pos = locale->syearmonth;
3737 break;
3738 default:
3739 SetLastError( ERROR_INVALID_PARAMETER );
3740 return FALSE;
3743 /* first the user override data */
3745 ret = call_enum_date_func( proc, locale, flags, 0, buffer, 1, unicode, ex, exex, lparam );
3747 /* then the remaining locale data */
3749 array = (const DWORD *)(locale_strings + pos + 1);
3750 for (i = 1; ret && i < locale_strings[pos]; i++)
3751 ret = call_enum_date_func( proc, locale, flags, array[i], buffer, 1, unicode, ex, exex, lparam );
3753 /* then the extra calendars */
3755 for (i = 0; ret && i < calendars[0]; i++)
3757 if (calendars[i + 1] == 1) continue;
3758 if (!(cal = get_calendar_data( locale, calendars[i + 1] ))) continue;
3759 switch (flags & ~LOCALE_USE_CP_ACP)
3761 case 0:
3762 case DATE_SHORTDATE:
3763 pos = cal->sshortdate;
3764 break;
3765 case DATE_LONGDATE:
3766 pos = cal->slongdate;
3767 break;
3768 case DATE_YEARMONTH:
3769 pos = cal->syearmonth;
3770 break;
3772 array = (const DWORD *)(locale_strings + pos + 1);
3773 for (j = 0; ret && j < locale_strings[pos]; j++)
3774 ret = call_enum_date_func( proc, locale, flags, array[j], buffer,
3775 calendars[i + 1], unicode, ex, exex, lparam );
3777 return TRUE;
3781 /******************************************************************************
3782 * Internal_EnumLanguageGroupLocales (kernelbase.@)
3784 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
3785 DWORD flags, LONG_PTR param, BOOL unicode )
3787 WCHAR name[10], value[10];
3788 DWORD name_len, value_len, type, index = 0, alt = 0;
3789 HKEY key, altkey;
3790 LCID lcid;
3792 if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
3794 SetLastError( ERROR_INVALID_PARAMETER );
3795 return FALSE;
3798 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
3799 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
3801 for (;;)
3803 name_len = ARRAY_SIZE(name);
3804 value_len = sizeof(value);
3805 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
3806 &type, (BYTE *)value, &value_len ))
3808 if (alt++) break;
3809 index = 0;
3810 continue;
3812 if (type != REG_SZ) continue;
3813 if (id != wcstoul( value, NULL, 16 )) continue;
3814 lcid = wcstoul( name, NULL, 16 );
3815 if (!unicode)
3817 char nameA[10];
3818 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3819 if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
3821 else if (!proc( id, lcid, name, param )) break;
3823 RegCloseKey( altkey );
3824 RegCloseKey( key );
3825 return TRUE;
3829 /***********************************************************************
3830 * Internal_EnumSystemCodePages (kernelbase.@)
3832 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
3833 BOOL unicode )
3835 WCHAR name[10];
3836 DWORD name_len, type, index = 0;
3837 HKEY key;
3839 if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
3841 for (;;)
3843 name_len = ARRAY_SIZE(name);
3844 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
3845 if (type != REG_SZ) continue;
3846 if (!wcstoul( name, NULL, 10 )) continue;
3847 if (!unicode)
3849 char nameA[10];
3850 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3851 if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
3853 else if (!proc( name )) break;
3855 RegCloseKey( key );
3856 return TRUE;
3860 /******************************************************************************
3861 * Internal_EnumSystemLanguageGroups (kernelbase.@)
3863 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
3864 DWORD flags, LONG_PTR param, BOOL unicode )
3866 WCHAR name[10], value[10], descr[80];
3867 DWORD name_len, value_len, type, index = 0;
3868 HKEY key;
3869 LGRPID id;
3871 if (!proc)
3873 SetLastError( ERROR_INVALID_PARAMETER );
3874 return FALSE;
3877 switch (flags)
3879 case 0:
3880 flags = LGRPID_INSTALLED;
3881 break;
3882 case LGRPID_INSTALLED:
3883 case LGRPID_SUPPORTED:
3884 break;
3885 default:
3886 SetLastError( ERROR_INVALID_FLAGS );
3887 return FALSE;
3890 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
3892 for (;;)
3894 name_len = ARRAY_SIZE(name);
3895 value_len = sizeof(value);
3896 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
3897 if (type != REG_SZ) continue;
3899 id = wcstoul( name, NULL, 16 );
3901 if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
3902 if (!LoadStringW( kernelbase_handle, id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
3903 TRACE( "%p: %lu %s %s %lx %Ix\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
3904 if (!unicode)
3906 char nameA[10], descrA[80];
3907 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3908 WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
3909 if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
3911 else if (!proc( id, name, descr, flags, param )) break;
3913 RegCloseKey( key );
3914 return TRUE;
3918 /**************************************************************************
3919 * Internal_EnumTimeFormats (kernelbase.@)
3921 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc,
3922 const NLS_LOCALE_DATA *locale, DWORD flags,
3923 BOOL unicode, BOOL ex, LPARAM lparam )
3925 WCHAR buffer[256];
3926 INT ret = TRUE;
3927 const DWORD *array;
3928 DWORD pos, i;
3930 if (!proc || !locale)
3932 SetLastError( ERROR_INVALID_PARAMETER );
3933 return FALSE;
3935 switch (flags & ~LOCALE_USE_CP_ACP)
3937 case 0:
3938 if (!get_locale_info( locale, 0, LOCALE_STIMEFORMAT, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3939 pos = locale->stimeformat;
3940 break;
3941 case TIME_NOSECONDS:
3942 if (!get_locale_info( locale, 0, LOCALE_SSHORTTIME, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3943 pos = locale->sshorttime;
3944 break;
3945 default:
3946 FIXME( "Unknown time format %lx\n", flags );
3947 SetLastError( ERROR_INVALID_PARAMETER );
3948 return FALSE;
3951 array = (const DWORD *)(locale_strings + pos + 1);
3952 for (i = 0; ret && i < locale_strings[pos]; i++)
3954 if (i) memcpy( buffer, locale_strings + array[i] + 1,
3955 (locale_strings[array[i]] + 1) * sizeof(WCHAR) );
3957 if (ex) ret = ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
3958 else if (unicode) ret = proc( buffer );
3959 else
3961 char buffA[256];
3962 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
3963 buffA, ARRAY_SIZE(buffA), NULL, NULL );
3964 ret = proc( (WCHAR *)buffA );
3967 return TRUE;
3971 /******************************************************************************
3972 * Internal_EnumUILanguages (kernelbase.@)
3974 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
3975 LONG_PTR param, BOOL unicode )
3977 WCHAR nameW[LOCALE_NAME_MAX_LENGTH];
3978 char nameA[LOCALE_NAME_MAX_LENGTH];
3979 DWORD i;
3981 if (!proc)
3983 SetLastError( ERROR_INVALID_PARAMETER );
3984 return FALSE;
3986 if (flags & ~(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))
3988 SetLastError( ERROR_INVALID_FLAGS );
3989 return FALSE;
3992 for (i = 0; i < locale_table->nb_lcnames; i++)
3994 if (!lcnames_index[i].name) continue; /* skip invariant locale */
3995 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
3996 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
3997 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
3998 continue; /* skip alternate sorts */
3999 if (flags & MUI_LANGUAGE_NAME)
4001 const WCHAR *str = locale_strings + lcnames_index[i].name;
4003 if (unicode)
4005 memcpy( nameW, str + 1, (*str + 1) * sizeof(WCHAR) );
4006 if (!proc( nameW, param )) break;
4008 else
4010 WideCharToMultiByte( CP_ACP, 0, str + 1, -1, nameA, sizeof(nameA), NULL, NULL );
4011 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
4014 else
4016 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4017 if (unicode)
4019 swprintf( nameW, ARRAY_SIZE(nameW), L"%04lx", lcnames_index[i].id );
4020 if (!proc( nameW, param )) break;
4022 else
4024 sprintf( nameA, "%04x", lcnames_index[i].id );
4025 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
4029 return TRUE;
4033 /******************************************************************************
4034 * CompareStringEx (kernelbase.@)
4036 INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1,
4037 const WCHAR *str2, int len2, NLSVERSIONINFO *version,
4038 void *reserved, LPARAM handle )
4040 DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | SORT_STRINGSORT |
4041 NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH | LOCALE_USE_CP_ACP;
4042 DWORD semistub_flags = NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE | LINGUISTIC_IGNOREDIACRITIC |
4043 SORT_DIGITSASNUMBERS | 0x10000000;
4044 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
4045 INT ret;
4046 static int once;
4048 if (version) FIXME( "unexpected version parameter\n" );
4049 if (reserved) FIXME( "unexpected reserved value\n" );
4050 if (handle) FIXME( "unexpected handle\n" );
4052 if (!str1 || !str2)
4054 SetLastError( ERROR_INVALID_PARAMETER );
4055 return 0;
4058 if (flags & ~(supported_flags | semistub_flags))
4060 SetLastError( ERROR_INVALID_FLAGS );
4061 return 0;
4064 if (flags & semistub_flags)
4066 if (!once++) FIXME( "semi-stub behavior for flag(s) 0x%lx\n", flags & semistub_flags );
4069 if (len1 < 0) len1 = lstrlenW(str1);
4070 if (len2 < 0) len2 = lstrlenW(str2);
4072 ret = compare_weights( flags, str1, len1, str2, len2, UNICODE_WEIGHT );
4073 if (!ret)
4075 if (!(flags & NORM_IGNORENONSPACE))
4076 ret = compare_weights( flags, str1, len1, str2, len2, DIACRITIC_WEIGHT );
4077 if (!ret && !(flags & NORM_IGNORECASE))
4078 ret = compare_weights( flags, str1, len1, str2, len2, CASE_WEIGHT );
4080 if (!ret) return CSTR_EQUAL;
4081 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
4085 /******************************************************************************
4086 * CompareStringA (kernelbase.@)
4088 INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1,
4089 const char *str2, int len2 )
4091 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
4092 WCHAR *buf2W = buf1W + 130;
4093 LPWSTR str1W, str2W;
4094 INT len1W = 0, len2W = 0, ret;
4095 UINT locale_cp = CP_ACP;
4097 if (!str1 || !str2)
4099 SetLastError( ERROR_INVALID_PARAMETER );
4100 return 0;
4103 if (flags & SORT_DIGITSASNUMBERS)
4105 SetLastError( ERROR_INVALID_FLAGS );
4106 return 0;
4109 if (len1 < 0) len1 = strlen(str1);
4110 if (len2 < 0) len2 = strlen(str2);
4112 locale_cp = get_lcid_codepage( lcid, flags );
4113 if (len1)
4115 if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 );
4116 if (len1W) str1W = buf1W;
4117 else
4119 len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 );
4120 str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) );
4121 if (!str1W)
4123 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4124 return 0;
4126 MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W );
4129 else
4131 len1W = 0;
4132 str1W = buf1W;
4135 if (len2)
4137 if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 );
4138 if (len2W) str2W = buf2W;
4139 else
4141 len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 );
4142 str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) );
4143 if (!str2W)
4145 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
4146 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4147 return 0;
4149 MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W );
4152 else
4154 len2W = 0;
4155 str2W = buf2W;
4158 ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W );
4160 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
4161 if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W );
4162 return ret;
4166 /******************************************************************************
4167 * CompareStringW (kernelbase.@)
4169 INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1,
4170 const WCHAR *str2, int len2 )
4172 return CompareStringEx( NULL, flags, str1, len1, str2, len2, NULL, NULL, 0 );
4176 /******************************************************************************
4177 * CompareStringOrdinal (kernelbase.@)
4179 INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
4180 const WCHAR *str2, INT len2, BOOL ignore_case )
4182 int ret;
4184 if (!str1 || !str2)
4186 SetLastError( ERROR_INVALID_PARAMETER );
4187 return 0;
4189 if (len1 < 0) len1 = lstrlenW( str1 );
4190 if (len2 < 0) len2 = lstrlenW( str2 );
4192 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4193 if (ret < 0) return CSTR_LESS_THAN;
4194 if (ret > 0) return CSTR_GREATER_THAN;
4195 return CSTR_EQUAL;
4199 /******************************************************************************
4200 * ConvertDefaultLocale (kernelbase.@)
4202 LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
4204 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
4205 if (locale) lcid = locale->ilanguage;
4206 return lcid;
4210 /******************************************************************************
4211 * EnumCalendarInfoW (kernelbase.@)
4213 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
4214 CALID id, CALTYPE type )
4216 return Internal_EnumCalendarInfo( proc, NlsValidateLocale( &lcid, 0 ),
4217 id, type, TRUE, FALSE, FALSE, 0 );
4221 /******************************************************************************
4222 * EnumCalendarInfoExW (kernelbase.@)
4224 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
4225 CALID id, CALTYPE type )
4227 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4228 id, type, TRUE, TRUE, FALSE, 0 );
4231 /******************************************************************************
4232 * EnumCalendarInfoExEx (kernelbase.@)
4234 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
4235 LPCWSTR reserved, CALTYPE type, LPARAM lparam )
4237 LCID lcid;
4238 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4239 id, type, TRUE, TRUE, TRUE, lparam );
4243 /**************************************************************************
4244 * EnumDateFormatsW (kernelbase.@)
4246 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4248 return Internal_EnumDateFormats( proc, NlsValidateLocale( &lcid, 0 ),
4249 flags, TRUE, FALSE, FALSE, 0 );
4253 /**************************************************************************
4254 * EnumDateFormatsExW (kernelbase.@)
4256 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
4258 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4259 flags, TRUE, TRUE, FALSE, 0 );
4263 /**************************************************************************
4264 * EnumDateFormatsExEx (kernelbase.@)
4266 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
4267 DWORD flags, LPARAM lparam )
4269 LCID lcid;
4270 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4271 flags, TRUE, TRUE, TRUE, lparam );
4276 /******************************************************************************
4277 * EnumDynamicTimeZoneInformation (kernelbase.@)
4279 DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index,
4280 DYNAMIC_TIME_ZONE_INFORMATION *info )
4282 DYNAMIC_TIME_ZONE_INFORMATION tz;
4283 LSTATUS ret;
4284 DWORD size;
4286 if (!info) return ERROR_INVALID_PARAMETER;
4288 size = ARRAY_SIZE(tz.TimeZoneKeyName);
4289 ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL );
4290 if (ret) return ret;
4292 tz.DynamicDaylightTimeDisabled = TRUE;
4293 if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError();
4295 lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName );
4296 info->DynamicDaylightTimeDisabled = FALSE;
4297 return 0;
4301 /******************************************************************************
4302 * EnumLanguageGroupLocalesW (kernelbase.@)
4304 BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
4305 DWORD flags, LONG_PTR param )
4307 return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
4311 /******************************************************************************
4312 * EnumUILanguagesW (kernelbase.@)
4314 BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
4316 return Internal_EnumUILanguages( proc, flags, param, TRUE );
4320 /***********************************************************************
4321 * EnumSystemCodePagesW (kernelbase.@)
4323 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
4325 return Internal_EnumSystemCodePages( proc, flags, TRUE );
4329 /******************************************************************************
4330 * EnumSystemGeoID (kernelbase.@)
4332 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc )
4334 INT i;
4336 TRACE( "(%ld, %ld, %p)\n", class, parent, proc );
4338 if (!proc)
4340 SetLastError( ERROR_INVALID_PARAMETER );
4341 return FALSE;
4343 if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL)
4345 SetLastError( ERROR_INVALID_FLAGS );
4346 return FALSE;
4349 for (i = 0; i < geo_ids_count; i++)
4351 if (class != GEOCLASS_ALL && geo_ids[i].class != class) continue;
4352 if (parent && geo_ids[i].parent != parent) continue;
4353 if (!proc( geo_ids[i].id )) break;
4355 return TRUE;
4359 /******************************************************************************
4360 * EnumSystemLanguageGroupsW (kernelbase.@)
4362 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
4363 DWORD flags, LONG_PTR param )
4365 return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
4369 /******************************************************************************
4370 * EnumSystemLocalesA (kernelbase.@)
4372 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
4374 char name[10];
4375 DWORD i;
4377 for (i = 0; i < locale_table->nb_lcnames; i++)
4379 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4380 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4381 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4382 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4383 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
4384 continue; /* skip alternate sorts */
4385 sprintf( name, "%08x", lcnames_index[i].id );
4386 if (!proc( name )) break;
4388 return TRUE;
4392 /******************************************************************************
4393 * EnumSystemLocalesW (kernelbase.@)
4395 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
4397 WCHAR name[10];
4398 DWORD i;
4400 for (i = 0; i < locale_table->nb_lcnames; i++)
4402 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4403 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4404 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4405 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4406 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
4407 continue; /* skip alternate sorts */
4408 swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id );
4409 if (!proc( name )) break;
4411 return TRUE;
4415 /******************************************************************************
4416 * EnumSystemLocalesEx (kernelbase.@)
4418 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
4419 LPARAM param, void *reserved )
4421 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
4422 DWORD i, flags;
4424 if (reserved)
4426 SetLastError( ERROR_INVALID_PARAMETER );
4427 return FALSE;
4430 for (i = 0; i < locale_table->nb_lcnames; i++)
4432 const NLS_LOCALE_DATA *locale = get_locale_data( lcnames_index[i].idx );
4433 const WCHAR *str = locale_strings + lcnames_index[i].name;
4435 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4436 memcpy( buffer, str + 1, (*str + 1) * sizeof(WCHAR) );
4437 if (SORTIDFROMLCID( lcnames_index[i].id ) || wcschr( str + 1, '_' ))
4438 flags = LOCALE_ALTERNATE_SORTS;
4439 else
4440 flags = LOCALE_WINDOWS | (locale->inotneutral ? LOCALE_SPECIFICDATA : LOCALE_NEUTRALDATA);
4441 if (wanted_flags && !(flags & wanted_flags)) continue;
4442 if (!proc( buffer, flags, param )) break;
4444 return TRUE;
4448 /**************************************************************************
4449 * EnumTimeFormatsW (kernelbase.@)
4451 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4453 return Internal_EnumTimeFormats( proc, NlsValidateLocale( &lcid, 0 ), flags, TRUE, FALSE, 0 );
4457 /**************************************************************************
4458 * EnumTimeFormatsEx (kernelbase.@)
4460 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
4461 DWORD flags, LPARAM lparam )
4463 LCID lcid;
4464 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4465 flags, TRUE, TRUE, lparam );
4469 /**************************************************************************
4470 * FindNLSString (kernelbase.@)
4472 INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src,
4473 int srclen, const WCHAR *value, int valuelen, int *found )
4475 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
4477 LCIDToLocaleName( lcid, locale, ARRAY_SIZE(locale), 0 );
4478 return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 );
4482 /**************************************************************************
4483 * FindNLSStringEx (kernelbase.@)
4485 INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src,
4486 int srclen, const WCHAR *value, int valuelen, int *found,
4487 NLSVERSIONINFO *version, void *reserved, LPARAM handle )
4489 /* FIXME: this function should normalize strings before calling CompareStringEx() */
4490 DWORD mask = flags;
4491 int offset, inc, count;
4493 TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale), flags,
4494 wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found,
4495 version, reserved, handle );
4497 if (version || reserved || handle || !IsValidLocaleName(locale) ||
4498 !src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1)
4500 SetLastError( ERROR_INVALID_PARAMETER );
4501 return -1;
4503 if (srclen == -1) srclen = lstrlenW(src);
4504 if (valuelen == -1) valuelen = lstrlenW(value);
4506 srclen -= valuelen;
4507 if (srclen < 0) return -1;
4509 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
4510 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? srclen + 1 : 1;
4511 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : srclen;
4512 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4513 while (count--)
4515 if (CompareStringEx( locale, mask, src + offset, valuelen,
4516 value, valuelen, NULL, NULL, 0 ) == CSTR_EQUAL)
4518 if (found) *found = valuelen;
4519 return offset;
4521 offset += inc;
4523 return -1;
4527 /******************************************************************************
4528 * FindStringOrdinal (kernelbase.@)
4530 INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
4531 const WCHAR *val, INT val_size, BOOL ignore_case )
4533 INT offset, inc, count;
4535 TRACE( "%#lx %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
4536 wine_dbgstr_w(val), val_size, ignore_case );
4538 if (!src || !val)
4540 SetLastError( ERROR_INVALID_PARAMETER );
4541 return -1;
4544 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
4546 SetLastError( ERROR_INVALID_FLAGS );
4547 return -1;
4550 if (src_size == -1) src_size = lstrlenW( src );
4551 if (val_size == -1) val_size = lstrlenW( val );
4553 SetLastError( ERROR_SUCCESS );
4554 src_size -= val_size;
4555 if (src_size < 0) return -1;
4557 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
4558 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
4559 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4560 while (count--)
4562 if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
4563 return offset;
4564 offset += inc;
4566 return -1;
4570 /******************************************************************************
4571 * FoldStringW (kernelbase.@)
4573 INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
4575 NTSTATUS status;
4576 WCHAR *buf = dst;
4577 int len = dstlen;
4579 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
4581 SetLastError( ERROR_INVALID_PARAMETER );
4582 return 0;
4584 if (srclen == -1) srclen = lstrlenW(src) + 1;
4586 if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE)))
4588 len = srclen * 4;
4589 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4591 SetLastError( ERROR_OUTOFMEMORY );
4592 return 0;
4596 for (;;)
4598 status = fold_string( flags, src, srclen, buf, &len );
4599 if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf );
4600 if (status != STATUS_BUFFER_TOO_SMALL) break;
4601 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4603 SetLastError( ERROR_OUTOFMEMORY );
4604 return 0;
4607 if (status == STATUS_INVALID_PARAMETER_1)
4609 SetLastError( ERROR_INVALID_FLAGS );
4610 return 0;
4612 if (!set_ntstatus( status )) return 0;
4614 if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
4615 return len;
4619 static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang,
4620 BOOL ansi, WCHAR **buffer )
4622 DWORD len;
4624 if (!(flags & FORMAT_MESSAGE_FROM_STRING))
4626 const MESSAGE_RESOURCE_ENTRY *entry;
4627 NTSTATUS status = STATUS_INVALID_PARAMETER;
4629 if (flags & FORMAT_MESSAGE_FROM_HMODULE)
4631 HMODULE module = (HMODULE)src;
4632 if (!module) module = GetModuleHandleW( 0 );
4633 status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry );
4635 if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
4637 /* Fold win32 hresult to its embedded error code. */
4638 if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32)
4639 id = HRESULT_CODE( id );
4640 status = RtlFindMessage( kernelbase_handle, RT_MESSAGETABLE, lang, id, &entry );
4642 if (!set_ntstatus( status )) return NULL;
4644 src = entry->Text;
4645 ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE);
4648 if (!ansi) return src;
4649 len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
4650 if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
4651 MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len );
4652 return *buffer;
4656 /***********************************************************************
4657 * FormatMessageA (kernelbase.@)
4659 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4660 char *buffer, DWORD size, va_list *args )
4662 DWORD ret = 0;
4663 ULONG len, retsize = 0;
4664 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4665 const WCHAR *src;
4666 WCHAR *result, *message = NULL;
4667 NTSTATUS status;
4669 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4671 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4673 if (!buffer)
4675 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4676 return 0;
4678 *(char **)buffer = NULL;
4680 if (size >= 32768)
4682 SetLastError( ERROR_INVALID_PARAMETER );
4683 return 0;
4686 if (width == 0xff) width = ~0u;
4688 if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0;
4690 if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 )))
4691 status = STATUS_NO_MEMORY;
4692 else
4693 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4694 TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4695 result, 65536, &retsize );
4697 HeapFree( GetProcessHeap(), 0, message );
4699 if (status == STATUS_BUFFER_OVERFLOW)
4701 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4702 goto done;
4704 if (!set_ntstatus( status )) goto done;
4706 len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL );
4707 if (len <= 1)
4709 SetLastError( ERROR_NO_WORK_DONE );
4710 goto done;
4713 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4715 char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len ));
4716 if (!buf)
4718 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4719 goto done;
4721 *(char **)buffer = buf;
4722 WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL );
4724 else if (len > size)
4726 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4727 goto done;
4729 else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL );
4731 ret = len - 1;
4733 done:
4734 HeapFree( GetProcessHeap(), 0, result );
4735 return ret;
4739 /***********************************************************************
4740 * FormatMessageW (kernelbase.@)
4742 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4743 WCHAR *buffer, DWORD size, va_list *args )
4745 ULONG retsize = 0;
4746 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4747 const WCHAR *src;
4748 WCHAR *message = NULL;
4749 NTSTATUS status;
4751 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4753 if (!buffer)
4755 SetLastError( ERROR_INVALID_PARAMETER );
4756 return 0;
4759 if (width == 0xff) width = ~0u;
4761 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL;
4763 if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0;
4765 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4767 WCHAR *result;
4768 ULONG alloc = max( size * sizeof(WCHAR), 65536 );
4770 for (;;)
4772 if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc )))
4774 status = STATUS_NO_MEMORY;
4775 break;
4777 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4778 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4779 result, alloc, &retsize );
4780 if (!status)
4782 if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );
4783 else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
4784 result, max( retsize, size * sizeof(WCHAR) ));
4785 break;
4787 HeapFree( GetProcessHeap(), 0, result );
4788 if (status != STATUS_BUFFER_OVERFLOW) break;
4789 alloc *= 2;
4792 else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4793 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4794 buffer, size * sizeof(WCHAR), &retsize );
4796 HeapFree( GetProcessHeap(), 0, message );
4798 if (status == STATUS_BUFFER_OVERFLOW)
4800 if (size) buffer[size - 1] = 0;
4801 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4802 return 0;
4804 if (!set_ntstatus( status )) return 0;
4805 if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE );
4806 return retsize / sizeof(WCHAR) - 1;
4810 /******************************************************************************
4811 * GetACP (kernelbase.@)
4813 UINT WINAPI GetACP(void)
4815 return nls_info.AnsiTableInfo.CodePage;
4819 /***********************************************************************
4820 * GetCPInfo (kernelbase.@)
4822 BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
4824 const CPTABLEINFO *table;
4826 if (!cpinfo || !(table = get_codepage_table( codepage )))
4828 SetLastError( ERROR_INVALID_PARAMETER );
4829 return FALSE;
4831 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4832 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4833 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4834 return TRUE;
4838 /***********************************************************************
4839 * GetCPInfoExW (kernelbase.@)
4841 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
4843 const CPTABLEINFO *table;
4844 int min, max, pos;
4846 if (!cpinfo || !(table = get_codepage_table( codepage )))
4848 SetLastError( ERROR_INVALID_PARAMETER );
4849 return FALSE;
4851 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4852 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4853 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4854 cpinfo->CodePage = table->CodePage;
4855 cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
4857 min = 0;
4858 max = ARRAY_SIZE(codepage_names) - 1;
4859 cpinfo->CodePageName[0] = 0;
4860 while (min <= max)
4862 pos = (min + max) / 2;
4863 if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
4864 else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
4865 else
4867 wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
4868 break;
4871 return TRUE;
4875 /***********************************************************************
4876 * GetCalendarInfoW (kernelbase.@)
4878 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
4879 WCHAR *buffer, INT len, DWORD *value )
4881 const NLS_LOCALE_DATA *locale;
4883 TRACE( "%04lx %lu 0x%lx %p %d %p\n", lcid, calendar, type, buffer, len, value );
4885 if (!(locale = NlsValidateLocale( &lcid, 0 )))
4887 SetLastError( ERROR_INVALID_PARAMETER );
4888 return 0;
4890 return get_calendar_info( locale, calendar, type, buffer, len, value );
4894 /***********************************************************************
4895 * GetCalendarInfoEx (kernelbase.@)
4897 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *name, CALID calendar, const WCHAR *reserved,
4898 CALTYPE type, WCHAR *buffer, INT len, DWORD *value )
4900 LCID lcid;
4901 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
4903 TRACE( "%s %lu 0x%lx %p %d %p\n", debugstr_w(name), calendar, type, buffer, len, value );
4905 if (!locale)
4907 SetLastError( ERROR_INVALID_PARAMETER );
4908 return 0;
4910 return get_calendar_info( locale, calendar, type, buffer, len, value );
4914 static CRITICAL_SECTION tzname_section;
4915 static CRITICAL_SECTION_DEBUG tzname_section_debug =
4917 0, 0, &tzname_section,
4918 { &tzname_section_debug.ProcessLocksList, &tzname_section_debug.ProcessLocksList },
4919 0, 0, { (DWORD_PTR)(__FILE__ ": tzname_section") }
4921 static CRITICAL_SECTION tzname_section = { &tzname_section_debug, -1, 0, 0, 0, 0 };
4922 static struct {
4923 LCID lcid;
4924 WCHAR key_name[128];
4925 WCHAR standard_name[32];
4926 WCHAR daylight_name[32];
4927 } cached_tzname;
4929 /***********************************************************************
4930 * GetDynamicTimeZoneInformation (kernelbase.@)
4932 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info )
4934 HKEY key;
4935 LARGE_INTEGER now;
4937 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info )))
4938 return TIME_ZONE_ID_INVALID;
4940 RtlEnterCriticalSection( &tzname_section );
4941 if (cached_tzname.lcid == GetThreadLocale() &&
4942 !wcscmp( info->TimeZoneKeyName, cached_tzname.key_name ))
4944 wcscpy( info->StandardName, cached_tzname.standard_name );
4945 wcscpy( info->DaylightName, cached_tzname.daylight_name );
4946 RtlLeaveCriticalSection( &tzname_section );
4948 else
4950 RtlLeaveCriticalSection( &tzname_section );
4951 if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))
4953 RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
4954 sizeof(info->StandardName), NULL, 0, system_dir );
4955 RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
4956 sizeof(info->DaylightName), NULL, 0, system_dir );
4957 RegCloseKey( key );
4959 else return TIME_ZONE_ID_INVALID;
4961 RtlEnterCriticalSection( &tzname_section );
4962 cached_tzname.lcid = GetThreadLocale();
4963 wcscpy( cached_tzname.key_name, info->TimeZoneKeyName );
4964 wcscpy( cached_tzname.standard_name, info->StandardName );
4965 wcscpy( cached_tzname.daylight_name, info->DaylightName );
4966 RtlLeaveCriticalSection( &tzname_section );
4969 NtQuerySystemTime( &now );
4970 return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE );
4974 /******************************************************************************
4975 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
4977 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info,
4978 DWORD *first, DWORD *last )
4980 HKEY key, dst_key = 0;
4981 DWORD type, count, ret = ERROR_FILE_NOT_FOUND;
4983 if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret;
4985 if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done;
4986 count = sizeof(DWORD);
4987 if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done;
4988 if (type != REG_DWORD) goto done;
4989 count = sizeof(DWORD);
4990 if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done;
4991 if (type != REG_DWORD) goto done;
4992 ret = 0;
4994 done:
4995 RegCloseKey( dst_key );
4996 RegCloseKey( key );
4997 return ret;
5001 /******************************************************************************
5002 * GetFileMUIInfo (kernelbase.@)
5004 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIInfo( DWORD flags, const WCHAR *path,
5005 FILEMUIINFO *info, DWORD *size )
5007 FIXME( "stub: %lu, %s, %p, %p\n", flags, debugstr_w(path), info, size );
5008 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
5009 return FALSE;
5013 /******************************************************************************
5014 * GetFileMUIPath (kernelbase.@)
5016 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags, const WCHAR *filepath,
5017 WCHAR *language, ULONG *languagelen,
5018 WCHAR *muipath, ULONG *muipathlen,
5019 ULONGLONG *enumerator )
5021 FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5022 debugstr_w(language), languagelen, muipath, muipathlen, enumerator );
5023 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
5024 return FALSE;
5028 /******************************************************************************
5029 * GetGeoInfoW (kernelbase.@)
5031 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang )
5033 const struct geo_id *ptr = find_geo_id_entry( id );
5035 TRACE( "%ld %ld %p %d %d\n", id, type, data, count, lang );
5037 if (!ptr)
5039 SetLastError( ERROR_INVALID_PARAMETER );
5040 return 0;
5042 return get_geo_info( ptr, type, data, count, lang );
5046 /******************************************************************************
5047 * GetLocaleInfoA (kernelbase.@)
5049 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
5051 const NLS_LOCALE_DATA *locale;
5052 WCHAR *bufferW;
5053 INT lenW, ret;
5055 TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid, lctype, buffer, len );
5057 if (len < 0 || (len && !buffer))
5059 SetLastError( ERROR_INVALID_PARAMETER );
5060 return 0;
5062 if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
5064 SetLastError( ERROR_INVALID_FLAGS );
5065 return 0;
5067 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5069 SetLastError( ERROR_INVALID_PARAMETER );
5070 return 0;
5072 if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
5074 ret = get_locale_info( locale, lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) );
5075 return ret * sizeof(WCHAR);
5078 if (!(lenW = get_locale_info( locale, lcid, lctype, NULL, 0 ))) return 0;
5080 if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
5082 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5083 return 0;
5085 ret = get_locale_info( locale, lcid, lctype, bufferW, lenW );
5086 if (ret) ret = WideCharToMultiByte( get_locale_codepage( locale, lctype ), 0,
5087 bufferW, ret, buffer, len, NULL, NULL );
5088 RtlFreeHeap( GetProcessHeap(), 0, bufferW );
5089 return ret;
5093 /******************************************************************************
5094 * GetLocaleInfoW (kernelbase.@)
5096 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
5098 const NLS_LOCALE_DATA *locale;
5100 if (len < 0 || (len && !buffer))
5102 SetLastError( ERROR_INVALID_PARAMETER );
5103 return 0;
5106 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
5108 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5110 SetLastError( ERROR_INVALID_PARAMETER );
5111 return 0;
5113 return get_locale_info( locale, lcid, lctype, buffer, len );
5117 /******************************************************************************
5118 * GetLocaleInfoEx (kernelbase.@)
5120 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *name, LCTYPE info, WCHAR *buffer, INT len )
5122 LCID lcid;
5123 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5125 TRACE( "%s 0x%lx %p %d\n", debugstr_w(name), info, buffer, len );
5127 if (!locale)
5129 SetLastError( ERROR_INVALID_PARAMETER );
5130 return 0;
5132 return get_locale_info( locale, lcid, info, buffer, len );
5136 /******************************************************************************
5137 * GetNLSVersion (kernelbase.@)
5139 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info )
5141 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
5143 if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId ))
5145 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5146 return FALSE;
5148 if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES ))
5150 SetLastError( ERROR_INVALID_PARAMETER );
5151 return FALSE;
5153 return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info );
5157 /******************************************************************************
5158 * GetNLSVersionEx (kernelbase.@)
5160 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale,
5161 NLSVERSIONINFOEX *info )
5163 LCID lcid = 0;
5165 if (func != COMPARE_STRING)
5167 SetLastError( ERROR_INVALID_FLAGS );
5168 return FALSE;
5170 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5171 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5173 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5174 return FALSE;
5177 if (!(lcid = LocaleNameToLCID( locale, 0 ))) return FALSE;
5179 info->dwNLSVersion = info->dwDefinedVersion = sort.version;
5180 if (info->dwNLSVersionInfoSize >= sizeof(*info))
5182 const struct sortguid *sortid = get_language_sort( locale );
5183 info->dwEffectiveId = lcid;
5184 info->guidCustomVersion = sortid ? sortid->id : default_sort_guid;
5186 return TRUE;
5190 /******************************************************************************
5191 * GetOEMCP (kernelbase.@)
5193 UINT WINAPI GetOEMCP(void)
5195 return nls_info.OemTableInfo.CodePage;
5199 /***********************************************************************
5200 * GetProcessPreferredUILanguages (kernelbase.@)
5202 BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPreferredUILanguages( DWORD flags, ULONG *count,
5203 WCHAR *buffer, ULONG *size )
5205 return set_ntstatus( RtlGetProcessPreferredUILanguages( flags, count, buffer, size ));
5209 /***********************************************************************
5210 * GetStringTypeA (kernelbase.@)
5212 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
5213 WORD *chartype )
5215 UINT cp;
5216 INT countW;
5217 LPWSTR srcW;
5218 BOOL ret = FALSE;
5220 if (count == -1) count = strlen(src) + 1;
5222 cp = get_lcid_codepage( locale, 0 );
5223 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
5224 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5226 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
5228 * NOTE: the target buffer has 1 word for each CHARACTER in the source
5229 * string, with multibyte characters there maybe be more bytes in count
5230 * than character space in the buffer!
5232 ret = GetStringTypeW(type, srcW, countW, chartype);
5233 HeapFree(GetProcessHeap(), 0, srcW);
5235 return ret;
5239 /***********************************************************************
5240 * GetStringTypeW (kernelbase.@)
5242 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
5244 if (!src)
5246 SetLastError( ERROR_INVALID_PARAMETER );
5247 return FALSE;
5249 if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3)
5251 SetLastError( ERROR_INVALID_PARAMETER );
5252 return FALSE;
5255 if (count == -1) count = lstrlenW(src) + 1;
5257 while (count--) *chartype++ = get_char_type( type, *src++ );
5259 return TRUE;
5263 /***********************************************************************
5264 * GetStringTypeExW (kernelbase.@)
5266 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
5267 WORD *chartype )
5269 /* locale is ignored for Unicode */
5270 return GetStringTypeW( type, src, count, chartype );
5274 /***********************************************************************
5275 * GetSystemDefaultLCID (kernelbase.@)
5277 LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
5279 return system_lcid;
5283 /***********************************************************************
5284 * GetSystemDefaultLangID (kernelbase.@)
5286 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
5288 return LANGIDFROMLCID( GetSystemDefaultLCID() );
5292 /***********************************************************************
5293 * GetSystemDefaultLocaleName (kernelbase.@)
5295 INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT count )
5297 return get_locale_info( system_locale, system_lcid, LOCALE_SNAME, name, count );
5301 /***********************************************************************
5302 * GetSystemDefaultUILanguage (kernelbase.@)
5304 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
5306 LANGID lang;
5307 NtQueryInstallUILanguage( &lang );
5308 return lang;
5312 /***********************************************************************
5313 * GetSystemPreferredUILanguages (kernelbase.@)
5315 BOOL WINAPI DECLSPEC_HOTPATCH GetSystemPreferredUILanguages( DWORD flags, ULONG *count,
5316 WCHAR *buffer, ULONG *size )
5318 return set_ntstatus( RtlGetSystemPreferredUILanguages( flags, 0, count, buffer, size ));
5322 /***********************************************************************
5323 * GetThreadPreferredUILanguages (kernelbase.@)
5325 BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPreferredUILanguages( DWORD flags, ULONG *count,
5326 WCHAR *buffer, ULONG *size )
5328 return set_ntstatus( RtlGetThreadPreferredUILanguages( flags, count, buffer, size ));
5332 /***********************************************************************
5333 * GetTimeZoneInformation (kernelbase.@)
5335 DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info )
5337 DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
5338 DWORD ret = GetDynamicTimeZoneInformation( &tzinfo );
5340 memcpy( info, &tzinfo, sizeof(*info) );
5341 return ret;
5345 /***********************************************************************
5346 * GetTimeZoneInformationForYear (kernelbase.@)
5348 BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year,
5349 DYNAMIC_TIME_ZONE_INFORMATION *dynamic,
5350 TIME_ZONE_INFORMATION *info )
5352 DYNAMIC_TIME_ZONE_INFORMATION local_info;
5353 HKEY key = 0, dst_key;
5354 DWORD count;
5355 LRESULT ret;
5356 struct
5358 LONG bias;
5359 LONG std_bias;
5360 LONG dlt_bias;
5361 SYSTEMTIME std_date;
5362 SYSTEMTIME dlt_date;
5363 } data;
5365 TRACE( "(%u,%p)\n", year, info );
5367 if (!dynamic)
5369 if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE;
5370 dynamic = &local_info;
5373 if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done;
5374 if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
5375 sizeof(info->StandardName), NULL, 0, system_dir ))
5377 count = sizeof(info->StandardName);
5378 if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count )))
5379 goto done;
5381 if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
5382 sizeof(info->DaylightName), NULL, 0, system_dir ))
5384 count = sizeof(info->DaylightName);
5385 if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count )))
5386 goto done;
5389 ret = ERROR_FILE_NOT_FOUND;
5390 if (!dynamic->DynamicDaylightTimeDisabled &&
5391 !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key ))
5393 WCHAR yearW[16];
5394 swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year );
5395 count = sizeof(data);
5396 ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count );
5397 RegCloseKey( dst_key );
5399 if (ret)
5401 count = sizeof(data);
5402 ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count );
5405 if (!ret)
5407 info->Bias = data.bias;
5408 info->StandardBias = data.std_bias;
5409 info->DaylightBias = data.dlt_bias;
5410 info->StandardDate = data.std_date;
5411 info->DaylightDate = data.dlt_date;
5414 done:
5415 RegCloseKey( key );
5416 if (ret) SetLastError( ret );
5417 return !ret;
5421 /***********************************************************************
5422 * GetUserDefaultLCID (kernelbase.@)
5424 LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
5426 return user_lcid;
5430 /***********************************************************************
5431 * GetUserDefaultLangID (kernelbase.@)
5433 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
5435 return LANGIDFROMLCID( GetUserDefaultLCID() );
5439 /***********************************************************************
5440 * GetUserDefaultLocaleName (kernelbase.@)
5442 INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
5444 return get_locale_info( user_locale, user_lcid, LOCALE_SNAME, name, len );
5448 /***********************************************************************
5449 * GetUserDefaultUILanguage (kernelbase.@)
5451 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
5453 return LANGIDFROMLCID( GetUserDefaultLCID() );
5457 /******************************************************************************
5458 * GetUserGeoID (kernelbase.@)
5460 GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass )
5462 GEOID ret = 39070;
5463 const WCHAR *name;
5464 WCHAR bufferW[40];
5465 HKEY hkey;
5467 switch (geoclass)
5469 case GEOCLASS_NATION:
5470 name = L"Nation";
5471 break;
5472 case GEOCLASS_REGION:
5473 name = L"Region";
5474 break;
5475 default:
5476 WARN("Unknown geoclass %ld\n", geoclass);
5477 return GEOID_NOT_AVAILABLE;
5479 if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey ))
5481 DWORD count = sizeof(bufferW);
5482 if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count ))
5483 ret = wcstol( bufferW, NULL, 10 );
5484 RegCloseKey( hkey );
5486 return ret;
5490 /******************************************************************************
5491 * GetUserPreferredUILanguages (kernelbase.@)
5493 BOOL WINAPI DECLSPEC_HOTPATCH GetUserPreferredUILanguages( DWORD flags, ULONG *count,
5494 WCHAR *buffer, ULONG *size )
5496 return set_ntstatus( RtlGetUserPreferredUILanguages( flags, 0, count, buffer, size ));
5500 /******************************************************************************
5501 * IdnToAscii (kernelbase.@)
5503 INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen,
5504 WCHAR *dst, INT dstlen )
5506 NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
5507 if (!set_ntstatus( status )) return 0;
5508 return dstlen;
5512 /******************************************************************************
5513 * IdnToNameprepUnicode (kernelbase.@)
5515 INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
5516 WCHAR *dst, INT dstlen )
5518 NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen );
5519 if (!set_ntstatus( status )) return 0;
5520 return dstlen;
5524 /******************************************************************************
5525 * IdnToUnicode (kernelbase.@)
5527 INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen,
5528 WCHAR *dst, INT dstlen )
5530 NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen );
5531 if (!set_ntstatus( status )) return 0;
5532 return dstlen;
5536 /******************************************************************************
5537 * IsCharAlphaA (kernelbase.@)
5539 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
5541 WCHAR wc;
5542 DWORD reslen;
5543 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5544 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5548 /******************************************************************************
5549 * IsCharAlphaW (kernelbase.@)
5551 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
5553 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5557 /******************************************************************************
5558 * IsCharAlphaNumericA (kernelbase.@)
5560 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
5562 WCHAR wc;
5563 DWORD reslen;
5564 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5565 return reslen && (get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5569 /******************************************************************************
5570 * IsCharAlphaNumericW (kernelbase.@)
5572 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
5574 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5578 /******************************************************************************
5579 * IsCharBlankW (kernelbase.@)
5581 BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
5583 return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK);
5587 /******************************************************************************
5588 * IsCharCntrlW (kernelbase.@)
5590 BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
5592 return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL);
5596 /******************************************************************************
5597 * IsCharDigitW (kernelbase.@)
5599 BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
5601 return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT);
5605 /******************************************************************************
5606 * IsCharLowerA (kernelbase.@)
5608 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
5610 WCHAR wc;
5611 DWORD reslen;
5612 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5613 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5617 /******************************************************************************
5618 * IsCharLowerW (kernelbase.@)
5620 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
5622 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5626 /******************************************************************************
5627 * IsCharPunctW (kernelbase.@)
5629 BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
5631 return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT);
5635 /******************************************************************************
5636 * IsCharSpaceA (kernelbase.@)
5638 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
5640 WCHAR wc;
5641 DWORD reslen;
5642 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5643 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5647 /******************************************************************************
5648 * IsCharSpaceW (kernelbase.@)
5650 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
5652 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5656 /******************************************************************************
5657 * IsCharUpperA (kernelbase.@)
5659 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
5661 WCHAR wc;
5662 DWORD reslen;
5663 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5664 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5668 /******************************************************************************
5669 * IsCharUpperW (kernelbase.@)
5671 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
5673 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5677 /******************************************************************************
5678 * IsCharXDigitW (kernelbase.@)
5680 BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
5682 return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT);
5686 /******************************************************************************
5687 * IsDBCSLeadByte (kernelbase.@)
5689 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
5691 return nls_info.AnsiTableInfo.DBCSCodePage && nls_info.AnsiTableInfo.DBCSOffsets[testchar];
5695 /******************************************************************************
5696 * IsDBCSLeadByteEx (kernelbase.@)
5698 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
5700 const CPTABLEINFO *table = get_codepage_table( codepage );
5701 return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
5705 /******************************************************************************
5706 * IsNormalizedString (kernelbase.@)
5708 BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
5710 BOOLEAN res;
5711 if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
5712 return res;
5716 /******************************************************************************
5717 * IsValidCodePage (kernelbase.@)
5719 BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
5721 switch (codepage)
5723 case CP_ACP:
5724 case CP_OEMCP:
5725 case CP_MACCP:
5726 case CP_THREAD_ACP:
5727 return FALSE;
5728 default:
5729 return get_codepage_table( codepage ) != NULL;
5734 /******************************************************************************
5735 * IsValidLanguageGroup (kernelbase.@)
5737 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
5739 WCHAR name[10], value[10];
5740 DWORD type, value_len = sizeof(value);
5741 BOOL ret = FALSE;
5742 HKEY key;
5744 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
5746 swprintf( name, ARRAY_SIZE(name), L"%x", id );
5747 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
5748 ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
5750 RegCloseKey( key );
5751 return ret;
5755 /******************************************************************************
5756 * IsValidLocale (kernelbase.@)
5758 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
5760 switch (lcid)
5762 case LOCALE_NEUTRAL:
5763 case LOCALE_USER_DEFAULT:
5764 case LOCALE_SYSTEM_DEFAULT:
5765 return FALSE;
5766 default:
5767 return !!NlsValidateLocale( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES );
5772 /******************************************************************************
5773 * IsValidLocaleName (kernelbase.@)
5775 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
5777 if (locale == LOCALE_NAME_USER_DEFAULT) return FALSE;
5778 return !!find_lcname_entry( locale );
5782 /******************************************************************************
5783 * IsValidNLSVersion (kernelbase.@)
5785 DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale,
5786 NLSVERSIONINFOEX *info )
5788 static const GUID GUID_NULL;
5789 NLSVERSIONINFOEX infoex;
5790 DWORD ret;
5792 if (func != COMPARE_STRING)
5794 SetLastError( ERROR_INVALID_PARAMETER );
5795 return FALSE;
5797 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5798 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5800 SetLastError( ERROR_INVALID_PARAMETER );
5801 return FALSE;
5803 infoex.dwNLSVersionInfoSize = sizeof(infoex);
5804 if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE;
5806 ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff);
5807 if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL ))
5808 ret = find_sortguid( &info->guidCustomVersion ) != NULL;
5810 if (!ret) SetLastError( ERROR_SUCCESS );
5811 return ret;
5815 /***********************************************************************
5816 * LCIDToLocaleName (kernelbase.@)
5818 INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
5820 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, flags );
5822 if (!locale)
5824 SetLastError( ERROR_INVALID_PARAMETER );
5825 return 0;
5827 return get_locale_info( locale, lcid, LOCALE_SNAME, name, count );
5831 /***********************************************************************
5832 * LCMapStringEx (kernelbase.@)
5834 INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
5835 WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
5836 void *reserved, LPARAM handle )
5838 const struct sortguid *sortid = NULL;
5839 LPWSTR dst_ptr;
5840 INT len;
5842 if (version) FIXME( "unsupported version structure %p\n", version );
5843 if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
5844 if (handle)
5846 static int once;
5847 if (!once++) FIXME( "unsupported lparam %Ix\n", handle );
5850 if (!src || !srclen || dstlen < 0)
5852 SetLastError( ERROR_INVALID_PARAMETER );
5853 return 0;
5856 /* mutually exclusive flags */
5857 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
5858 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
5859 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
5860 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
5861 !flags)
5863 SetLastError( ERROR_INVALID_FLAGS );
5864 return 0;
5867 if (!dstlen) dst = NULL;
5869 if (flags & LCMAP_LINGUISTIC_CASING && !(sortid = get_language_sort( locale )))
5871 FIXME( "unknown locale %s\n", debugstr_w(locale) );
5872 SetLastError( ERROR_INVALID_PARAMETER );
5873 return 0;
5876 if (flags & LCMAP_SORTKEY)
5878 INT ret;
5880 if (src == dst)
5882 SetLastError( ERROR_INVALID_FLAGS );
5883 return 0;
5885 if (srclen < 0) srclen = lstrlenW(src);
5887 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5888 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5890 if ((ret = get_sortkey( flags, src, srclen, (char *)dst, dstlen ))) ret++;
5891 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
5892 return ret;
5895 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
5896 if (flags & SORT_STRINGSORT)
5898 SetLastError( ERROR_INVALID_FLAGS );
5899 return 0;
5901 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
5902 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
5903 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
5904 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
5906 SetLastError( ERROR_INVALID_FLAGS );
5907 return 0;
5910 if (srclen < 0) srclen = lstrlenW(src) + 1;
5912 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5913 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5915 if (!dst) /* return required string length */
5917 if (flags & NORM_IGNORESYMBOLS)
5919 for (len = 0; srclen; src++, srclen--)
5920 if (!(get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE))) len++;
5922 else if (flags & LCMAP_FULLWIDTH)
5924 for (len = 0; srclen; src++, srclen--, len++)
5926 if (compose_katakana( src, srclen, NULL ) == 2)
5928 src++;
5929 srclen--;
5933 else if (flags & LCMAP_HALFWIDTH)
5935 for (len = 0; srclen; src++, srclen--, len++)
5937 WCHAR wch = *src;
5938 /* map Hiragana to Katakana before decomposition if needed */
5939 if ((flags & LCMAP_KATAKANA) &&
5940 ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E))
5941 wch += 0x60;
5943 if (decompose_katakana( wch, NULL, 0 ) == 2) len++;
5946 else len = srclen;
5947 return len;
5950 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
5952 SetLastError( ERROR_INVALID_FLAGS );
5953 return 0;
5956 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
5958 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
5960 if ((flags & NORM_IGNORESYMBOLS) && (get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE)))
5961 continue;
5962 *dst_ptr++ = *src;
5963 len--;
5965 goto done;
5968 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
5970 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
5972 WCHAR wch;
5973 if (flags & LCMAP_FULLWIDTH)
5975 /* map half-width character to full-width one,
5976 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
5977 if (map_to_fullwidth( src, srclen, &wch ) == 2)
5979 src++;
5980 srclen--;
5983 else wch = *src;
5985 if (flags & LCMAP_KATAKANA)
5987 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
5988 we can't use C3_HIRAGANA as some characters can't map to katakana */
5989 if ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E) wch += 0x60;
5991 else if (flags & LCMAP_HIRAGANA)
5993 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
5994 we can't use C3_KATAKANA as some characters can't map to hiragana */
5995 if ((wch >= 0x30A1 && wch <= 0x30F6) || wch == 0x30FD || wch == 0x30FE) wch -= 0x60;
5998 if (flags & LCMAP_HALFWIDTH)
6000 /* map full-width character to half-width one,
6001 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
6002 if (map_to_halfwidth(wch, dst_ptr, len) == 2)
6004 len--;
6005 dst_ptr++;
6006 if (!len) break;
6009 else *dst_ptr = wch;
6011 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen) goto done;
6013 srclen = dst_ptr - dst;
6014 src = dst;
6017 if (flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
6019 const USHORT *table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
6020 table = table + 2 + (flags & LCMAP_LOWERCASE ? table[1] : 0);
6021 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--, len--)
6022 *dst_ptr++ = casemap( table, *src );
6024 else
6026 len = min( srclen, dstlen );
6027 memcpy( dst, src, len * sizeof(WCHAR) );
6028 dst_ptr = dst + len;
6029 srclen -= len;
6032 done:
6033 if (srclen)
6035 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6036 return 0;
6039 return dst_ptr - dst;
6043 /***********************************************************************
6044 * LCMapStringA (kernelbase.@)
6046 INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
6047 char *dst, int dstlen )
6049 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
6050 LPWSTR srcW, dstW;
6051 INT ret = 0, srclenW, dstlenW;
6052 UINT locale_cp = CP_ACP;
6054 if (!src || !srclen || dstlen < 0)
6056 SetLastError( ERROR_INVALID_PARAMETER );
6057 return 0;
6060 locale_cp = get_lcid_codepage( lcid, flags );
6062 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
6063 if (srclenW) srcW = bufW;
6064 else
6066 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
6067 srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
6068 if (!srcW)
6070 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6071 return 0;
6073 MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
6076 if (flags & LCMAP_SORTKEY)
6078 if (src == dst)
6080 SetLastError( ERROR_INVALID_FLAGS );
6081 goto done;
6083 ret = LCMapStringEx( NULL, flags, srcW, srclenW, (WCHAR *)dst, dstlen, NULL, NULL, 0 );
6084 goto done;
6087 if (flags & SORT_STRINGSORT)
6089 SetLastError( ERROR_INVALID_FLAGS );
6090 goto done;
6093 dstlenW = LCMapStringEx( NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0 );
6094 if (!dstlenW) goto done;
6096 dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
6097 if (!dstW)
6099 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6100 goto done;
6102 LCMapStringEx( NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0 );
6103 ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
6104 HeapFree( GetProcessHeap(), 0, dstW );
6106 done:
6107 if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
6108 return ret;
6112 /***********************************************************************
6113 * LCMapStringW (kernelbase.@)
6115 INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
6116 WCHAR *dst, int dstlen )
6118 return LCMapStringEx( NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
6122 /***********************************************************************
6123 * LocaleNameToLCID (kernelbase.@)
6125 LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
6127 LCID lcid;
6128 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6130 if (!locale)
6132 SetLastError( ERROR_INVALID_PARAMETER );
6133 return 0;
6135 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
6136 lcid = locale->idefaultlanguage;
6137 return lcid;
6141 /******************************************************************************
6142 * MultiByteToWideChar (kernelbase.@)
6144 INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
6145 WCHAR *dst, INT dstlen )
6147 const CPTABLEINFO *info;
6148 int ret;
6150 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6152 SetLastError( ERROR_INVALID_PARAMETER );
6153 return 0;
6155 if (srclen < 0) srclen = strlen(src) + 1;
6157 switch (codepage)
6159 case CP_SYMBOL:
6160 ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
6161 break;
6162 case CP_UTF7:
6163 ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
6164 break;
6165 case CP_UNIXCP:
6166 codepage = unix_cp;
6167 /* fall through */
6168 default:
6169 if (!(info = get_codepage_table( codepage )))
6171 SetLastError( ERROR_INVALID_PARAMETER );
6172 return 0;
6174 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
6176 SetLastError( ERROR_INVALID_FLAGS );
6177 return 0;
6179 if (info->CodePage == CP_UTF8)
6180 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6181 else
6182 ret = mbstowcs_codepage( info, flags, src, srclen, dst, dstlen );
6183 break;
6185 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
6186 return ret;
6190 /******************************************************************************
6191 * NormalizeString (kernelbase.@)
6193 INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
6194 WCHAR *dst, INT dst_len)
6196 NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len );
6198 switch (status)
6200 case STATUS_OBJECT_NAME_NOT_FOUND:
6201 status = STATUS_INVALID_PARAMETER;
6202 break;
6203 case STATUS_BUFFER_TOO_SMALL:
6204 case STATUS_NO_UNICODE_TRANSLATION:
6205 dst_len = -dst_len;
6206 break;
6208 SetLastError( RtlNtStatusToDosError( status ));
6209 return dst_len;
6213 /******************************************************************************
6214 * ResolveLocaleName (kernelbase.@)
6216 INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
6218 FIXME( "stub: %s, %p, %d\n", wine_dbgstr_w(name), buffer, len );
6220 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
6221 return 0;
6225 /******************************************************************************
6226 * SetLocaleInfoW (kernelbase.@)
6228 BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
6230 WCHAR *str, tmp[80];
6232 if (!data)
6234 SetLastError( ERROR_INVALID_PARAMETER );
6235 return FALSE;
6238 switch (LOWORD(lctype))
6240 case LOCALE_ICALENDARTYPE: return set_registry_entry( &entry_icalendartype, data );
6241 case LOCALE_ICURRDIGITS: return set_registry_entry( &entry_icurrdigits, data );
6242 case LOCALE_ICURRENCY: return set_registry_entry( &entry_icurrency, data );
6243 case LOCALE_IDIGITS: return set_registry_entry( &entry_idigits, data );
6244 case LOCALE_IDIGITSUBSTITUTION: return set_registry_entry( &entry_idigitsubstitution, data );
6245 case LOCALE_IFIRSTDAYOFWEEK: return set_registry_entry( &entry_ifirstdayofweek, data );
6246 case LOCALE_IFIRSTWEEKOFYEAR: return set_registry_entry( &entry_ifirstweekofyear, data );
6247 case LOCALE_ILZERO: return set_registry_entry( &entry_ilzero, data );
6248 case LOCALE_IMEASURE: return set_registry_entry( &entry_imeasure, data );
6249 case LOCALE_INEGCURR: return set_registry_entry( &entry_inegcurr, data );
6250 case LOCALE_INEGNUMBER: return set_registry_entry( &entry_inegnumber, data );
6251 case LOCALE_IPAPERSIZE: return set_registry_entry( &entry_ipapersize, data );
6252 case LOCALE_S1159: return set_registry_entry( &entry_s1159, data );
6253 case LOCALE_S2359: return set_registry_entry( &entry_s2359, data );
6254 case LOCALE_SCURRENCY: return set_registry_entry( &entry_scurrency, data );
6255 case LOCALE_SDECIMAL: return set_registry_entry( &entry_sdecimal, data );
6256 case LOCALE_SGROUPING: return set_registry_entry( &entry_sgrouping, data );
6257 case LOCALE_SLIST: return set_registry_entry( &entry_slist, data );
6258 case LOCALE_SLONGDATE: return set_registry_entry( &entry_slongdate, data );
6259 case LOCALE_SMONDECIMALSEP: return set_registry_entry( &entry_smondecimalsep, data );
6260 case LOCALE_SMONGROUPING: return set_registry_entry( &entry_smongrouping, data );
6261 case LOCALE_SMONTHOUSANDSEP: return set_registry_entry( &entry_smonthousandsep, data );
6262 case LOCALE_SNATIVEDIGITS: return set_registry_entry( &entry_snativedigits, data );
6263 case LOCALE_SNEGATIVESIGN: return set_registry_entry( &entry_snegativesign, data );
6264 case LOCALE_SPOSITIVESIGN: return set_registry_entry( &entry_spositivesign, data );
6265 case LOCALE_SSHORTTIME: return set_registry_entry( &entry_sshorttime, data );
6266 case LOCALE_STHOUSAND: return set_registry_entry( &entry_sthousand, data );
6267 case LOCALE_SYEARMONTH: return set_registry_entry( &entry_syearmonth, data );
6269 case LOCALE_SDATE:
6270 if (!get_locale_info( user_locale, user_lcid, LOCALE_SSHORTDATE, tmp, ARRAY_SIZE(tmp) )) break;
6271 data = locale_replace_separator( tmp, data );
6272 /* fall through */
6273 case LOCALE_SSHORTDATE:
6274 if (!set_registry_entry( &entry_sshortdate, data )) return FALSE;
6275 update_registry_value( LOCALE_IDATE, L"iDate" );
6276 update_registry_value( LOCALE_SDATE, L"sDate" );
6277 return TRUE;
6279 case LOCALE_STIME:
6280 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6281 data = locale_replace_separator( tmp, data );
6282 /* fall through */
6283 case LOCALE_STIMEFORMAT:
6284 if (!set_registry_entry( &entry_stimeformat, data )) return FALSE;
6285 update_registry_value( LOCALE_ITIME, L"iTime" );
6286 update_registry_value( LOCALE_ITIMEMARKPOSN, L"iTimePrefix" );
6287 update_registry_value( LOCALE_ITLZERO, L"iTLZero" );
6288 update_registry_value( LOCALE_STIME, L"sTime" );
6289 return TRUE;
6291 case LOCALE_ITIME:
6292 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6293 if (!(str = find_format( tmp, L"Hh" ))) break;
6294 while (*str == 'h' || *str == 'H') *str++ = (*data == '0' ? 'h' : 'H');
6295 if (!set_registry_entry( &entry_stimeformat, tmp )) break;
6296 update_registry_value( LOCALE_ITIME, L"iTime" );
6297 return TRUE;
6299 case LOCALE_SINTLSYMBOL:
6300 /* FIXME: also store sintlsymbol */
6301 set_registry_entry( &entry_scurrency, data );
6302 return TRUE;
6304 SetLastError( ERROR_INVALID_FLAGS );
6305 return FALSE;
6309 /***********************************************************************
6310 * SetCalendarInfoW (kernelbase.@)
6312 INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
6314 FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
6315 return 0;
6319 /***********************************************************************
6320 * SetProcessPreferredUILanguages (kernelbase.@)
6322 BOOL WINAPI DECLSPEC_HOTPATCH SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6324 return set_ntstatus( RtlSetProcessPreferredUILanguages( flags, buffer, count ));
6328 /***********************************************************************
6329 * SetThreadPreferredUILanguages (kernelbase.@)
6331 BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6333 return set_ntstatus( RtlSetThreadPreferredUILanguages( flags, buffer, count ));
6337 /***********************************************************************
6338 * SetTimeZoneInformation (kernelbase.@)
6340 BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info )
6342 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info ));
6346 /******************************************************************************
6347 * SetUserGeoID (kernelbase.@)
6349 BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id )
6351 const struct geo_id *geo = find_geo_id_entry( id );
6352 WCHAR bufferW[10];
6353 HKEY hkey;
6355 if (!geo)
6357 SetLastError( ERROR_INVALID_PARAMETER );
6358 return FALSE;
6360 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
6362 const WCHAR *name = geo->class == GEOCLASS_NATION ? L"Nation" : L"Region";
6363 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geo->id );
6364 RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6366 if (geo->class == GEOCLASS_NATION || wcscmp( geo->iso2, L"XX" ))
6367 lstrcpyW( bufferW, geo->iso2 );
6368 else
6369 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", geo->uncode );
6370 RegSetValueExW( hkey, L"Name", 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6371 RegCloseKey( hkey );
6373 return TRUE;
6377 /***********************************************************************
6378 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
6380 BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info,
6381 const SYSTEMTIME *system,
6382 SYSTEMTIME *local )
6384 TIME_ZONE_INFORMATION tzinfo;
6385 LARGE_INTEGER ft;
6387 if (!info)
6389 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6390 info = &tzinfo;
6393 if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE;
6394 switch (get_timezone_id( info, ft, FALSE ))
6396 case TIME_ZONE_ID_UNKNOWN:
6397 ft.QuadPart -= info->Bias * (LONGLONG)600000000;
6398 break;
6399 case TIME_ZONE_ID_STANDARD:
6400 ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6401 break;
6402 case TIME_ZONE_ID_DAYLIGHT:
6403 ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6404 break;
6405 default:
6406 return FALSE;
6408 return FileTimeToSystemTime( (FILETIME *)&ft, local );
6412 /***********************************************************************
6413 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
6415 BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info,
6416 const SYSTEMTIME *local,
6417 SYSTEMTIME *system )
6419 TIME_ZONE_INFORMATION tzinfo;
6420 LARGE_INTEGER ft;
6422 if (!info)
6424 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6425 info = &tzinfo;
6428 if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE;
6429 switch (get_timezone_id( info, ft, TRUE ))
6431 case TIME_ZONE_ID_UNKNOWN:
6432 ft.QuadPart += info->Bias * (LONGLONG)600000000;
6433 break;
6434 case TIME_ZONE_ID_STANDARD:
6435 ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6436 break;
6437 case TIME_ZONE_ID_DAYLIGHT:
6438 ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6439 break;
6440 default:
6441 return FALSE;
6443 return FileTimeToSystemTime( (FILETIME *)&ft, system );
6447 /***********************************************************************
6448 * VerLanguageNameA (kernelbase.@)
6450 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
6452 return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6456 /***********************************************************************
6457 * VerLanguageNameW (kernelbase.@)
6459 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
6461 return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6465 /***********************************************************************
6466 * WideCharToMultiByte (kernelbase.@)
6468 INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
6469 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
6471 int ret;
6473 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6475 SetLastError( ERROR_INVALID_PARAMETER );
6476 return 0;
6479 if (srclen < 0) srclen = lstrlenW(src) + 1;
6481 switch (codepage)
6483 case CP_SYMBOL:
6484 ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
6485 break;
6486 case CP_UTF7:
6487 ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
6488 break;
6489 case CP_UTF8:
6490 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
6491 break;
6492 case CP_UNIXCP:
6493 if (unix_cp == CP_UTF8)
6495 if (used) *used = FALSE;
6496 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, NULL, NULL );
6497 break;
6499 codepage = unix_cp;
6500 /* fall through */
6501 default:
6502 ret = wcstombs_codepage( codepage, flags, src, srclen, dst, dstlen, defchar, used );
6503 break;
6505 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
6506 return ret;
6510 /***********************************************************************
6511 * GetUserDefaultGeoName (kernelbase.@)
6513 INT WINAPI GetUserDefaultGeoName(LPWSTR geo_name, int count)
6515 WCHAR buffer[32];
6516 LSTATUS status;
6517 DWORD size;
6518 HKEY key;
6520 TRACE( "geo_name %p, count %d.\n", geo_name, count );
6522 if (count && !geo_name)
6524 SetLastError( ERROR_INVALID_PARAMETER );
6525 return 0;
6527 if (!(status = RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &key )))
6529 size = sizeof(buffer);
6530 status = RegQueryValueExW( key, L"Name", NULL, NULL, (BYTE *)buffer, &size );
6531 RegCloseKey( key );
6533 if (status)
6535 const struct geo_id *geo = find_geo_id_entry( GetUserGeoID( GEOCLASS_NATION ));
6536 if (geo && geo->id != 39070)
6537 lstrcpyW( buffer, geo->iso2 );
6538 else
6539 lstrcpyW( buffer, L"001" );
6541 size = lstrlenW( buffer ) + 1;
6542 if (count < size)
6544 if (!count)
6545 return size;
6546 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6547 return 0;
6549 lstrcpyW( geo_name, buffer );
6550 return size;
6554 /***********************************************************************
6555 * SetUserDefaultGeoName (kernelbase.@)
6557 BOOL WINAPI SetUserGeoName(PWSTR geo_name)
6559 const struct geo_id *geo;
6561 TRACE( "geo_name %s.\n", debugstr_w( geo_name ));
6563 if (!geo_name || !(geo = find_geo_name_entry( geo_name )))
6565 SetLastError( ERROR_INVALID_PARAMETER );
6566 return FALSE;
6568 return SetUserGeoID( geo->id );