kernelbase: Reimplement GetTimeFormatA().
[wine.git] / dlls / kernelbase / locale.c
blob73e6953ec51bf80a4e09a130b471f0b50a321fdd
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 const WCHAR *subkey;
53 enum { NOT_CACHED, CACHED, MISSING } status;
54 WCHAR data[80];
57 static const WCHAR world_subkey[] = { 0xd83c, 0xdf0e, 0xd83c, 0xdf0f, 0xd83c, 0xdf0d, 0 }; /* 🌎🌏🌍 */
59 static struct registry_entry entry_icalendartype = { L"iCalendarType" };
60 static struct registry_entry entry_icountry = { L"iCountry" };
61 static struct registry_entry entry_icurrdigits = { L"iCurrDigits" };
62 static struct registry_entry entry_icurrency = { L"iCurrency" };
63 static struct registry_entry entry_idigits = { L"iDigits" };
64 static struct registry_entry entry_idigitsubstitution = { L"NumShape" };
65 static struct registry_entry entry_ifirstdayofweek = { L"iFirstDayOfWeek" };
66 static struct registry_entry entry_ifirstweekofyear = { L"iFirstWeekOfYear" };
67 static struct registry_entry entry_ilzero = { L"iLZero" };
68 static struct registry_entry entry_imeasure = { L"iMeasure" };
69 static struct registry_entry entry_inegcurr = { L"iNegCurr" };
70 static struct registry_entry entry_inegnumber = { L"iNegNumber" };
71 static struct registry_entry entry_ipapersize = { L"iPaperSize" };
72 static struct registry_entry entry_s1159 = { L"s1159" };
73 static struct registry_entry entry_s2359 = { L"s2359" };
74 static struct registry_entry entry_scurrency = { L"sCurrency" };
75 static struct registry_entry entry_sdecimal = { L"sDecimal" };
76 static struct registry_entry entry_sgrouping = { L"sGrouping" };
77 static struct registry_entry entry_sintlsymbol = { L"Currencies", world_subkey };
78 static struct registry_entry entry_slist = { L"sList" };
79 static struct registry_entry entry_slongdate = { L"sLongDate" };
80 static struct registry_entry entry_smondecimalsep = { L"sMonDecimalSep" };
81 static struct registry_entry entry_smongrouping = { L"sMonGrouping" };
82 static struct registry_entry entry_smonthousandsep = { L"sMonThousandSep" };
83 static struct registry_entry entry_snativedigits = { L"sNativeDigits" };
84 static struct registry_entry entry_snegativesign = { L"sNegativeSign" };
85 static struct registry_entry entry_spositivesign = { L"sPositiveSign" };
86 static struct registry_entry entry_sshortdate = { L"sShortDate" };
87 static struct registry_entry entry_sshorttime = { L"sShortTime" };
88 static struct registry_entry entry_sthousand = { L"sThousand" };
89 static struct registry_entry entry_stimeformat = { L"sTimeFormat" };
90 static struct registry_entry entry_syearmonth = { L"sYearMonth" };
93 static const struct { UINT cp; const WCHAR *name; } codepage_names[] =
95 { 37, L"IBM EBCDIC US Canada" },
96 { 424, L"IBM EBCDIC Hebrew" },
97 { 437, L"OEM United States" },
98 { 500, L"IBM EBCDIC International" },
99 { 708, L"Arabic ASMO" },
100 { 720, L"Arabic (Transparent ASMO)" },
101 { 737, L"OEM Greek 437G" },
102 { 775, L"OEM Baltic" },
103 { 850, L"OEM Multilingual Latin 1" },
104 { 852, L"OEM Slovak Latin 2" },
105 { 855, L"OEM Cyrillic" },
106 { 856, L"Hebrew PC" },
107 { 857, L"OEM Turkish" },
108 { 860, L"OEM Portuguese" },
109 { 861, L"OEM Icelandic" },
110 { 862, L"OEM Hebrew" },
111 { 863, L"OEM Canadian French" },
112 { 864, L"OEM Arabic" },
113 { 865, L"OEM Nordic" },
114 { 866, L"OEM Russian" },
115 { 869, L"OEM Greek" },
116 { 874, L"ANSI/OEM Thai" },
117 { 875, L"IBM EBCDIC Greek" },
118 { 878, L"Russian KOI8" },
119 { 932, L"ANSI/OEM Japanese Shift-JIS" },
120 { 936, L"ANSI/OEM Simplified Chinese GBK" },
121 { 949, L"ANSI/OEM Korean Unified Hangul" },
122 { 950, L"ANSI/OEM Traditional Chinese Big5" },
123 { 1006, L"IBM Arabic" },
124 { 1026, L"IBM EBCDIC Latin 5 Turkish" },
125 { 1250, L"ANSI Eastern Europe" },
126 { 1251, L"ANSI Cyrillic" },
127 { 1252, L"ANSI Latin 1" },
128 { 1253, L"ANSI Greek" },
129 { 1254, L"ANSI Turkish" },
130 { 1255, L"ANSI Hebrew" },
131 { 1256, L"ANSI Arabic" },
132 { 1257, L"ANSI Baltic" },
133 { 1258, L"ANSI/OEM Viet Nam" },
134 { 1361, L"Korean Johab" },
135 { 10000, L"Mac Roman" },
136 { 10001, L"Mac Japanese" },
137 { 10002, L"Mac Traditional Chinese" },
138 { 10003, L"Mac Korean" },
139 { 10004, L"Mac Arabic" },
140 { 10005, L"Mac Hebrew" },
141 { 10006, L"Mac Greek" },
142 { 10007, L"Mac Cyrillic" },
143 { 10008, L"Mac Simplified Chinese" },
144 { 10010, L"Mac Romanian" },
145 { 10017, L"Mac Ukrainian" },
146 { 10021, L"Mac Thai" },
147 { 10029, L"Mac Latin 2" },
148 { 10079, L"Mac Icelandic" },
149 { 10081, L"Mac Turkish" },
150 { 10082, L"Mac Croatian" },
151 { 20127, L"US-ASCII (7bit)" },
152 { 20866, L"Russian KOI8" },
153 { 20932, L"EUC-JP" },
154 { 20949, L"Korean Wansung" },
155 { 21866, L"Ukrainian KOI8" },
156 { 28591, L"ISO 8859-1 Latin 1" },
157 { 28592, L"ISO 8859-2 Latin 2 (East European)" },
158 { 28593, L"ISO 8859-3 Latin 3 (South European)" },
159 { 28594, L"ISO 8859-4 Latin 4 (Baltic old)" },
160 { 28595, L"ISO 8859-5 Cyrillic" },
161 { 28596, L"ISO 8859-6 Arabic" },
162 { 28597, L"ISO 8859-7 Greek" },
163 { 28598, L"ISO 8859-8 Hebrew" },
164 { 28599, L"ISO 8859-9 Latin 5 (Turkish)" },
165 { 28600, L"ISO 8859-10 Latin 6 (Nordic)" },
166 { 28601, L"ISO 8859-11 Latin (Thai)" },
167 { 28603, L"ISO 8859-13 Latin 7 (Baltic)" },
168 { 28604, L"ISO 8859-14 Latin 8 (Celtic)" },
169 { 28605, L"ISO 8859-15 Latin 9 (Euro)" },
170 { 28606, L"ISO 8859-16 Latin 10 (Balkan)" },
171 { 65000, L"65000 (UTF-7)" },
172 { 65001, L"65001 (UTF-8)" }
175 /* Unicode expanded ligatures */
176 static const WCHAR ligatures[][5] =
178 { 0x00c6, 'A','E',0 },
179 { 0x00de, 'T','H',0 },
180 { 0x00df, 's','s',0 },
181 { 0x00e6, 'a','e',0 },
182 { 0x00fe, 't','h',0 },
183 { 0x0132, 'I','J',0 },
184 { 0x0133, 'i','j',0 },
185 { 0x0152, 'O','E',0 },
186 { 0x0153, 'o','e',0 },
187 { 0x01c4, 'D',0x017d,0 },
188 { 0x01c5, 'D',0x017e,0 },
189 { 0x01c6, 'd',0x017e,0 },
190 { 0x01c7, 'L','J',0 },
191 { 0x01c8, 'L','j',0 },
192 { 0x01c9, 'l','j',0 },
193 { 0x01ca, 'N','J',0 },
194 { 0x01cb, 'N','j',0 },
195 { 0x01cc, 'n','j',0 },
196 { 0x01e2, 0x0100,0x0112,0 },
197 { 0x01e3, 0x0101,0x0113,0 },
198 { 0x01f1, 'D','Z',0 },
199 { 0x01f2, 'D','z',0 },
200 { 0x01f3, 'd','z',0 },
201 { 0x01fc, 0x00c1,0x00c9,0 },
202 { 0x01fd, 0x00e1,0x00e9,0 },
203 { 0x05f0, 0x05d5,0x05d5,0 },
204 { 0x05f1, 0x05d5,0x05d9,0 },
205 { 0x05f2, 0x05d9,0x05d9,0 },
206 { 0xfb00, 'f','f',0 },
207 { 0xfb01, 'f','i',0 },
208 { 0xfb02, 'f','l',0 },
209 { 0xfb03, 'f','f','i',0 },
210 { 0xfb04, 'f','f','l',0 },
211 { 0xfb05, 0x017f,'t',0 },
212 { 0xfb06, 's','t',0 },
215 struct calendar
217 USHORT icalintvalue; /* 00 */
218 USHORT itwodigityearmax; /* 02 */
219 UINT sshortdate; /* 04 */
220 UINT syearmonth; /* 08 */
221 UINT slongdate; /* 0c */
222 UINT serastring; /* 10 */
223 UINT iyearoffsetrange; /* 14 */
224 UINT sdayname; /* 18 */
225 UINT sabbrevdayname; /* 1c */
226 UINT smonthname; /* 20 */
227 UINT sabbrevmonthname; /* 24 */
228 UINT scalname; /* 28 */
229 UINT smonthday; /* 2c */
230 UINT sabbreverastring; /* 30 */
231 UINT sshortestdayname; /* 34 */
232 UINT srelativelongdate; /* 38 */
233 UINT unused[3]; /* 3c */
236 static const struct geo_id
238 GEOID id;
239 WCHAR latitude[12];
240 WCHAR longitude[12];
241 GEOCLASS class;
242 GEOID parent;
243 WCHAR iso2[4];
244 WCHAR iso3[4];
245 USHORT uncode;
246 USHORT dialcode;
247 WCHAR currcode[4];
248 WCHAR currsymbol[8];
249 } *geo_ids;
251 static const struct geo_index
253 WCHAR name[4];
254 UINT idx;
255 } *geo_index;
257 static unsigned int geo_ids_count;
258 static unsigned int geo_index_count;
260 enum charmaps
262 CHARMAP_FOLDDIGITS,
263 CHARMAP_COMPAT,
264 CHARMAP_HIRAGANA,
265 CHARMAP_KATAKANA,
266 CHARMAP_HALFWIDTH,
267 CHARMAP_FULLWIDTH,
268 CHARMAP_TRADITIONAL,
269 CHARMAP_SIMPLIFIED,
270 NB_CHARMAPS
273 static const USHORT *charmaps[NB_CHARMAPS];
275 /* NLS normalization file */
276 struct norm_table
278 WCHAR name[13]; /* 00 file name */
279 USHORT checksum[3]; /* 1a checksum? */
280 USHORT version[4]; /* 20 Unicode version */
281 USHORT form; /* 28 normalization form */
282 USHORT len_factor; /* 2a factor for length estimates */
283 USHORT unknown1; /* 2c */
284 USHORT decomp_size; /* 2e decomposition hash size */
285 USHORT comp_size; /* 30 composition hash size */
286 USHORT unknown2; /* 32 */
287 USHORT classes; /* 34 combining classes table offset */
288 USHORT props_level1; /* 36 char properties table level 1 offset */
289 USHORT props_level2; /* 38 char properties table level 2 offset */
290 USHORT decomp_hash; /* 3a decomposition hash table offset */
291 USHORT decomp_map; /* 3c decomposition character map table offset */
292 USHORT decomp_seq; /* 3e decomposition character sequences offset */
293 USHORT comp_hash; /* 40 composition hash table offset */
294 USHORT comp_seq; /* 42 composition character sequences offset */
295 /* BYTE[] combining class values */
296 /* BYTE[0x2200] char properties index level 1 */
297 /* BYTE[] char properties index level 2 */
298 /* WORD[] decomposition hash table */
299 /* WORD[] decomposition character map */
300 /* WORD[] decomposition character sequences */
301 /* WORD[] composition hash table */
302 /* WORD[] composition character sequences */
305 static NLSTABLEINFO nls_info;
306 static UINT unix_cp = CP_UTF8;
307 static LCID system_lcid;
308 static LCID user_lcid;
309 static HKEY intl_key;
310 static HKEY nls_key;
311 static HKEY tz_key;
312 static const NLS_LOCALE_LCID_INDEX *lcids_index;
313 static const NLS_LOCALE_LCNAME_INDEX *lcnames_index;
314 static const NLS_LOCALE_HEADER *locale_table;
315 static const WCHAR *locale_strings;
316 static const NLS_LOCALE_DATA *system_locale;
317 static const NLS_LOCALE_DATA *user_locale;
319 static CPTABLEINFO codepages[128];
320 static unsigned int nb_codepages;
322 static struct norm_table *norm_info;
324 struct sortguid
326 GUID id; /* sort GUID */
327 DWORD flags; /* flags */
328 DWORD compr; /* offset to compression table */
329 DWORD except; /* exception table offset in sortkey table */
330 DWORD ling_except; /* exception table offset for linguistic casing */
331 DWORD casemap; /* linguistic casemap table offset */
334 #define FLAG_HAS_3_BYTE_WEIGHTS 0x01
335 #define FLAG_REVERSEDIACRITICS 0x10
336 #define FLAG_DOUBLECOMPRESSION 0x20
337 #define FLAG_INVERSECASING 0x40
339 static const struct sortguid *current_locale_sort;
341 static const GUID default_sort_guid = { 0x00000001, 0x57ee, 0x1e5c, { 0x00, 0xb4, 0xd0, 0x00, 0x0b, 0xb1, 0xe1, 0x1e }};
343 static struct
345 DWORD *keys; /* sortkey table, indexed by char */
346 USHORT *casemap; /* casemap table, in l_intl.nls format */
347 WORD *ctypes; /* CT_CTYPE1,2,3 values */
348 BYTE *ctype_idx; /* index to map char to ctypes array entry */
349 DWORD version; /* NLS version */
350 DWORD guid_count; /* number of sort GUIDs */
351 struct sortguid *guids; /* table of sort GUIDs */
352 } sort;
354 static CRITICAL_SECTION locale_section;
355 static CRITICAL_SECTION_DEBUG critsect_debug =
357 0, 0, &locale_section,
358 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
359 0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") }
361 static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
364 static void load_locale_nls(void)
366 struct
368 UINT ctypes;
369 UINT unknown1;
370 UINT unknown2;
371 UINT unknown3;
372 UINT locales;
373 UINT charmaps;
374 UINT geoids;
375 UINT scripts;
376 } *header;
377 struct geo_header
379 WCHAR signature[4]; /* L"geo" */
380 UINT total_size;
381 UINT ids_offset;
382 UINT ids_count;
383 UINT index_offset;
384 UINT index_count;
385 } *geo_header;
387 LARGE_INTEGER dummy;
388 const USHORT *map_ptr;
389 unsigned int i;
391 RtlGetLocaleFileMappingAddress( (void **)&header, &system_lcid, &dummy );
392 locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales);
393 lcids_index = (const NLS_LOCALE_LCID_INDEX *)((char *)locale_table + locale_table->lcids_offset);
394 lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset);
395 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
396 geo_header = (struct geo_header *)((char *)header + header->geoids);
397 geo_ids = (const struct geo_id *)((char *)geo_header + geo_header->ids_offset);
398 geo_index = (const struct geo_index *)((char *)geo_header + geo_header->index_offset);
399 geo_ids_count = geo_header->ids_count;
400 geo_index_count = geo_header->index_count;
401 map_ptr = (const USHORT *)((char *)header + header->charmaps);
402 for (i = 0; i < NB_CHARMAPS; i++, map_ptr += *map_ptr) charmaps[i] = map_ptr + 1;
406 static void init_sortkeys( DWORD *ptr )
408 WORD *ctype;
409 DWORD *table;
411 sort.keys = (DWORD *)((char *)ptr + ptr[0]);
412 sort.casemap = (USHORT *)((char *)ptr + ptr[1]);
414 ctype = (WORD *)((char *)ptr + ptr[2]);
415 sort.ctypes = ctype + 2;
416 sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2;
418 table = (DWORD *)((char *)ptr + ptr[3]);
419 sort.version = table[0];
420 sort.guid_count = table[1];
421 sort.guids = (struct sortguid *)(table + 2);
425 static const struct sortguid *find_sortguid( const GUID *guid )
427 int pos, ret, min = 0, max = sort.guid_count - 1;
429 while (min <= max)
431 pos = (min + max) / 2;
432 ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) );
433 if (!ret) return &sort.guids[pos];
434 if (ret > 0) min = pos + 1;
435 else max = pos - 1;
437 ERR( "no sort found for %s\n", debugstr_guid( guid ));
438 return NULL;
442 static const struct sortguid *get_language_sort( const WCHAR *locale )
444 WCHAR *p, *end, buffer[LOCALE_NAME_MAX_LENGTH], guidstr[39];
445 const struct sortguid *ret;
446 UNICODE_STRING str;
447 GUID guid;
448 HKEY key = 0;
449 DWORD size, type;
451 if (locale == LOCALE_NAME_USER_DEFAULT)
453 if (current_locale_sort) return current_locale_sort;
454 GetUserDefaultLocaleName( buffer, ARRAY_SIZE( buffer ));
456 else lstrcpynW( buffer, locale, LOCALE_NAME_MAX_LENGTH );
458 if (buffer[0] && !RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key ))
460 for (;;)
462 size = sizeof(guidstr);
463 if (!RegQueryValueExW( key, buffer, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ)
465 RtlInitUnicodeString( &str, guidstr );
466 if (!RtlGUIDFromString( &str, &guid ))
468 ret = find_sortguid( &guid );
469 goto done;
471 break;
473 for (p = end = buffer; *p; p++) if (*p == '-' || *p == '_') end = p;
474 if (end == buffer) break;
475 *end = 0;
478 ret = find_sortguid( &default_sort_guid );
479 done:
480 RegCloseKey( key );
481 return ret;
485 static const NLS_LOCALE_DATA *get_locale_data( UINT idx )
487 ULONG offset = locale_table->locales_offset + idx * locale_table->locale_size;
488 return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
492 static const struct calendar *get_calendar_data( const NLS_LOCALE_DATA *locale, UINT id )
494 if (id == CAL_HIJRI) id = locale->islamic_cal[0];
495 else if (id == CAL_PERSIAN) id = locale->islamic_cal[1];
497 if (!id || id > locale_table->nb_calendars) return NULL;
498 return (const struct calendar *)((const char *)locale_table + locale_table->calendars_offset +
499 (id - 1) * locale_table->calendar_size);
503 static int compare_locale_names( const WCHAR *n1, const WCHAR *n2 )
505 for (;;)
507 WCHAR ch1 = *n1++;
508 WCHAR ch2 = *n2++;
509 if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A';
510 else if (ch1 == '_') ch1 = '-';
511 if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A';
512 else if (ch2 == '_') ch2 = '-';
513 if (!ch1 || ch1 != ch2) return ch1 - ch2;
518 static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry( const WCHAR *name )
520 int min = 0, max = locale_table->nb_lcnames - 1;
522 while (min <= max)
524 int res, pos = (min + max) / 2;
525 const WCHAR *str = locale_strings + lcnames_index[pos].name;
526 res = compare_locale_names( name, str + 1 );
527 if (res < 0) max = pos - 1;
528 else if (res > 0) min = pos + 1;
529 else return &lcnames_index[pos];
531 return NULL;
535 static const NLS_LOCALE_LCID_INDEX *find_lcid_entry( LCID lcid )
537 int min = 0, max = locale_table->nb_lcids - 1;
539 while (min <= max)
541 int pos = (min + max) / 2;
542 if (lcid < lcids_index[pos].id) max = pos - 1;
543 else if (lcid > lcids_index[pos].id) min = pos + 1;
544 else return &lcids_index[pos];
546 return NULL;
550 static const struct geo_id *find_geo_id_entry( GEOID id )
552 int min = 0, max = geo_ids_count - 1;
554 while (min <= max)
556 int pos = (min + max) / 2;
557 if (id < geo_ids[pos].id) max = pos - 1;
558 else if (id > geo_ids[pos].id) min = pos + 1;
559 else return &geo_ids[pos];
561 return NULL;
565 static const struct geo_id *find_geo_name_entry( const WCHAR *name )
567 int min = 0, max = geo_index_count - 1;
569 while (min <= max)
571 int res, pos = (min + max) / 2;
572 res = wcsicmp( name, geo_index[pos].name );
573 if (res < 0) max = pos - 1;
574 else if (res > 0) min = pos + 1;
575 else return &geo_ids[geo_index[pos].idx];
577 return NULL;
581 static const NLS_LOCALE_DATA *get_locale_by_name( const WCHAR *name, LCID *lcid )
583 const NLS_LOCALE_LCNAME_INDEX *entry;
585 if (name == LOCALE_NAME_USER_DEFAULT)
587 *lcid = user_lcid;
588 return user_locale;
590 if (!(entry = find_lcname_entry( name ))) return NULL;
591 *lcid = entry->id;
592 return get_locale_data( entry->idx );
596 /******************************************************************************
597 * NlsValidateLocale (kernelbase.@)
599 * Note: it seems to return some internal data on Windows, we simply return the locale.nls data pointer.
601 const NLS_LOCALE_DATA * WINAPI NlsValidateLocale( LCID *lcid, ULONG flags )
603 const NLS_LOCALE_LCNAME_INDEX *name_entry;
604 const NLS_LOCALE_LCID_INDEX *entry;
605 const NLS_LOCALE_DATA *locale;
607 switch (*lcid)
609 case LOCALE_SYSTEM_DEFAULT:
610 *lcid = system_lcid;
611 return system_locale;
612 case LOCALE_NEUTRAL:
613 case LOCALE_USER_DEFAULT:
614 case LOCALE_CUSTOM_DEFAULT:
615 case LOCALE_CUSTOM_UNSPECIFIED:
616 case LOCALE_CUSTOM_UI_DEFAULT:
617 *lcid = user_lcid;
618 return user_locale;
619 default:
620 if (!(entry = find_lcid_entry( *lcid ))) return NULL;
621 locale = get_locale_data( entry->idx );
622 if ((flags & LOCALE_ALLOW_NEUTRAL_NAMES) || locale->inotneutral) return locale;
623 if ((name_entry = find_lcname_entry( locale_strings + locale->ssortlocale + 1 )))
624 locale = get_locale_data( name_entry->idx );
625 return locale;
630 static int locale_return_data( const WCHAR *data, int datalen, LCTYPE type, WCHAR *buffer, int len )
632 if (type & LOCALE_RETURN_NUMBER)
634 SetLastError( ERROR_INVALID_FLAGS );
635 return 0;
638 if (!len) return datalen;
639 if (datalen > len)
641 SetLastError( ERROR_INSUFFICIENT_BUFFER );
642 return 0;
644 memcpy( buffer, data, datalen * sizeof(WCHAR) );
645 return datalen;
649 static BOOL set_registry_entry( struct registry_entry *entry, const WCHAR *data )
651 DWORD size = (wcslen(data) + 1) * sizeof(WCHAR);
652 LSTATUS ret;
654 if (size > sizeof(entry->data))
656 SetLastError( ERROR_INVALID_FLAGS );
657 return FALSE;
659 TRACE( "setting %s to %s\n", debugstr_w(entry->value), debugstr_w(data) );
661 RtlEnterCriticalSection( &locale_section );
662 if (!(ret = RegSetKeyValueW( intl_key, entry->subkey, entry->value, REG_SZ, (BYTE *)data, size )))
664 wcscpy( entry->data, data );
665 entry->status = CACHED;
667 RtlLeaveCriticalSection( &locale_section );
668 if (ret) SetLastError( ret );
669 return !ret;
673 static int locale_return_reg_string( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
675 DWORD size;
676 LRESULT res;
677 int ret = -1;
679 if (type & LOCALE_NOUSEROVERRIDE) return -1;
681 RtlEnterCriticalSection( &locale_section );
682 switch (entry->status)
684 case NOT_CACHED:
685 size = sizeof(entry->data);
686 if (entry->subkey)
688 HKEY key;
689 if (!(res = RegOpenKeyExW( intl_key, entry->subkey, 0, KEY_READ, &key )))
691 res = RegQueryValueExW( key, entry->value, NULL, NULL, (BYTE *)entry->data, &size );
692 RegCloseKey( key );
695 else res = RegQueryValueExW( intl_key, entry->value, NULL, NULL, (BYTE *)entry->data, &size );
697 if (res)
699 entry->status = MISSING;
700 break;
702 entry->status = CACHED;
703 /* fall through */
704 case CACHED:
705 ret = locale_return_data( entry->data, wcslen(entry->data) + 1, type, buffer, len );
706 break;
707 case MISSING:
708 break;
710 RtlLeaveCriticalSection( &locale_section );
711 return ret;
715 static int locale_return_string( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
717 return locale_return_data( locale_strings + pos + 1, locale_strings[pos] + 1, type, buffer, len );
721 static int locale_return_number( UINT val, LCTYPE type, WCHAR *buffer, int len )
723 int ret;
724 WCHAR tmp[80];
726 if (!(type & LOCALE_RETURN_NUMBER))
728 switch (LOWORD(type))
730 case LOCALE_ILANGUAGE:
731 case LOCALE_IDEFAULTLANGUAGE:
732 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%04x", val ) + 1;
733 break;
734 case LOCALE_IDEFAULTEBCDICCODEPAGE:
735 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", val ) + 1;
736 break;
737 default:
738 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val ) + 1;
739 break;
742 else ret = sizeof(UINT) / sizeof(WCHAR);
744 if (!len) return ret;
745 if (ret > len)
747 SetLastError( ERROR_INSUFFICIENT_BUFFER );
748 return 0;
751 if (type & LOCALE_RETURN_NUMBER) memcpy( buffer, &val, sizeof(val) );
752 else wcscpy( buffer, tmp );
754 return ret;
758 static int locale_return_reg_number( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
760 int ret, val;
761 WCHAR *end, tmp[80];
763 if (type & LOCALE_RETURN_NUMBER)
765 ret = locale_return_reg_string( entry, type & ~LOCALE_RETURN_NUMBER, tmp, ARRAY_SIZE( tmp ));
766 if (ret == -1) return ret;
767 val = wcstol( tmp, &end, 10 );
768 if (*end) /* invalid number */
770 SetLastError( ERROR_INVALID_FLAGS );
771 return 0;
773 return locale_return_number( val, type, buffer, len );
775 return locale_return_reg_string( entry, type, buffer, len );
779 static int locale_return_grouping( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
781 WORD i, count = locale_strings[pos];
782 const WCHAR *str = locale_strings + pos + 1;
783 int ret;
785 if (type & LOCALE_RETURN_NUMBER)
787 SetLastError( ERROR_INVALID_FLAGS );
788 return 0;
790 ret = 2 * count;
791 if (str[count - 1]) ret += 2; /* for final zero */
793 if (!len) return ret;
794 if (ret > len)
796 SetLastError( ERROR_INSUFFICIENT_BUFFER );
797 return 0;
799 for (i = 0; i < count; i++)
801 if (!str[i]) /* explicit null termination */
803 buffer[-1] = 0;
804 return ret;
806 *buffer++ = '0' + str[i];
807 *buffer++ = ';';
809 *buffer++ = '0';
810 *buffer = 0;
811 return ret;
815 static int locale_return_strarray( DWORD pos, WORD idx, LCTYPE type, WCHAR *buffer, int len )
817 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
818 WORD count = locale_strings[pos];
820 return locale_return_string( idx < count ? array[idx] : 0, type, buffer, len );
824 static int locale_return_strarray_concat( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
826 WORD i, count = locale_strings[pos];
827 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
828 int ret;
830 if (type & LOCALE_RETURN_NUMBER)
832 SetLastError( ERROR_INVALID_FLAGS );
833 return 0;
835 for (i = 0, ret = 1; i < count; i++) ret += locale_strings[array[i]];
837 if (!len) return ret;
838 if (ret > len)
840 SetLastError( ERROR_INSUFFICIENT_BUFFER );
841 return 0;
843 for (i = 0; i < count; i++)
845 memcpy( buffer, locale_strings + array[i] + 1, locale_strings[array[i]] * sizeof(WCHAR) );
846 buffer += locale_strings[array[i]];
848 *buffer = 0;
849 return ret;
853 static int cal_return_number( UINT val, CALTYPE type, WCHAR *buffer, int len, DWORD *value )
855 WCHAR tmp[12];
856 int ret;
858 if (type & CAL_RETURN_NUMBER)
860 *value = val;
861 return sizeof(UINT) / sizeof(WCHAR);
863 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val );
864 return locale_return_data( tmp, ret + 1, 0, buffer, len );
868 /* find the first format char in a format string */
869 static WCHAR *find_format( WCHAR *str, const WCHAR *accept )
871 for ( ; *str; str++)
873 if (*str == '\'')
875 if (!(str = wcschr( str + 1, '\'' ))) return NULL;
877 else if (wcschr( accept, *str ))
879 /* ignore "ddd" and "dddd" */
880 if (str[0] != 'd' || str[1] != 'd' || str[2] != 'd') return str;
881 str += 2;
882 while (str[1] == 'd') str++;
885 return NULL;
889 /* replace the separator in a date/time format string */
890 static WCHAR *locale_replace_separator( WCHAR *buffer, const WCHAR *sep )
892 UINT pos = 0;
893 WCHAR res[80];
894 WCHAR *next, *str = find_format( buffer, L"dMyHhms" );
896 if (!str) return buffer;
897 pos = str - buffer;
898 memcpy( res, buffer, pos * sizeof(WCHAR) );
899 for (;;)
901 res[pos++] = *str++;
902 while (str[0] == str[-1]) res[pos++] = *str++; /* copy repeated chars */
903 if (!(next = find_format( str, L"dMyHhms" ))) break;
904 wcscpy( res + pos, sep );
905 pos += wcslen(sep);
906 str = next;
908 wcscpy( res + pos, str );
909 return wcscpy( buffer, res );
913 /* FIXME: hardcoded, sortname is apparently not available in locale.nls */
914 static const WCHAR *get_locale_sortname( LCID lcid )
916 switch (PRIMARYLANGID( lcid ))
918 case LANG_CHINESE:
919 switch (SORTIDFROMLCID( lcid ))
921 case SORT_CHINESE_PRCP:
922 switch (SUBLANGID( lcid ))
924 case SUBLANG_CHINESE_TRADITIONAL:
925 case SUBLANG_CHINESE_HONGKONG:
926 case 0x1f:
927 return L"Stroke Count";
928 default:
929 return L"Pronunciation";
931 case SORT_CHINESE_UNICODE: return L"Unicode";
932 case SORT_CHINESE_PRC: return L"Stroke Count";
933 case SORT_CHINESE_BOPOMOFO: return L"Bopomofo";
934 case SORT_CHINESE_RADICALSTROKE: return L"Radical/Stroke";
935 case 5: return L"Surname";
937 break;
939 case LANG_GEORGIAN:
940 if (SORTIDFROMLCID( lcid ) == SORT_GEORGIAN_MODERN) return L"Modern";
941 return L"Traditional";
943 case LANG_GERMAN:
944 switch (SUBLANGID( lcid ))
946 case SUBLANG_NEUTRAL:
947 case SUBLANG_DEFAULT:
948 if (SORTIDFROMLCID( lcid ) == SORT_GERMAN_PHONE_BOOK) return L"Phone Book (DIN)";
949 return L"Dictionary";
951 break;
953 case LANG_HUNGARIAN:
954 if (SORTIDFROMLCID( lcid ) == SORT_HUNGARIAN_TECHNICAL) return L"Technical";
955 break;
957 case LANG_INVARIANT:
958 if (SORTIDFROMLCID( lcid ) == SORT_INVARIANT_MATH) return L"Default";
959 return L"Maths Alphanumerics";
961 case LANG_JAPANESE:
962 switch (SORTIDFROMLCID( lcid ))
964 case SORT_JAPANESE_XJIS: return L"XJIS";
965 case SORT_JAPANESE_UNICODE: return L"Unicode";
966 case SORT_JAPANESE_RADICALSTROKE: return L"Radical/Stroke";
968 break;
970 case LANG_KOREAN:
971 if (SORTIDFROMLCID( lcid ) == SORT_KOREAN_UNICODE) return L"Unicode";
972 return L"Dictionary";
974 case LANG_SPANISH:
975 switch (SUBLANGID( lcid ))
977 case SUBLANG_NEUTRAL:
978 case SUBLANG_SPANISH_MODERN:
979 return L"International";
980 case SUBLANG_DEFAULT:
981 return L"Traditional";
983 break;
985 return L"Default";
989 /* get locale information from the locale.nls file */
990 static int get_locale_info( const NLS_LOCALE_DATA *locale, LCID lcid, LCTYPE type,
991 WCHAR *buffer, int len )
993 static const WCHAR spermille[] = { 0x2030, 0 }; /* this one seems hardcoded */
994 static const BYTE ipossignposn[] = { 3, 3, 4, 2, 1, 1, 3, 4, 1, 3, 4, 2, 4, 3, 3, 1 };
995 static const BYTE inegsignposn[] = { 0, 3, 4, 2, 0, 1, 3, 4, 1, 3, 4, 2, 4, 3, 0, 0 };
996 static const BYTE inegsymprecedes[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, };
997 const WCHAR *sort;
998 WCHAR *str, *end, tmp[80];
999 UINT val;
1000 int ret;
1002 if (locale != user_locale) type |= LOCALE_NOUSEROVERRIDE;
1004 switch (LOWORD(type))
1006 case LOCALE_ILANGUAGE:
1007 /* return default language for neutral locales */
1008 val = locale->inotneutral ? locale->ilanguage : locale->idefaultlanguage;
1009 return locale_return_number( val, type, buffer, len );
1011 case LOCALE_SLOCALIZEDDISPLAYNAME:
1012 /* FIXME: localization */
1013 return locale_return_string( locale->sengdisplayname, type, buffer, len );
1015 case LOCALE_SABBREVLANGNAME:
1016 return locale_return_string( locale->sabbrevlangname, type, buffer, len );
1018 case LOCALE_SNATIVELANGNAME:
1019 return locale_return_string( locale->snativelangname, type, buffer, len );
1021 case LOCALE_ICOUNTRY:
1022 if ((ret = locale_return_reg_number( &entry_icountry, type, buffer, len )) != -1) return ret;
1023 return locale_return_number( locale->icountry, type, buffer, len );
1025 case LOCALE_SLOCALIZEDCOUNTRYNAME:
1026 /* FIXME: localization */
1027 return locale_return_string( locale->sengcountry, type, buffer, len );
1029 case LOCALE_SABBREVCTRYNAME:
1030 return locale_return_string( locale->sabbrevctryname, type, buffer, len );
1032 case LOCALE_SNATIVECTRYNAME:
1033 return locale_return_string( locale->snativectryname, type, buffer, len );
1035 case LOCALE_IDEFAULTLANGUAGE:
1036 return locale_return_number( locale->idefaultlanguage, type, buffer, len );
1038 case LOCALE_IDEFAULTCOUNTRY:
1039 return locale_return_number( locale->icountry, type, buffer, len );
1041 case LOCALE_IDEFAULTCODEPAGE:
1042 val = locale->idefaultcodepage == CP_UTF8 ? CP_OEMCP : locale->idefaultcodepage;
1043 return locale_return_number( val, type, buffer, len );
1045 case LOCALE_SLIST:
1046 if ((ret = locale_return_reg_string( &entry_slist, type, buffer, len )) != -1) return ret;
1047 return locale_return_string( locale->slist, type, buffer, len );
1049 case LOCALE_IMEASURE:
1050 if ((ret = locale_return_reg_number( &entry_imeasure, type, buffer, len )) != -1) return ret;
1051 return locale_return_number( locale->imeasure, type, buffer, len );
1053 case LOCALE_SDECIMAL:
1054 if ((ret = locale_return_reg_string( &entry_sdecimal, type, buffer, len )) != -1) return ret;
1055 return locale_return_string( locale->sdecimal, type, buffer, len );
1057 case LOCALE_STHOUSAND:
1058 if ((ret = locale_return_reg_string( &entry_sthousand, type, buffer, len )) != -1) return ret;
1059 return locale_return_string( locale->sthousand, type, buffer, len );
1061 case LOCALE_SGROUPING:
1062 if ((ret = locale_return_reg_string( &entry_sgrouping, type, buffer, len )) != -1) return ret;
1063 return locale_return_grouping( locale->sgrouping, type, buffer, len );
1065 case LOCALE_IDIGITS:
1066 if ((ret = locale_return_reg_number( &entry_idigits, type, buffer, len )) != -1) return ret;
1067 return locale_return_number( locale->idigits, type, buffer, len );
1069 case LOCALE_ILZERO:
1070 if ((ret = locale_return_reg_number( &entry_ilzero, type, buffer, len )) != -1) return ret;
1071 return locale_return_number( locale->ilzero, type, buffer, len );
1073 case LOCALE_SNATIVEDIGITS:
1074 if ((ret = locale_return_reg_string( &entry_snativedigits, type, buffer, len )) != -1) return ret;
1075 return locale_return_strarray_concat( locale->snativedigits, type, buffer, len );
1077 case LOCALE_SCURRENCY:
1078 if ((ret = locale_return_reg_string( &entry_scurrency, type, buffer, len )) != -1) return ret;
1079 return locale_return_string( locale->scurrency, type, buffer, len );
1081 case LOCALE_SINTLSYMBOL:
1082 if ((ret = locale_return_reg_string( &entry_sintlsymbol, type, buffer, len )) != -1) return ret;
1083 return locale_return_string( locale->sintlsymbol, type, buffer, len );
1085 case LOCALE_SMONDECIMALSEP:
1086 if ((ret = locale_return_reg_string( &entry_smondecimalsep, type, buffer, len )) != -1) return ret;
1087 return locale_return_string( locale->smondecimalsep, type, buffer, len );
1089 case LOCALE_SMONTHOUSANDSEP:
1090 if ((ret = locale_return_reg_string( &entry_smonthousandsep, type, buffer, len )) != -1) return ret;
1091 return locale_return_string( locale->smonthousandsep, type, buffer, len );
1093 case LOCALE_SMONGROUPING:
1094 if ((ret = locale_return_reg_string( &entry_smongrouping, type, buffer, len )) != -1) return ret;
1095 return locale_return_grouping( locale->smongrouping, type, buffer, len );
1097 case LOCALE_ICURRDIGITS:
1098 case LOCALE_IINTLCURRDIGITS:
1099 if ((ret = locale_return_reg_number( &entry_icurrdigits, type, buffer, len )) != -1) return ret;
1100 return locale_return_number( locale->icurrdigits, type, buffer, len );
1102 case LOCALE_ICURRENCY:
1103 if ((ret = locale_return_reg_number( &entry_icurrency, type, buffer, len )) != -1) return ret;
1104 return locale_return_number( locale->icurrency, type, buffer, len );
1106 case LOCALE_INEGCURR:
1107 if ((ret = locale_return_reg_number( &entry_inegcurr, type, buffer, len )) != -1) return ret;
1108 return locale_return_number( locale->inegcurr, type, buffer, len );
1110 case LOCALE_SDATE:
1111 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1112 tmp, ARRAY_SIZE( tmp ))) break;
1113 if (!(str = find_format( tmp, L"dMy" ))) break;
1114 while (str[1] == str[0]) str++; /* skip repeated chars */
1115 if (!(end = find_format( ++str, L"dMy" ))) break;
1116 *end++ = 0;
1117 return locale_return_data( str, end - str, type, buffer, len );
1119 case LOCALE_STIME:
1120 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1121 tmp, ARRAY_SIZE( tmp ))) break;
1122 if (!(str = find_format( tmp, L"Hhms" ))) break;
1123 while (str[1] == str[0]) str++; /* skip repeated chars */
1124 if (!(end = find_format( ++str, L"Hhms" ))) break;
1125 *end++ = 0;
1126 return locale_return_data( str, end - str, type, buffer, len );
1128 case LOCALE_SSHORTDATE:
1129 if ((ret = locale_return_reg_string( &entry_sshortdate, type, buffer, len )) != -1) return ret;
1130 return locale_return_strarray( locale->sshortdate, 0, type, buffer, len );
1132 case LOCALE_SLONGDATE:
1133 if ((ret = locale_return_reg_string( &entry_slongdate, type, buffer, len )) != -1) return ret;
1134 return locale_return_strarray( locale->slongdate, 0, type, buffer, len );
1136 case LOCALE_IDATE:
1137 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1138 tmp, ARRAY_SIZE( tmp ))) break;
1139 /* if both year and day are found before month, the last one takes precedence */
1140 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1142 if (*str == 'M') break;
1143 val = (*str == 'y' ? 2 : 1);
1145 return locale_return_number( val, type, buffer, len );
1147 case LOCALE_ILDATE:
1148 if (!get_locale_info( locale, lcid, LOCALE_SLONGDATE | (type & LOCALE_NOUSEROVERRIDE),
1149 tmp, ARRAY_SIZE( tmp ))) break;
1150 /* if both year and day are found before month, the last one takes precedence */
1151 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1153 if (*str == 'M') break;
1154 val = (*str == 'y' ? 2 : 1);
1156 return locale_return_number( val, type, buffer, len );
1158 case LOCALE_ITIME:
1159 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1160 tmp, ARRAY_SIZE( tmp ))) break;
1161 if (!(str = find_format( tmp, L"Hh" ))) break;
1162 return locale_return_number( *str == 'H', type, buffer, len );
1164 case LOCALE_ICENTURY:
1165 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1166 tmp, ARRAY_SIZE( tmp ))) break;
1167 if (!(str = find_format( tmp, L"y" ))) break;
1168 return locale_return_number( !wcsncmp( str, L"yyyy", 4 ), type, buffer, len );
1170 case LOCALE_ITLZERO:
1171 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1172 tmp, ARRAY_SIZE( tmp ))) break;
1173 if (!(str = find_format( tmp, L"Hh" ))) break;
1174 return locale_return_number( str[1] == str[0], type, buffer, len );
1176 case LOCALE_IDAYLZERO:
1177 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1178 tmp, ARRAY_SIZE( tmp ))) break;
1179 if (!(str = find_format( tmp, L"d" ))) break;
1180 return locale_return_number( str[1] == 'd', type, buffer, len );
1182 case LOCALE_IMONLZERO:
1183 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1184 tmp, ARRAY_SIZE( tmp ))) break;
1185 if (!(str = find_format( tmp, L"M" ))) break;
1186 return locale_return_number( str[1] == 'M', type, buffer, len );
1188 case LOCALE_S1159:
1189 if ((ret = locale_return_reg_string( &entry_s1159, type, buffer, len )) != -1) return ret;
1190 return locale_return_string( locale->s1159, type, buffer, len );
1192 case LOCALE_S2359:
1193 if ((ret = locale_return_reg_string( &entry_s2359, type, buffer, len )) != -1) return ret;
1194 return locale_return_string( locale->s2359, type, buffer, len );
1196 case LOCALE_SDAYNAME1:
1197 case LOCALE_SDAYNAME2:
1198 case LOCALE_SDAYNAME3:
1199 case LOCALE_SDAYNAME4:
1200 case LOCALE_SDAYNAME5:
1201 case LOCALE_SDAYNAME6:
1202 case LOCALE_SDAYNAME7:
1203 return locale_return_strarray( locale->sdayname,
1204 LOWORD(type - LOCALE_SDAYNAME1 + 1) % 7, type, buffer, len );
1206 case LOCALE_SABBREVDAYNAME1:
1207 case LOCALE_SABBREVDAYNAME2:
1208 case LOCALE_SABBREVDAYNAME3:
1209 case LOCALE_SABBREVDAYNAME4:
1210 case LOCALE_SABBREVDAYNAME5:
1211 case LOCALE_SABBREVDAYNAME6:
1212 case LOCALE_SABBREVDAYNAME7:
1213 return locale_return_strarray( locale->sabbrevdayname,
1214 LOWORD(type - LOCALE_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1216 case LOCALE_SMONTHNAME1:
1217 case LOCALE_SMONTHNAME2:
1218 case LOCALE_SMONTHNAME3:
1219 case LOCALE_SMONTHNAME4:
1220 case LOCALE_SMONTHNAME5:
1221 case LOCALE_SMONTHNAME6:
1222 case LOCALE_SMONTHNAME7:
1223 case LOCALE_SMONTHNAME8:
1224 case LOCALE_SMONTHNAME9:
1225 case LOCALE_SMONTHNAME10:
1226 case LOCALE_SMONTHNAME11:
1227 case LOCALE_SMONTHNAME12:
1228 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1229 locale->sgenitivemonth : locale->smonthname,
1230 type - LOCALE_SMONTHNAME1, type, buffer, len );
1232 case LOCALE_SABBREVMONTHNAME1:
1233 case LOCALE_SABBREVMONTHNAME2:
1234 case LOCALE_SABBREVMONTHNAME3:
1235 case LOCALE_SABBREVMONTHNAME4:
1236 case LOCALE_SABBREVMONTHNAME5:
1237 case LOCALE_SABBREVMONTHNAME6:
1238 case LOCALE_SABBREVMONTHNAME7:
1239 case LOCALE_SABBREVMONTHNAME8:
1240 case LOCALE_SABBREVMONTHNAME9:
1241 case LOCALE_SABBREVMONTHNAME10:
1242 case LOCALE_SABBREVMONTHNAME11:
1243 case LOCALE_SABBREVMONTHNAME12:
1244 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1245 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1246 type - LOCALE_SABBREVMONTHNAME1, type, buffer, len );
1248 case LOCALE_SPOSITIVESIGN:
1249 if ((ret = locale_return_reg_string( &entry_spositivesign, type, buffer, len )) != -1) return ret;
1250 return locale_return_string( locale->spositivesign, type, buffer, len );
1252 case LOCALE_SNEGATIVESIGN:
1253 if ((ret = locale_return_reg_string( &entry_snegativesign, type, buffer, len )) != -1) return ret;
1254 return locale_return_string( locale->snegativesign, type, buffer, len );
1256 case LOCALE_IPOSSIGNPOSN:
1257 if (!get_locale_info( locale, lcid,
1258 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1259 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1260 return locale_return_number( ipossignposn[val], type, buffer, len );
1262 case LOCALE_INEGSIGNPOSN:
1263 if (!get_locale_info( locale, lcid,
1264 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1265 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1266 return locale_return_number( inegsignposn[val], type, buffer, len );
1268 case LOCALE_IPOSSYMPRECEDES:
1269 if (!get_locale_info( locale, lcid,
1270 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1271 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1272 return locale_return_number( !(val & 1), type, buffer, len );
1274 case LOCALE_IPOSSEPBYSPACE:
1275 if (!get_locale_info( locale, lcid,
1276 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1277 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1278 return locale_return_number( !!(val & 2), type, buffer, len );
1280 case LOCALE_INEGSYMPRECEDES:
1281 if (!get_locale_info( locale, lcid,
1282 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1283 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1284 return locale_return_number( inegsymprecedes[val], type, buffer, len );
1286 case LOCALE_INEGSEPBYSPACE:
1287 if (!get_locale_info( locale, lcid,
1288 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1289 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1290 return locale_return_number( (val >= 8), type, buffer, len );
1292 case LOCALE_FONTSIGNATURE:
1293 return locale_return_data( locale_strings + locale->fontsignature + 1,
1294 locale_strings[locale->fontsignature], type, buffer, len );
1296 case LOCALE_SISO639LANGNAME:
1297 return locale_return_string( locale->siso639langname, type, buffer, len );
1299 case LOCALE_SISO3166CTRYNAME:
1300 return locale_return_string( locale->siso3166ctryname, type, buffer, len );
1302 case LOCALE_IGEOID:
1303 return locale_return_number( locale->igeoid, type, buffer, len );
1305 case LOCALE_SNAME:
1306 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1308 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1309 if (entry) return locale_return_string( entry->name, type, buffer, len );
1311 return locale_return_string( locale->sname, type, buffer, len );
1313 case LOCALE_SDURATION:
1314 return locale_return_strarray( locale->sduration, 0, type, buffer, len );
1316 case LOCALE_SKEYBOARDSTOINSTALL:
1317 return locale_return_string( locale->skeyboardstoinstall, type, buffer, len );
1319 case LOCALE_SSHORTESTDAYNAME1:
1320 case LOCALE_SSHORTESTDAYNAME2:
1321 case LOCALE_SSHORTESTDAYNAME3:
1322 case LOCALE_SSHORTESTDAYNAME4:
1323 case LOCALE_SSHORTESTDAYNAME5:
1324 case LOCALE_SSHORTESTDAYNAME6:
1325 case LOCALE_SSHORTESTDAYNAME7:
1326 return locale_return_strarray( locale->sshortestdayname,
1327 LOWORD(type - LOCALE_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1329 case LOCALE_SISO639LANGNAME2:
1330 return locale_return_string( locale->siso639langname2, type, buffer, len );
1332 case LOCALE_SISO3166CTRYNAME2:
1333 return locale_return_string( locale->siso3166ctryname2, type, buffer, len );
1335 case LOCALE_SNAN:
1336 return locale_return_string( locale->snan, type, buffer, len );
1338 case LOCALE_SPOSINFINITY:
1339 return locale_return_string( locale->sposinfinity, type, buffer, len );
1341 case LOCALE_SNEGINFINITY:
1342 return locale_return_string( locale->sneginfinity, type, buffer, len );
1344 case LOCALE_SSCRIPTS:
1345 return locale_return_string( locale->sscripts, type, buffer, len );
1347 case LOCALE_SPARENT:
1348 return locale_return_string( locale->sparent, type, buffer, len );
1350 case LOCALE_SCONSOLEFALLBACKNAME:
1351 return locale_return_string( locale->sconsolefallbackname, type, buffer, len );
1353 case LOCALE_SLOCALIZEDLANGUAGENAME:
1354 /* FIXME: localization */
1355 return locale_return_string( locale->senglanguage, type, buffer, len );
1357 case LOCALE_IREADINGLAYOUT:
1358 return locale_return_number( locale->ireadinglayout, type, buffer, len );
1360 case LOCALE_INEUTRAL:
1361 return locale_return_number( !locale->inotneutral, type, buffer, len );
1363 case LOCALE_SENGLISHDISPLAYNAME:
1364 return locale_return_string( locale->sengdisplayname, type, buffer, len );
1366 case LOCALE_SNATIVEDISPLAYNAME:
1367 return locale_return_string( locale->snativedisplayname, type, buffer, len );
1369 case LOCALE_INEGATIVEPERCENT:
1370 return locale_return_number( locale->inegativepercent, type, buffer, len );
1372 case LOCALE_IPOSITIVEPERCENT:
1373 return locale_return_number( locale->ipositivepercent, type, buffer, len );
1375 case LOCALE_SPERCENT:
1376 return locale_return_string( locale->spercent, type, buffer, len );
1378 case LOCALE_SPERMILLE:
1379 return locale_return_data( spermille, ARRAY_SIZE(spermille), type, buffer, len );
1381 case LOCALE_SMONTHDAY:
1382 return locale_return_strarray( locale->smonthday, 0, type, buffer, len );
1384 case LOCALE_SSHORTTIME:
1385 if ((ret = locale_return_reg_string( &entry_sshorttime, type, buffer, len )) != -1) return ret;
1386 return locale_return_strarray( locale->sshorttime, 0, type, buffer, len );
1388 case LOCALE_SOPENTYPELANGUAGETAG:
1389 return locale_return_string( locale->sopentypelanguagetag, type, buffer, len );
1391 case LOCALE_SSORTLOCALE:
1392 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1394 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1395 if (entry) return locale_return_string( entry->name, type, buffer, len );
1397 return locale_return_string( locale->ssortlocale, type, buffer, len );
1399 case LOCALE_SRELATIVELONGDATE:
1400 return locale_return_string( locale->srelativelongdate, type, buffer, len );
1402 case 0x007d: /* undocumented */
1403 return locale_return_number( 0, type, buffer, len );
1405 case LOCALE_SSHORTESTAM:
1406 return locale_return_string( locale->sshortestam, type, buffer, len );
1408 case LOCALE_SSHORTESTPM:
1409 return locale_return_string( locale->sshortestpm, type, buffer, len );
1411 case LOCALE_SENGLANGUAGE:
1412 return locale_return_string( locale->senglanguage, type, buffer, len );
1414 case LOCALE_SENGCOUNTRY:
1415 return locale_return_string( locale->sengcountry, type, buffer, len );
1417 case LOCALE_STIMEFORMAT:
1418 if ((ret = locale_return_reg_string( &entry_stimeformat, type, buffer, len )) != -1) return ret;
1419 return locale_return_strarray( locale->stimeformat, 0, type, buffer, len );
1421 case LOCALE_IDEFAULTANSICODEPAGE:
1422 val = locale->idefaultansicodepage == CP_UTF8 ? CP_ACP : locale->idefaultansicodepage;
1423 return locale_return_number( val, type, buffer, len );
1425 case LOCALE_ITIMEMARKPOSN:
1426 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1427 tmp, ARRAY_SIZE( tmp ))) break;
1428 if (!(str = find_format( tmp, L"Hhmst" ))) break;
1429 return locale_return_number( *str == 't', type, buffer, len );
1431 case LOCALE_SYEARMONTH:
1432 if ((ret = locale_return_reg_string( &entry_syearmonth, type, buffer, len )) != -1) return ret;
1433 return locale_return_strarray( locale->syearmonth, 0, type, buffer, len );
1435 case LOCALE_SENGCURRNAME:
1436 return locale_return_string( locale->sengcurrname, type, buffer, len );
1438 case LOCALE_SNATIVECURRNAME:
1439 return locale_return_string( locale->snativecurrname, type, buffer, len );
1441 case LOCALE_ICALENDARTYPE:
1442 if ((ret = locale_return_reg_number( &entry_icalendartype, type, buffer, len )) != -1) return ret;
1443 return locale_return_number( locale_strings[locale->scalendartype + 1], type, buffer, len );
1445 case LOCALE_IPAPERSIZE:
1446 if ((ret = locale_return_reg_number( &entry_ipapersize, type, buffer, len )) != -1) return ret;
1447 return locale_return_number( locale->ipapersize, type, buffer, len );
1449 case LOCALE_IOPTIONALCALENDAR:
1450 return locale_return_number( locale_strings[locale->scalendartype + 2], type, buffer, len );
1452 case LOCALE_IFIRSTDAYOFWEEK:
1453 if ((ret = locale_return_reg_number( &entry_ifirstdayofweek, type, buffer, len )) != -1) return ret;
1454 return locale_return_number( (locale->ifirstdayofweek + 6) % 7, type, buffer, len );
1456 case LOCALE_IFIRSTWEEKOFYEAR:
1457 if ((ret = locale_return_reg_number( &entry_ifirstweekofyear, type, buffer, len )) != -1) return ret;
1458 return locale_return_number( locale->ifirstweekofyear, type, buffer, len );
1460 case LOCALE_SMONTHNAME13:
1461 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1462 locale->sgenitivemonth : locale->smonthname,
1463 12, type, buffer, len );
1465 case LOCALE_SABBREVMONTHNAME13:
1466 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1467 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1468 12, type, buffer, len );
1470 case LOCALE_INEGNUMBER:
1471 if ((ret = locale_return_reg_number( &entry_inegnumber, type, buffer, len )) != -1) return ret;
1472 return locale_return_number( locale->inegnumber, type, buffer, len );
1474 case LOCALE_IDEFAULTMACCODEPAGE:
1475 val = locale->idefaultmaccodepage == CP_UTF8 ? CP_MACCP : locale->idefaultmaccodepage;
1476 return locale_return_number( val, type, buffer, len );
1478 case LOCALE_IDEFAULTEBCDICCODEPAGE:
1479 return locale_return_number( locale->idefaultebcdiccodepage, type, buffer, len );
1481 case LOCALE_SSORTNAME:
1482 sort = get_locale_sortname( lcid );
1483 return locale_return_data( sort, wcslen(sort) + 1, type, buffer, len );
1485 case LOCALE_IDIGITSUBSTITUTION:
1486 if ((ret = locale_return_reg_number( &entry_idigitsubstitution, type, buffer, len )) != -1) return ret;
1487 return locale_return_number( locale->idigitsubstitution, type, buffer, len );
1489 SetLastError( ERROR_INVALID_FLAGS );
1490 return 0;
1494 /* get calendar information from the locale.nls file */
1495 static int get_calendar_info( const NLS_LOCALE_DATA *locale, CALID id, CALTYPE type,
1496 WCHAR *buffer, int len, DWORD *value )
1498 unsigned int i, val = 0;
1499 const struct calendar *cal;
1501 if (type & CAL_RETURN_NUMBER)
1503 if (buffer || len || !value) goto invalid;
1505 else if (len < 0 || value) goto invalid;
1507 if (id != CAL_GREGORIAN)
1509 const USHORT *ids = locale_strings + locale->scalendartype;
1510 for (i = 0; i < ids[0]; i++) if (ids[1 + i] == id) break;
1511 if (i == ids[0]) goto invalid;
1513 if (!(cal = get_calendar_data( locale, id ))) goto invalid;
1515 switch (LOWORD(type))
1517 case CAL_ICALINTVALUE:
1518 return cal_return_number( cal->icalintvalue, type, buffer, len, value );
1520 case CAL_SCALNAME:
1521 return locale_return_strarray( locale->calnames, id - 1, type, buffer, len );
1523 case CAL_IYEAROFFSETRANGE:
1524 if (cal->iyearoffsetrange)
1526 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1527 const short *info = (const short *)locale_strings + array[0];
1528 val = (info[5] < 0) ? -info[5] : info[5] + 1; /* year zero */
1530 return cal_return_number( val, type, buffer, len, value );
1532 case CAL_SERASTRING:
1533 if (id == CAL_GREGORIAN) return locale_return_string( locale->serastring, type, buffer, len );
1534 if (cal->iyearoffsetrange)
1536 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1537 const short *info = (const short *)locale_strings + array[0];
1538 val = info[1] - 1;
1540 return locale_return_strarray( cal->serastring, val, type, buffer, len );
1542 case CAL_SSHORTDATE:
1543 val = (id == CAL_GREGORIAN) ? locale->sshortdate : cal->sshortdate;
1544 return locale_return_strarray( val, 0, type, buffer, len );
1546 case CAL_SLONGDATE:
1547 val = (id == CAL_GREGORIAN) ? locale->slongdate : cal->slongdate;
1548 return locale_return_strarray( val, 0, type, buffer, len );
1550 case CAL_SDAYNAME1:
1551 case CAL_SDAYNAME2:
1552 case CAL_SDAYNAME3:
1553 case CAL_SDAYNAME4:
1554 case CAL_SDAYNAME5:
1555 case CAL_SDAYNAME6:
1556 case CAL_SDAYNAME7:
1557 val = (id == CAL_GREGORIAN) ? locale->sdayname : cal->sdayname;
1558 return locale_return_strarray( val, (LOWORD(type) - CAL_SDAYNAME1 + 1) % 7, type, buffer, len );
1560 case CAL_SABBREVDAYNAME1:
1561 case CAL_SABBREVDAYNAME2:
1562 case CAL_SABBREVDAYNAME3:
1563 case CAL_SABBREVDAYNAME4:
1564 case CAL_SABBREVDAYNAME5:
1565 case CAL_SABBREVDAYNAME6:
1566 case CAL_SABBREVDAYNAME7:
1567 val = (id == CAL_GREGORIAN) ? locale->sabbrevdayname : cal->sabbrevdayname;
1568 return locale_return_strarray( val, (LOWORD(type) - CAL_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1569 case CAL_SMONTHNAME1:
1570 case CAL_SMONTHNAME2:
1571 case CAL_SMONTHNAME3:
1572 case CAL_SMONTHNAME4:
1573 case CAL_SMONTHNAME5:
1574 case CAL_SMONTHNAME6:
1575 case CAL_SMONTHNAME7:
1576 case CAL_SMONTHNAME8:
1577 case CAL_SMONTHNAME9:
1578 case CAL_SMONTHNAME10:
1579 case CAL_SMONTHNAME11:
1580 case CAL_SMONTHNAME12:
1581 case CAL_SMONTHNAME13:
1582 if (id != CAL_GREGORIAN) val = cal->smonthname;
1583 else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) val = locale->sgenitivemonth;
1584 else val = locale->smonthname;
1585 return locale_return_strarray( val, LOWORD(type) - CAL_SMONTHNAME1, type, buffer, len );
1587 case CAL_SABBREVMONTHNAME1:
1588 case CAL_SABBREVMONTHNAME2:
1589 case CAL_SABBREVMONTHNAME3:
1590 case CAL_SABBREVMONTHNAME4:
1591 case CAL_SABBREVMONTHNAME5:
1592 case CAL_SABBREVMONTHNAME6:
1593 case CAL_SABBREVMONTHNAME7:
1594 case CAL_SABBREVMONTHNAME8:
1595 case CAL_SABBREVMONTHNAME9:
1596 case CAL_SABBREVMONTHNAME10:
1597 case CAL_SABBREVMONTHNAME11:
1598 case CAL_SABBREVMONTHNAME12:
1599 case CAL_SABBREVMONTHNAME13:
1600 if (id != CAL_GREGORIAN) val = cal->sabbrevmonthname;
1601 else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) val = locale->sabbrevgenitivemonth;
1602 else val = locale->sabbrevmonthname;
1603 return locale_return_strarray( val, LOWORD(type) - CAL_SABBREVMONTHNAME1, type, buffer, len );
1605 case CAL_SYEARMONTH:
1606 val = (id == CAL_GREGORIAN) ? locale->syearmonth : cal->syearmonth;
1607 return locale_return_strarray( val, 0, type, buffer, len );
1609 case CAL_ITWODIGITYEARMAX:
1610 return cal_return_number( cal->itwodigityearmax, type, buffer, len, value );
1612 case CAL_SSHORTESTDAYNAME1:
1613 case CAL_SSHORTESTDAYNAME2:
1614 case CAL_SSHORTESTDAYNAME3:
1615 case CAL_SSHORTESTDAYNAME4:
1616 case CAL_SSHORTESTDAYNAME5:
1617 case CAL_SSHORTESTDAYNAME6:
1618 case CAL_SSHORTESTDAYNAME7:
1619 val = (id == CAL_GREGORIAN) ? locale->sshortestdayname : cal->sshortestdayname;
1620 return locale_return_strarray( val, (LOWORD(type) - CAL_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1622 case CAL_SMONTHDAY:
1623 val = (id == CAL_GREGORIAN) ? locale->smonthday : cal->smonthday;
1624 return locale_return_strarray( val, 0, type, buffer, len );
1626 case CAL_SABBREVERASTRING:
1627 if (id == CAL_GREGORIAN) return locale_return_string( locale->sabbreverastring, type, buffer, len );
1628 if (cal->iyearoffsetrange)
1630 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1631 const short *info = (const short *)locale_strings + array[0];
1632 val = info[1] - 1;
1634 return locale_return_strarray( cal->sabbreverastring, val, type, buffer, len );
1636 case CAL_SRELATIVELONGDATE:
1637 val = (id == CAL_GREGORIAN) ? locale->srelativelongdate : cal->srelativelongdate;
1638 return locale_return_string( val, type, buffer, len );
1640 case CAL_SENGLISHERANAME:
1641 case CAL_SENGLISHABBREVERANAME:
1642 /* not supported on Windows */
1643 break;
1645 SetLastError( ERROR_INVALID_FLAGS );
1646 return 0;
1648 invalid:
1649 SetLastError( ERROR_INVALID_PARAMETER );
1650 return 0;
1654 /* get geo information from the locale.nls file */
1655 static int get_geo_info( const struct geo_id *geo, enum SYSGEOTYPE type,
1656 WCHAR *buffer, int len, LANGID lang )
1658 WCHAR tmp[12];
1659 const WCHAR *str = tmp;
1660 int ret;
1662 switch (type)
1664 case GEO_NATION:
1665 if (geo->class != GEOCLASS_NATION) return 0;
1666 /* fall through */
1667 case GEO_ID:
1668 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->id );
1669 break;
1670 case GEO_ISO_UN_NUMBER:
1671 swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", geo->uncode );
1672 break;
1673 case GEO_PARENT:
1674 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->parent );
1675 break;
1676 case GEO_DIALINGCODE:
1677 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->dialcode );
1678 break;
1679 case GEO_ISO2:
1680 str = geo->iso2;
1681 break;
1682 case GEO_ISO3:
1683 str = geo->iso3;
1684 break;
1685 case GEO_LATITUDE:
1686 str = geo->latitude;
1687 break;
1688 case GEO_LONGITUDE:
1689 str = geo->longitude;
1690 break;
1691 case GEO_CURRENCYCODE:
1692 str = geo->currcode;
1693 break;
1694 case GEO_CURRENCYSYMBOL:
1695 str = geo->currsymbol;
1696 break;
1697 case GEO_RFC1766:
1698 case GEO_LCID:
1699 case GEO_FRIENDLYNAME:
1700 case GEO_OFFICIALNAME:
1701 case GEO_TIMEZONES:
1702 case GEO_OFFICIALLANGUAGES:
1703 case GEO_NAME:
1704 FIXME( "type %u is not supported\n", type );
1705 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1706 return 0;
1707 default:
1708 SetLastError( ERROR_INVALID_FLAGS );
1709 return 0;
1712 ret = lstrlenW(str) + 1;
1713 if (!buffer || !len) return ret;
1715 memcpy( buffer, str, min( ret, len ) * sizeof(WCHAR) );
1716 if (len < ret) SetLastError( ERROR_INSUFFICIENT_BUFFER );
1717 return len < ret ? 0 : ret;
1721 /* update a registry value based on the current user locale info */
1722 static void update_registry_value( UINT type, const WCHAR *subkey, const WCHAR *value )
1724 WCHAR buffer[80];
1725 UINT len = get_locale_info( user_locale, user_lcid, type, buffer, ARRAY_SIZE(buffer) );
1726 if (len) RegSetKeyValueW( intl_key, subkey, value, REG_SZ, (BYTE *)buffer, len * sizeof(WCHAR) );
1730 /* update all registry values upon user locale change */
1731 static void update_locale_registry(void)
1733 WCHAR buffer[80];
1734 UINT len;
1736 len = swprintf( buffer, ARRAY_SIZE(buffer), L"%08x", GetUserDefaultLCID() );
1737 RegSetValueExW( intl_key, L"Locale", 0, REG_SZ, (BYTE *)buffer, (len + 1) * sizeof(WCHAR) );
1739 #define UPDATE(val,entry) update_registry_value( LOCALE_NOUSEROVERRIDE | (val), (entry).subkey, (entry).value )
1740 UPDATE( LOCALE_ICALENDARTYPE, entry_icalendartype );
1741 UPDATE( LOCALE_ICOUNTRY, entry_icountry );
1742 UPDATE( LOCALE_ICURRDIGITS, entry_icurrdigits );
1743 UPDATE( LOCALE_ICURRENCY, entry_icurrency );
1744 UPDATE( LOCALE_IDIGITS, entry_idigits );
1745 UPDATE( LOCALE_IDIGITSUBSTITUTION, entry_idigitsubstitution );
1746 UPDATE( LOCALE_IFIRSTDAYOFWEEK, entry_ifirstdayofweek );
1747 UPDATE( LOCALE_IFIRSTWEEKOFYEAR, entry_ifirstweekofyear );
1748 UPDATE( LOCALE_ILZERO, entry_ilzero );
1749 UPDATE( LOCALE_IMEASURE, entry_imeasure );
1750 UPDATE( LOCALE_INEGCURR, entry_inegcurr );
1751 UPDATE( LOCALE_INEGNUMBER, entry_inegnumber );
1752 UPDATE( LOCALE_IPAPERSIZE, entry_ipapersize );
1753 UPDATE( LOCALE_S1159, entry_s1159 );
1754 UPDATE( LOCALE_S2359, entry_s2359 );
1755 UPDATE( LOCALE_SCURRENCY, entry_scurrency );
1756 UPDATE( LOCALE_SDECIMAL, entry_sdecimal );
1757 UPDATE( LOCALE_SGROUPING, entry_sgrouping );
1758 UPDATE( LOCALE_SINTLSYMBOL, entry_sintlsymbol );
1759 UPDATE( LOCALE_SLIST, entry_slist );
1760 UPDATE( LOCALE_SLONGDATE, entry_slongdate );
1761 UPDATE( LOCALE_SMONDECIMALSEP, entry_smondecimalsep );
1762 UPDATE( LOCALE_SMONGROUPING, entry_smongrouping );
1763 UPDATE( LOCALE_SMONTHOUSANDSEP, entry_smonthousandsep );
1764 UPDATE( LOCALE_SNATIVEDIGITS, entry_snativedigits );
1765 UPDATE( LOCALE_SNEGATIVESIGN, entry_snegativesign );
1766 UPDATE( LOCALE_SPOSITIVESIGN, entry_spositivesign );
1767 UPDATE( LOCALE_SSHORTDATE, entry_sshortdate );
1768 UPDATE( LOCALE_SSHORTTIME, entry_sshorttime );
1769 UPDATE( LOCALE_STHOUSAND, entry_sthousand );
1770 UPDATE( LOCALE_STIMEFORMAT, entry_stimeformat );
1771 UPDATE( LOCALE_SYEARMONTH, entry_syearmonth );
1772 #undef UPDATE
1773 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDATE, NULL, L"iDate" );
1774 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIME, NULL, L"iTime" );
1775 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIMEMARKPOSN, NULL, L"iTimePrefix" );
1776 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITLZERO, NULL, L"iTLZero" );
1777 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SDATE, NULL, L"sDate" );
1778 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STIME, NULL, L"sTime" );
1779 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SABBREVLANGNAME, NULL, L"sLanguage" );
1780 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SCOUNTRY, NULL, L"sCountry" );
1781 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNAME, NULL, L"LocaleName" );
1782 SetUserGeoID( user_locale->igeoid );
1786 /***********************************************************************
1787 * init_locale
1789 void init_locale( HMODULE module )
1791 USHORT utf8[2] = { 0, CP_UTF8 };
1792 USHORT *ansi_ptr, *oem_ptr;
1793 void *sort_ptr;
1794 WCHAR bufferW[LOCALE_NAME_MAX_LENGTH];
1795 DYNAMIC_TIME_ZONE_INFORMATION timezone;
1796 const WCHAR *user_locale_name;
1797 DWORD count;
1798 SIZE_T size;
1799 HKEY hkey;
1801 kernelbase_handle = module;
1802 load_locale_nls();
1804 if (system_lcid == LOCALE_CUSTOM_UNSPECIFIED) system_lcid = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT );
1805 system_locale = NlsValidateLocale( &system_lcid, 0 );
1807 NtQueryDefaultLocale( TRUE, &user_lcid );
1808 if (!(user_locale = NlsValidateLocale( &user_lcid, 0 )))
1810 if (GetEnvironmentVariableW( L"WINEUSERLOCALE", bufferW, ARRAY_SIZE(bufferW) ))
1811 user_locale = get_locale_by_name( bufferW, &user_lcid );
1812 if (!user_locale) user_locale = system_locale;
1814 user_lcid = user_locale->ilanguage;
1815 if (user_lcid == LOCALE_CUSTOM_UNSPECIFIED) user_lcid = LOCALE_CUSTOM_DEFAULT;
1817 if (GetEnvironmentVariableW( L"WINEUNIXCP", bufferW, ARRAY_SIZE(bufferW) ))
1818 unix_cp = wcstoul( bufferW, NULL, 10 );
1820 NtGetNlsSectionPtr( 9, 0, NULL, &sort_ptr, &size );
1821 NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size );
1822 init_sortkeys( sort_ptr );
1824 ansi_ptr = NtCurrentTeb()->Peb->AnsiCodePageData ? NtCurrentTeb()->Peb->AnsiCodePageData : utf8;
1825 oem_ptr = NtCurrentTeb()->Peb->OemCodePageData ? NtCurrentTeb()->Peb->OemCodePageData : utf8;
1826 RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info );
1828 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
1829 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
1830 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1831 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tz_key, NULL );
1832 RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International",
1833 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL );
1835 current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT );
1837 if (GetDynamicTimeZoneInformation( &timezone ) != TIME_ZONE_ID_INVALID &&
1838 !RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\TimeZoneInformation",
1839 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1841 RegSetValueExW( hkey, L"StandardName", 0, REG_SZ, (BYTE *)timezone.StandardName,
1842 (lstrlenW(timezone.StandardName) + 1) * sizeof(WCHAR) );
1843 RegSetValueExW( hkey, L"TimeZoneKeyName", 0, REG_SZ, (BYTE *)timezone.TimeZoneKeyName,
1844 (lstrlenW(timezone.TimeZoneKeyName) + 1) * sizeof(WCHAR) );
1845 RegCloseKey( hkey );
1848 /* Update registry contents if the user locale has changed.
1849 * This simulates the action of the Windows control panel. */
1851 user_locale_name = locale_strings + user_locale->sname + 1;
1852 count = sizeof(bufferW);
1853 if (!RegQueryValueExW( intl_key, L"LocaleName", NULL, NULL, (BYTE *)bufferW, &count ))
1855 if (!wcscmp( bufferW, user_locale_name )) return; /* unchanged */
1856 TRACE( "updating registry, locale changed %s -> %s\n",
1857 debugstr_w(bufferW), debugstr_w(user_locale_name) );
1859 else TRACE( "updating registry, locale changed none -> %s\n", debugstr_w(user_locale_name) );
1861 update_locale_registry();
1863 if (!RegCreateKeyExW( nls_key, L"Codepage",
1864 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1866 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", GetACP() );
1867 RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1868 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", GetOEMCP() );
1869 RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1870 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", system_locale->idefaultmaccodepage );
1871 RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1872 RegCloseKey( hkey );
1877 static inline WCHAR casemap( const USHORT *table, WCHAR ch )
1879 return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
1883 static inline WORD get_char_type( DWORD type, WCHAR ch )
1885 const BYTE *ptr = sort.ctype_idx + ((const WORD *)sort.ctype_idx)[ch >> 8];
1886 ptr = sort.ctype_idx + ((const WORD *)ptr)[(ch >> 4) & 0x0f] + (ch & 0x0f);
1887 return sort.ctypes[*ptr * 3 + type / 2];
1891 static BYTE rol( BYTE val, BYTE count )
1893 return (val << count) | (val >> (8 - count));
1897 static BYTE get_char_props( const struct norm_table *info, unsigned int ch )
1899 const BYTE *level1 = (const BYTE *)((const USHORT *)info + info->props_level1);
1900 const BYTE *level2 = (const BYTE *)((const USHORT *)info + info->props_level2);
1901 BYTE off = level1[ch / 128];
1903 if (!off || off >= 0xfb) return rol( off, 5 );
1904 return level2[(off - 1) * 128 + ch % 128];
1908 static const WCHAR *get_decomposition( WCHAR ch, unsigned int *ret_len )
1910 const struct pair { WCHAR src; USHORT dst; } *pairs;
1911 const USHORT *hash_table = (const USHORT *)norm_info + norm_info->decomp_hash;
1912 const WCHAR *ret;
1913 unsigned int i, pos, end, len, hash;
1915 *ret_len = 1;
1916 hash = ch % norm_info->decomp_size;
1917 pos = hash_table[hash];
1918 if (pos >> 13)
1920 if (get_char_props( norm_info, ch ) != 0xbf) return NULL;
1921 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pos & 0x1fff);
1922 len = pos >> 13;
1924 else
1926 pairs = (const struct pair *)((const USHORT *)norm_info + norm_info->decomp_map);
1928 /* find the end of the hash bucket */
1929 for (i = hash + 1; i < norm_info->decomp_size; i++) if (!(hash_table[i] >> 13)) break;
1930 if (i < norm_info->decomp_size) end = hash_table[i];
1931 else for (end = pos; pairs[end].src; end++) ;
1933 for ( ; pos < end; pos++)
1935 if (pairs[pos].src != (WCHAR)ch) continue;
1936 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pairs[pos].dst & 0x1fff);
1937 len = pairs[pos].dst >> 13;
1938 break;
1940 if (pos >= end) return NULL;
1943 if (len == 7) while (ret[len]) len++;
1944 if (!ret[0]) len = 0; /* ignored char */
1945 *ret_len = len;
1946 return ret;
1950 static WCHAR compose_chars( WCHAR ch1, WCHAR ch2 )
1952 const USHORT *table = (const USHORT *)norm_info + norm_info->comp_hash;
1953 const WCHAR *chars = (const USHORT *)norm_info + norm_info->comp_seq;
1954 unsigned int hash, start, end, i;
1955 WCHAR ch[3];
1957 hash = (ch1 + 95 * ch2) % norm_info->comp_size;
1958 start = table[hash];
1959 end = table[hash + 1];
1960 while (start < end)
1962 for (i = 0; i < 3; i++, start++)
1964 ch[i] = chars[start];
1965 if (IS_HIGH_SURROGATE( ch[i] )) start++;
1967 if (ch[0] == ch1 && ch[1] == ch2) return ch[2];
1969 return 0;
1973 static UINT get_locale_codepage( const NLS_LOCALE_DATA *locale, ULONG flags )
1975 UINT ret = locale->idefaultansicodepage;
1976 if ((flags & LOCALE_USE_CP_ACP) || ret == CP_UTF8) ret = nls_info.AnsiTableInfo.CodePage;
1977 return ret;
1981 static UINT get_lcid_codepage( LCID lcid, ULONG flags )
1983 UINT ret = nls_info.AnsiTableInfo.CodePage;
1985 if (!(flags & LOCALE_USE_CP_ACP) && lcid != system_lcid)
1987 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
1988 if (locale) ret = locale->idefaultansicodepage;
1990 return ret;
1994 static const CPTABLEINFO *get_codepage_table( UINT codepage )
1996 static const CPTABLEINFO utf7_cpinfo = { CP_UTF7, 5, '?', 0xfffd, '?', '?' };
1997 static const CPTABLEINFO utf8_cpinfo = { CP_UTF8, 4, '?', 0xfffd, '?', '?' };
1998 unsigned int i;
1999 USHORT *ptr;
2000 SIZE_T size;
2002 switch (codepage)
2004 case CP_ACP:
2005 return &nls_info.AnsiTableInfo;
2006 case CP_OEMCP:
2007 return &nls_info.OemTableInfo;
2008 case CP_MACCP:
2009 codepage = system_locale->idefaultmaccodepage;
2010 break;
2011 case CP_THREAD_ACP:
2012 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 );
2013 break;
2015 if (codepage == nls_info.AnsiTableInfo.CodePage) return &nls_info.AnsiTableInfo;
2016 if (codepage == nls_info.OemTableInfo.CodePage) return &nls_info.OemTableInfo;
2017 if (codepage == CP_UTF8) return &utf8_cpinfo;
2018 if (codepage == CP_UTF7) return &utf7_cpinfo;
2020 RtlEnterCriticalSection( &locale_section );
2022 for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done;
2024 if (i == ARRAY_SIZE( codepages ))
2026 RtlLeaveCriticalSection( &locale_section );
2027 ERR( "too many codepages\n" );
2028 return NULL;
2030 if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size ))
2032 RtlLeaveCriticalSection( &locale_section );
2033 SetLastError( ERROR_INVALID_PARAMETER );
2034 return NULL;
2036 RtlInitCodePageTable( ptr, &codepages[i] );
2037 nb_codepages++;
2038 done:
2039 RtlLeaveCriticalSection( &locale_section );
2040 return &codepages[i];
2044 static const WCHAR *get_ligature( WCHAR wc )
2046 int low = 0, high = ARRAY_SIZE( ligatures ) -1;
2047 while (low <= high)
2049 int pos = (low + high) / 2;
2050 if (ligatures[pos][0] < wc) low = pos + 1;
2051 else if (ligatures[pos][0] > wc) high = pos - 1;
2052 else return ligatures[pos] + 1;
2054 return NULL;
2058 static NTSTATUS expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2060 int i, len, pos = 0;
2061 NTSTATUS ret = STATUS_SUCCESS;
2062 const WCHAR *expand;
2064 for (i = 0; i < srclen; i++)
2066 if (!(expand = get_ligature( src[i] )))
2068 expand = src + i;
2069 len = 1;
2071 else len = lstrlenW( expand );
2073 if (*dstlen && ret == STATUS_SUCCESS)
2075 if (pos + len <= *dstlen) memcpy( dst + pos, expand, len * sizeof(WCHAR) );
2076 else ret = STATUS_BUFFER_TOO_SMALL;
2078 pos += len;
2080 *dstlen = pos;
2081 return ret;
2085 static NTSTATUS fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2087 int i, len = *dstlen;
2089 *dstlen = srclen;
2090 if (!len) return STATUS_SUCCESS;
2091 if (srclen > len) return STATUS_BUFFER_TOO_SMALL;
2092 for (i = 0; i < srclen; i++) dst[i] = casemap( charmaps[CHARMAP_FOLDDIGITS], src[i] );
2093 return STATUS_SUCCESS;
2097 static NTSTATUS fold_string( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2099 NTSTATUS ret;
2100 WCHAR *tmp;
2102 switch (flags)
2104 case MAP_PRECOMPOSED:
2105 return RtlNormalizeString( NormalizationC, src, srclen, dst, dstlen );
2106 case MAP_FOLDCZONE:
2107 case MAP_PRECOMPOSED | MAP_FOLDCZONE:
2108 return RtlNormalizeString( NormalizationKC, src, srclen, dst, dstlen );
2109 case MAP_COMPOSITE:
2110 return RtlNormalizeString( NormalizationD, src, srclen, dst, dstlen );
2111 case MAP_COMPOSITE | MAP_FOLDCZONE:
2112 return RtlNormalizeString( NormalizationKD, src, srclen, dst, dstlen );
2113 case MAP_FOLDDIGITS:
2114 return fold_digits( src, srclen, dst, dstlen );
2115 case MAP_EXPAND_LIGATURES:
2116 case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
2117 return expand_ligatures( src, srclen, dst, dstlen );
2118 case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
2119 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2120 return STATUS_NO_MEMORY;
2121 fold_digits( src, srclen, tmp, &srclen );
2122 ret = RtlNormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
2123 break;
2124 case MAP_FOLDDIGITS | MAP_FOLDCZONE:
2125 case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
2126 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2127 return STATUS_NO_MEMORY;
2128 fold_digits( src, srclen, tmp, &srclen );
2129 ret = RtlNormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
2130 break;
2131 case MAP_FOLDDIGITS | MAP_COMPOSITE:
2132 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2133 return STATUS_NO_MEMORY;
2134 fold_digits( src, srclen, tmp, &srclen );
2135 ret = RtlNormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
2136 break;
2137 case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
2138 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2139 return STATUS_NO_MEMORY;
2140 fold_digits( src, srclen, tmp, &srclen );
2141 ret = RtlNormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
2142 break;
2143 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
2144 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
2145 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2146 return STATUS_NO_MEMORY;
2147 fold_digits( src, srclen, tmp, &srclen );
2148 ret = expand_ligatures( tmp, srclen, dst, dstlen );
2149 break;
2150 default:
2151 return STATUS_INVALID_PARAMETER_1;
2153 RtlFreeHeap( GetProcessHeap(), 0, tmp );
2154 return ret;
2158 static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2160 int len, i;
2162 if (flags)
2164 SetLastError( ERROR_INVALID_FLAGS );
2165 return 0;
2167 if (!dstlen) return srclen;
2168 len = min( srclen, dstlen );
2169 for (i = 0; i < len; i++)
2171 unsigned char c = src[i];
2172 dst[i] = (c < 0x20) ? c : c + 0xf000;
2174 if (len < srclen)
2176 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2177 return 0;
2179 return len;
2183 static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2185 static const signed char base64_decoding_table[] =
2187 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2188 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2189 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2190 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2191 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2192 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2193 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2194 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2197 const char *source_end = src + srclen;
2198 int offset = 0, pos = 0;
2199 DWORD byte_pair = 0;
2201 if (flags)
2203 SetLastError( ERROR_INVALID_FLAGS );
2204 return 0;
2206 #define OUTPUT(ch) \
2207 do { \
2208 if (dstlen > 0) \
2210 if (pos >= dstlen) goto overflow; \
2211 dst[pos] = (ch); \
2213 pos++; \
2214 } while(0)
2216 while (src < source_end)
2218 if (*src == '+')
2220 src++;
2221 if (src >= source_end) break;
2222 if (*src == '-')
2224 /* just a plus sign escaped as +- */
2225 OUTPUT( '+' );
2226 src++;
2227 continue;
2232 signed char sextet = *src;
2233 if (sextet == '-')
2235 /* skip over the dash and end base64 decoding
2236 * the current, unfinished byte pair is discarded */
2237 src++;
2238 offset = 0;
2239 break;
2241 if (sextet < 0)
2243 /* the next character of src is < 0 and therefore not part of a base64 sequence
2244 * the current, unfinished byte pair is NOT discarded in this case
2245 * this is probably a bug in Windows */
2246 break;
2248 sextet = base64_decoding_table[sextet];
2249 if (sextet == -1)
2251 /* -1 means that the next character of src is not part of a base64 sequence
2252 * in other words, all sextets in this base64 sequence have been processed
2253 * the current, unfinished byte pair is discarded */
2254 offset = 0;
2255 break;
2258 byte_pair = (byte_pair << 6) | sextet;
2259 offset += 6;
2260 if (offset >= 16)
2262 /* this byte pair is done */
2263 OUTPUT( byte_pair >> (offset - 16) );
2264 offset -= 16;
2266 src++;
2268 while (src < source_end);
2270 else
2272 OUTPUT( (unsigned char)*src );
2273 src++;
2276 return pos;
2278 overflow:
2279 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2280 return 0;
2281 #undef OUTPUT
2285 static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2287 DWORD reslen;
2288 NTSTATUS status;
2290 if (!dstlen) dst = NULL;
2291 status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen );
2292 if (status == STATUS_SOME_NOT_MAPPED)
2294 if (flags & MB_ERR_INVALID_CHARS)
2296 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2297 return 0;
2300 else if (!set_ntstatus( status )) reslen = 0;
2302 return reslen / sizeof(WCHAR);
2306 static inline int is_private_use_area_char( WCHAR code )
2308 return (code >= 0xe000 && code <= 0xf8ff);
2312 static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen )
2314 if (info->DBCSOffsets)
2316 for ( ; srclen; src++, srclen-- )
2318 USHORT off = info->DBCSOffsets[*src];
2319 if (off)
2321 if (srclen == 1) break; /* partial char, error */
2322 if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar &&
2323 ((src[0] << 8) | src[1]) != info->TransUniDefaultChar) break;
2324 src++;
2325 srclen--;
2326 continue;
2328 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2329 break;
2330 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2333 else
2335 for ( ; srclen; src++, srclen-- )
2337 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2338 break;
2339 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2342 return !!srclen;
2347 static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2348 WCHAR *dst, int dstlen )
2350 WCHAR ch;
2351 USHORT off;
2352 int len;
2353 const WCHAR *decomp;
2354 unsigned int decomp_len;
2356 if (info->DBCSOffsets)
2358 if (!dstlen) /* compute length */
2360 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2362 if ((off = info->DBCSOffsets[*src]))
2364 if (srclen > 1 && src[1])
2366 src++;
2367 srclen--;
2368 ch = info->DBCSOffsets[off + *src];
2370 else ch = info->UniDefaultChar;
2372 else ch = info->MultiByteTable[*src];
2373 get_decomposition( ch, &decomp_len );
2375 return len;
2378 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2380 if ((off = info->DBCSOffsets[*src]))
2382 if (srclen > 1 && src[1])
2384 src++;
2385 srclen--;
2386 ch = info->DBCSOffsets[off + *src];
2388 else ch = info->UniDefaultChar;
2390 else ch = info->MultiByteTable[*src];
2392 if ((decomp = get_decomposition( ch, &decomp_len )))
2394 if (len < decomp_len) break;
2395 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2397 else *dst = ch;
2400 else
2402 if (!dstlen) /* compute length */
2404 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2405 get_decomposition( info->MultiByteTable[*src], &decomp_len );
2406 return len;
2409 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2411 ch = info->MultiByteTable[*src];
2412 if ((decomp = get_decomposition( ch, &decomp_len )))
2414 if (len < decomp_len) break;
2415 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2417 else *dst = ch;
2421 if (srclen)
2423 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2424 return 0;
2426 return dstlen - len;
2430 static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2431 WCHAR *dst, int dstlen )
2433 const USHORT *table = info->MultiByteTable;
2434 int ret = srclen;
2436 if (!dstlen) return srclen;
2438 if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */
2440 srclen = dstlen;
2441 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2442 ret = 0;
2445 while (srclen >= 16)
2447 dst[0] = table[src[0]];
2448 dst[1] = table[src[1]];
2449 dst[2] = table[src[2]];
2450 dst[3] = table[src[3]];
2451 dst[4] = table[src[4]];
2452 dst[5] = table[src[5]];
2453 dst[6] = table[src[6]];
2454 dst[7] = table[src[7]];
2455 dst[8] = table[src[8]];
2456 dst[9] = table[src[9]];
2457 dst[10] = table[src[10]];
2458 dst[11] = table[src[11]];
2459 dst[12] = table[src[12]];
2460 dst[13] = table[src[13]];
2461 dst[14] = table[src[14]];
2462 dst[15] = table[src[15]];
2463 src += 16;
2464 dst += 16;
2465 srclen -= 16;
2468 /* now handle the remaining characters */
2469 src += srclen;
2470 dst += srclen;
2471 switch (srclen)
2473 case 15: dst[-15] = table[src[-15]];
2474 case 14: dst[-14] = table[src[-14]];
2475 case 13: dst[-13] = table[src[-13]];
2476 case 12: dst[-12] = table[src[-12]];
2477 case 11: dst[-11] = table[src[-11]];
2478 case 10: dst[-10] = table[src[-10]];
2479 case 9: dst[-9] = table[src[-9]];
2480 case 8: dst[-8] = table[src[-8]];
2481 case 7: dst[-7] = table[src[-7]];
2482 case 6: dst[-6] = table[src[-6]];
2483 case 5: dst[-5] = table[src[-5]];
2484 case 4: dst[-4] = table[src[-4]];
2485 case 3: dst[-3] = table[src[-3]];
2486 case 2: dst[-2] = table[src[-2]];
2487 case 1: dst[-1] = table[src[-1]];
2488 case 0: break;
2490 return ret;
2494 static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2495 WCHAR *dst, int dstlen )
2497 USHORT off;
2498 int i;
2500 if (!dstlen)
2502 for (i = 0; srclen; i++, src++, srclen--)
2503 if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; }
2504 return i;
2507 for (i = dstlen; srclen && i; i--, srclen--, src++, dst++)
2509 if ((off = info->DBCSOffsets[*src]))
2511 if (srclen > 1 && src[1])
2513 src++;
2514 srclen--;
2515 *dst = info->DBCSOffsets[off + *src];
2517 else *dst = info->UniDefaultChar;
2519 else *dst = info->MultiByteTable[*src];
2521 if (srclen)
2523 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2524 return 0;
2526 return dstlen - i;
2530 static int mbstowcs_codepage( const CPTABLEINFO *info, DWORD flags, const char *src, int srclen,
2531 WCHAR *dst, int dstlen )
2533 CPTABLEINFO local_info;
2534 const unsigned char *str = (const unsigned char *)src;
2536 if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256)
2538 local_info = *info;
2539 local_info.MultiByteTable += 257;
2540 info = &local_info;
2542 if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen ))
2544 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2545 return 0;
2548 if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen );
2550 if (info->DBCSOffsets)
2551 return mbstowcs_dbcs( info, str, srclen, dst, dstlen );
2552 else
2553 return mbstowcs_sbcs( info, str, srclen, dst, dstlen );
2557 static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2558 const char *defchar, BOOL *used )
2560 int len, i;
2562 if (flags)
2564 SetLastError( ERROR_INVALID_FLAGS );
2565 return 0;
2567 if (defchar || used)
2569 SetLastError( ERROR_INVALID_PARAMETER );
2570 return 0;
2572 if (!dstlen) return srclen;
2573 len = min( srclen, dstlen );
2574 for (i = 0; i < len; i++)
2576 if (src[i] < 0x20) dst[i] = src[i];
2577 else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000;
2578 else
2580 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2581 return 0;
2584 if (srclen > len)
2586 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2587 return 0;
2589 return len;
2593 static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2594 const char *defchar, BOOL *used )
2596 static const char directly_encodable[] =
2598 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
2599 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
2600 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
2601 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
2602 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
2603 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
2604 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
2605 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
2607 #define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
2609 static const char base64_encoding_table[] =
2610 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2612 const WCHAR *source_end = src + srclen;
2613 int pos = 0;
2615 if (defchar || used)
2617 SetLastError( ERROR_INVALID_PARAMETER );
2618 return 0;
2620 if (flags)
2622 SetLastError( ERROR_INVALID_FLAGS );
2623 return 0;
2626 #define OUTPUT(ch) \
2627 do { \
2628 if (dstlen > 0) \
2630 if (pos >= dstlen) goto overflow; \
2631 dst[pos] = (ch); \
2633 pos++; \
2634 } while (0)
2636 while (src < source_end)
2638 if (*src == '+')
2640 OUTPUT( '+' );
2641 OUTPUT( '-' );
2642 src++;
2644 else if (ENCODABLE(*src))
2646 OUTPUT( *src );
2647 src++;
2649 else
2651 unsigned int offset = 0, byte_pair = 0;
2653 OUTPUT( '+' );
2654 while (src < source_end && !ENCODABLE(*src))
2656 byte_pair = (byte_pair << 16) | *src;
2657 offset += 16;
2658 while (offset >= 6)
2660 offset -= 6;
2661 OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] );
2663 src++;
2665 if (offset)
2667 /* Windows won't create a padded base64 character if there's no room for the - sign
2668 * as well ; this is probably a bug in Windows */
2669 if (dstlen > 0 && pos + 1 >= dstlen) goto overflow;
2670 byte_pair <<= (6 - offset);
2671 OUTPUT( base64_encoding_table[byte_pair & 0x3f] );
2673 /* Windows always explicitly terminates the base64 sequence
2674 even though RFC 2152 (page 3, rule 2) does not require this */
2675 OUTPUT( '-' );
2678 return pos;
2680 overflow:
2681 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2682 return 0;
2683 #undef OUTPUT
2684 #undef ENCODABLE
2688 static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2689 const char *defchar, BOOL *used )
2691 DWORD reslen;
2692 NTSTATUS status;
2694 if (used) *used = FALSE;
2695 if (!dstlen) dst = NULL;
2696 status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
2697 if (status == STATUS_SOME_NOT_MAPPED)
2699 if (flags & WC_ERR_INVALID_CHARS)
2701 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2702 return 0;
2704 if (used) *used = TRUE;
2706 else if (!set_ntstatus( status )) reslen = 0;
2707 return reslen;
2711 static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2712 char *dst, unsigned int dstlen )
2714 const char *table = info->WideCharTable;
2715 int ret = srclen;
2717 if (!dstlen) return srclen;
2719 if (dstlen < srclen)
2721 /* buffer too small: fill it up to dstlen and return error */
2722 srclen = dstlen;
2723 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2724 ret = 0;
2727 while (srclen >= 16)
2729 dst[0] = table[src[0]];
2730 dst[1] = table[src[1]];
2731 dst[2] = table[src[2]];
2732 dst[3] = table[src[3]];
2733 dst[4] = table[src[4]];
2734 dst[5] = table[src[5]];
2735 dst[6] = table[src[6]];
2736 dst[7] = table[src[7]];
2737 dst[8] = table[src[8]];
2738 dst[9] = table[src[9]];
2739 dst[10] = table[src[10]];
2740 dst[11] = table[src[11]];
2741 dst[12] = table[src[12]];
2742 dst[13] = table[src[13]];
2743 dst[14] = table[src[14]];
2744 dst[15] = table[src[15]];
2745 src += 16;
2746 dst += 16;
2747 srclen -= 16;
2750 /* now handle remaining characters */
2751 src += srclen;
2752 dst += srclen;
2753 switch(srclen)
2755 case 15: dst[-15] = table[src[-15]];
2756 case 14: dst[-14] = table[src[-14]];
2757 case 13: dst[-13] = table[src[-13]];
2758 case 12: dst[-12] = table[src[-12]];
2759 case 11: dst[-11] = table[src[-11]];
2760 case 10: dst[-10] = table[src[-10]];
2761 case 9: dst[-9] = table[src[-9]];
2762 case 8: dst[-8] = table[src[-8]];
2763 case 7: dst[-7] = table[src[-7]];
2764 case 6: dst[-6] = table[src[-6]];
2765 case 5: dst[-5] = table[src[-5]];
2766 case 4: dst[-4] = table[src[-4]];
2767 case 3: dst[-3] = table[src[-3]];
2768 case 2: dst[-2] = table[src[-2]];
2769 case 1: dst[-1] = table[src[-1]];
2770 case 0: break;
2772 return ret;
2776 static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2777 char *dst, unsigned int dstlen )
2779 const USHORT *table = info->WideCharTable;
2780 int i;
2782 if (!dstlen)
2784 for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++;
2785 return i;
2788 for (i = dstlen; srclen && i; i--, srclen--, src++)
2790 if (table[*src] & 0xff00)
2792 if (i == 1) break; /* do not output a partial char */
2793 i--;
2794 *dst++ = table[*src] >> 8;
2796 *dst++ = (char)table[*src];
2798 if (srclen)
2800 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2801 return 0;
2803 return dstlen - i;
2807 static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2809 const unsigned char *table = info->WideCharTable;
2811 if (wch >= 0x10000) return 0;
2812 if ((flags & WC_NO_BEST_FIT_CHARS) || table[wch] == info->DefaultChar)
2813 return (info->MultiByteTable[table[wch]] == wch);
2814 return 1;
2818 static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2820 const unsigned short *table = info->WideCharTable;
2821 unsigned short ch;
2823 if (wch >= 0x10000) return 0;
2824 ch = table[wch];
2825 if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
2827 if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch;
2828 return info->MultiByteTable[ch] == wch;
2830 return 1;
2834 static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2835 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2837 const char *table = info->WideCharTable;
2838 const char def = defchar ? *defchar : (char)info->DefaultChar;
2839 int i;
2840 BOOL tmp;
2841 WCHAR wch;
2842 unsigned int composed;
2844 if (!used) used = &tmp; /* avoid checking on every char */
2845 *used = FALSE;
2847 if (!dstlen)
2849 for (i = 0; srclen; i++, src++, srclen--)
2851 wch = *src;
2852 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2854 /* now check if we can use the composed char */
2855 if (is_valid_sbcs_mapping( info, flags, composed ))
2857 /* we have a good mapping, use it */
2858 src++;
2859 srclen--;
2860 continue;
2862 /* no mapping for the composed char, check the other flags */
2863 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2865 *used = TRUE;
2866 src++; /* skip the non-spacing char */
2867 srclen--;
2868 continue;
2870 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2872 src++;
2873 srclen--;
2875 /* WC_SEPCHARS is the default */
2877 if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch );
2879 return i;
2882 for (i = dstlen; srclen && i; dst++, i--, src++, srclen--)
2884 wch = *src;
2885 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2887 /* now check if we can use the composed char */
2888 if (is_valid_sbcs_mapping( info, flags, composed ))
2890 /* we have a good mapping, use it */
2891 *dst = table[composed];
2892 src++;
2893 srclen--;
2894 continue;
2896 /* no mapping for the composed char, check the other flags */
2897 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2899 *dst = def;
2900 *used = TRUE;
2901 src++; /* skip the non-spacing char */
2902 srclen--;
2903 continue;
2905 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2907 src++;
2908 srclen--;
2910 /* WC_SEPCHARS is the default */
2913 *dst = table[wch];
2914 if (!is_valid_sbcs_mapping( info, flags, wch ))
2916 *dst = def;
2917 *used = TRUE;
2920 if (srclen)
2922 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2923 return 0;
2925 return dstlen - i;
2929 static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2930 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2932 const USHORT *table = info->WideCharTable;
2933 WCHAR wch, defchar_value;
2934 unsigned int composed;
2935 unsigned short res;
2936 BOOL tmp;
2937 int i;
2939 if (!defchar[1]) defchar_value = (unsigned char)defchar[0];
2940 else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1];
2942 if (!used) used = &tmp; /* avoid checking on every char */
2943 *used = FALSE;
2945 if (!dstlen)
2947 if (!defchar && !used && !(flags & WC_COMPOSITECHECK))
2949 for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++;
2950 return i;
2952 for (i = 0; srclen; srclen--, src++, i++)
2954 wch = *src;
2955 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2957 /* now check if we can use the composed char */
2958 if (is_valid_dbcs_mapping( info, flags, composed ))
2960 /* we have a good mapping for the composed char, use it */
2961 res = table[composed];
2962 if (res & 0xff00) i++;
2963 src++;
2964 srclen--;
2965 continue;
2967 /* no mapping for the composed char, check the other flags */
2968 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2970 if (defchar_value & 0xff00) i++;
2971 *used = TRUE;
2972 src++; /* skip the non-spacing char */
2973 srclen--;
2974 continue;
2976 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2978 src++;
2979 srclen--;
2981 /* WC_SEPCHARS is the default */
2984 res = table[wch];
2985 if (!is_valid_dbcs_mapping( info, flags, wch ))
2987 res = defchar_value;
2988 *used = TRUE;
2990 if (res & 0xff00) i++;
2992 return i;
2996 for (i = dstlen; srclen && i; i--, srclen--, src++)
2998 wch = *src;
2999 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
3001 /* now check if we can use the composed char */
3002 if (is_valid_dbcs_mapping( info, flags, composed ))
3004 /* we have a good mapping for the composed char, use it */
3005 res = table[composed];
3006 src++;
3007 srclen--;
3008 goto output_char;
3010 /* no mapping for the composed char, check the other flags */
3011 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
3013 res = defchar_value;
3014 *used = TRUE;
3015 src++; /* skip the non-spacing char */
3016 srclen--;
3017 goto output_char;
3019 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
3021 src++;
3022 srclen--;
3024 /* WC_SEPCHARS is the default */
3027 res = table[wch];
3028 if (!is_valid_dbcs_mapping( info, flags, wch ))
3030 res = defchar_value;
3031 *used = TRUE;
3034 output_char:
3035 if (res & 0xff00)
3037 if (i == 1) break; /* do not output a partial char */
3038 i--;
3039 *dst++ = res >> 8;
3041 *dst++ = (char)res;
3043 if (srclen)
3045 SetLastError( ERROR_INSUFFICIENT_BUFFER );
3046 return 0;
3048 return dstlen - i;
3052 static int wcstombs_codepage( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, int srclen,
3053 char *dst, int dstlen, const char *defchar, BOOL *used )
3055 if (flags || defchar || used)
3057 if (!defchar) defchar = (const char *)&info->DefaultChar;
3058 if (info->DBCSOffsets)
3059 return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
3060 else
3061 return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
3063 if (info->DBCSOffsets)
3064 return wcstombs_dbcs( info, src, srclen, dst, dstlen );
3065 else
3066 return wcstombs_sbcs( info, src, srclen, dst, dstlen );
3070 static int get_sortkey( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen )
3072 WCHAR dummy[4]; /* no decomposition is larger than 4 chars */
3073 int key_len[4];
3074 char *key_ptr[4];
3075 const WCHAR *src_save = src;
3076 int srclen_save = srclen;
3078 key_len[0] = key_len[1] = key_len[2] = key_len[3] = 0;
3079 for (; srclen; srclen--, src++)
3081 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
3082 dummy[0] = *src;
3083 if (decomposed_len)
3085 for (i = 0; i < decomposed_len; i++)
3087 WCHAR wch = dummy[i];
3088 unsigned int ce;
3090 if ((flags & NORM_IGNORESYMBOLS) &&
3091 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
3092 continue;
3094 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
3096 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
3097 if (ce != (unsigned int)-1)
3099 if (ce >> 16) key_len[0] += 2;
3100 if ((ce >> 8) & 0xff) key_len[1]++;
3101 if ((ce >> 4) & 0x0f) key_len[2]++;
3102 if (ce & 1)
3104 if (wch >> 8) key_len[3]++;
3105 key_len[3]++;
3108 else
3110 key_len[0] += 2;
3111 if (wch >> 8) key_len[0]++;
3112 if (wch & 0xff) key_len[0]++;
3118 if (!dstlen) /* compute length */
3119 /* 4 * '\1' + key length */
3120 return key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4;
3122 if (dstlen < key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4 + 1)
3123 return 0; /* overflow */
3125 src = src_save;
3126 srclen = srclen_save;
3128 key_ptr[0] = dst;
3129 key_ptr[1] = key_ptr[0] + key_len[0] + 1;
3130 key_ptr[2] = key_ptr[1] + key_len[1] + 1;
3131 key_ptr[3] = key_ptr[2] + key_len[2] + 1;
3133 for (; srclen; srclen--, src++)
3135 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
3136 dummy[0] = *src;
3137 if (decomposed_len)
3139 for (i = 0; i < decomposed_len; i++)
3141 WCHAR wch = dummy[i];
3142 unsigned int ce;
3144 if ((flags & NORM_IGNORESYMBOLS) &&
3145 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
3146 continue;
3148 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
3150 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
3151 if (ce != (unsigned int)-1)
3153 WCHAR key;
3154 if ((key = ce >> 16))
3156 *key_ptr[0]++ = key >> 8;
3157 *key_ptr[0]++ = key & 0xff;
3159 /* make key 1 start from 2 */
3160 if ((key = (ce >> 8) & 0xff)) *key_ptr[1]++ = key + 1;
3161 /* make key 2 start from 2 */
3162 if ((key = (ce >> 4) & 0x0f)) *key_ptr[2]++ = key + 1;
3163 /* key 3 is always a character code */
3164 if (ce & 1)
3166 if (wch >> 8) *key_ptr[3]++ = wch >> 8;
3167 if (wch & 0xff) *key_ptr[3]++ = wch & 0xff;
3170 else
3172 *key_ptr[0]++ = 0xff;
3173 *key_ptr[0]++ = 0xfe;
3174 if (wch >> 8) *key_ptr[0]++ = wch >> 8;
3175 if (wch & 0xff) *key_ptr[0]++ = wch & 0xff;
3181 *key_ptr[0] = 1;
3182 *key_ptr[1] = 1;
3183 *key_ptr[2] = 1;
3184 *key_ptr[3]++ = 1;
3185 *key_ptr[3] = 0;
3186 return key_ptr[3] - dst;
3190 /* compose a full-width katakana. return consumed source characters. */
3191 static int map_to_fullwidth( const WCHAR *src, int srclen, WCHAR *dst )
3193 *dst = casemap( charmaps[CHARMAP_FULLWIDTH], *src );
3194 if (srclen <= 1) return 1;
3196 switch (src[1])
3198 case 0xff9e: /* datakuten (voiced sound) */
3199 if ((*src >= 0xff76 && *src <= 0xff84) || (*src >= 0xff8a && *src <= 0xff8e) || *src == 0x30fd)
3200 *dst += 1;
3201 else if (*src == 0xff73)
3202 *dst = 0x30f4; /* KATAKANA LETTER VU */
3203 else if (*src == 0xff9c)
3204 *dst = 0x30f7; /* KATAKANA LETTER VA */
3205 else if (*src == 0x30f0)
3206 *dst = 0x30f8; /* KATAKANA LETTER VI */
3207 else if (*src == 0x30f1)
3208 *dst = 0x30f9; /* KATAKANA LETTER VE */
3209 else if (*src == 0xff66)
3210 *dst = 0x30fa; /* KATAKANA LETTER VO */
3211 else
3212 return 1;
3213 break;
3214 case 0xff9f: /* handakuten (semi-voiced sound) */
3215 if (*src >= 0xff8a && *src <= 0xff8e)
3216 *dst += 2;
3217 else
3218 return 1;
3219 break;
3220 default:
3221 return 1;
3223 return 2;
3226 /* map single full-width character to single or double half-width characters. */
3227 static int map_to_halfwidth( WCHAR c, WCHAR *dst, int dstlen )
3229 static const BYTE katakana_map[] =
3231 0x01, 0x00, 0x01, 0x00, /* U+30a8- */
3232 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b0- */
3233 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b8- */
3234 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, /* U+30c0- */
3235 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30c8- */
3236 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, /* U+30d0- */
3237 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x00, /* U+30d8- */
3238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e0- */
3239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e8- */
3240 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3241 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x01 /* U+30f8- */
3243 USHORT shift = c - 0x30ac;
3244 BYTE k;
3246 if (shift < ARRAY_SIZE(katakana_map) && (k = katakana_map[shift]))
3248 if (dstlen >= 2)
3250 dst[0] = casemap( charmaps[CHARMAP_HALFWIDTH], c - k );
3251 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3253 return 2;
3255 if (dstlen >= 1) dst[0] = casemap( charmaps[CHARMAP_HALFWIDTH], c );
3256 return 1;
3260 /* 32-bit collation element table format:
3261 * unicode weight - high 16 bit, diacritic weight - high 8 bit of low 16 bit,
3262 * case weight - high 4 bit of low 8 bit.
3265 enum weight { UNICODE_WEIGHT, DIACRITIC_WEIGHT, CASE_WEIGHT };
3267 static unsigned int get_weight( WCHAR ch, enum weight type )
3269 unsigned int ret;
3271 ret = collation_table[collation_table[collation_table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
3272 if (ret == ~0u) return ch;
3274 switch (type)
3276 case UNICODE_WEIGHT: return ret >> 16;
3277 case DIACRITIC_WEIGHT: return (ret >> 8) & 0xff;
3278 case CASE_WEIGHT: return (ret >> 4) & 0x0f;
3279 default: return 0;
3284 static void inc_str_pos( const WCHAR **str, int *len, unsigned int *dpos, unsigned int *dlen )
3286 (*dpos)++;
3287 if (*dpos == *dlen)
3289 *dpos = *dlen = 0;
3290 (*str)++;
3291 (*len)--;
3296 static int compare_weights(int flags, const WCHAR *str1, int len1,
3297 const WCHAR *str2, int len2, enum weight type )
3299 unsigned int ce1, ce2, dpos1 = 0, dpos2 = 0, dlen1 = 0, dlen2 = 0;
3300 const WCHAR *dstr1 = NULL, *dstr2 = NULL;
3302 while (len1 > 0 && len2 > 0)
3304 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3305 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3307 if (flags & NORM_IGNORESYMBOLS)
3309 int skip = 0;
3310 /* FIXME: not tested */
3311 if (get_char_type( CT_CTYPE1, dstr1[dpos1] ) & (C1_PUNCT | C1_SPACE))
3313 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3314 skip = 1;
3316 if (get_char_type( CT_CTYPE1, dstr2[dpos2] ) & (C1_PUNCT | C1_SPACE))
3318 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3319 skip = 1;
3321 if (skip) continue;
3324 /* hyphen and apostrophe are treated differently depending on
3325 * whether SORT_STRINGSORT specified or not
3327 if (type == UNICODE_WEIGHT && !(flags & SORT_STRINGSORT))
3329 if (dstr1[dpos1] == '-' || dstr1[dpos1] == '\'')
3331 if (dstr2[dpos2] != '-' && dstr2[dpos2] != '\'')
3333 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3334 continue;
3337 else if (dstr2[dpos2] == '-' || dstr2[dpos2] == '\'')
3339 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3340 continue;
3344 ce1 = get_weight( dstr1[dpos1], type );
3345 if (!ce1)
3347 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3348 continue;
3350 ce2 = get_weight( dstr2[dpos2], type );
3351 if (!ce2)
3353 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3354 continue;
3357 if (ce1 - ce2) return ce1 - ce2;
3359 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3360 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3362 while (len1)
3364 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3365 ce1 = get_weight( dstr1[dpos1], type );
3366 if (ce1) break;
3367 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3369 while (len2)
3371 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3372 ce2 = get_weight( dstr2[dpos2], type );
3373 if (ce2) break;
3374 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3376 return len1 - len2;
3380 static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare )
3382 static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
3383 int first, last, limit, dayinsecs;
3385 if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */
3386 if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */
3388 /* if year is 0 then date is in day-of-week format, otherwise
3389 * it's absolute date.
3391 if (!compare->wYear)
3393 /* wDay is interpreted as number of the week in the month
3394 * 5 means: the last week in the month */
3395 /* calculate the day of the first DayOfWeek in the month */
3396 first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1;
3397 /* check needed for the 5th weekday of the month */
3398 last = month_lengths[tf->Month - 1] +
3399 (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400))));
3400 limit = first + 7 * (compare->wDay - 1);
3401 if (limit > last) limit -= 7;
3403 else limit = compare->wDay;
3405 limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60;
3406 dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second;
3407 return dayinsecs - limit;
3411 static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local )
3413 int year;
3414 BOOL before_standard_date, after_daylight_date;
3415 LARGE_INTEGER t2;
3416 TIME_FIELDS tf;
3418 if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN;
3420 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
3421 if (info->StandardDate.wMonth == 0 ||
3422 (info->StandardDate.wYear == 0 &&
3423 (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 ||
3424 info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5)))
3426 SetLastError( ERROR_INVALID_PARAMETER );
3427 return TIME_ZONE_ID_INVALID;
3430 if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000;
3431 RtlTimeToTimeFields( &time, &tf );
3432 year = tf.Year;
3433 if (!is_local)
3435 t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000;
3436 RtlTimeToTimeFields( &t2, &tf );
3438 if (tf.Year == year)
3439 before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0;
3440 else
3441 before_standard_date = tf.Year < year;
3443 if (!is_local)
3445 t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000;
3446 RtlTimeToTimeFields( &t2, &tf );
3448 if (tf.Year == year)
3449 after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0;
3450 else
3451 after_daylight_date = tf.Year > year;
3453 if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */
3455 if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3457 else /* Down south */
3459 if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3461 return TIME_ZONE_ID_STANDARD;
3465 /* Note: the Internal_ functions are not documented. The number of parameters
3466 * should be correct, but their exact meaning may not.
3469 /******************************************************************************
3470 * Internal_EnumCalendarInfo (kernelbase.@)
3472 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc,
3473 const NLS_LOCALE_DATA *locale, CALID id,
3474 CALTYPE type, BOOL unicode, BOOL ex,
3475 BOOL exex, LPARAM lparam )
3477 const USHORT *calendars;
3478 USHORT cal = id;
3479 WCHAR buffer[256];
3480 INT ret, i, count = 1;
3482 if (!proc || !locale)
3484 SetLastError( ERROR_INVALID_PARAMETER );
3485 return FALSE;
3488 if (id == ENUM_ALL_CALENDARS)
3490 count = locale_strings[locale->scalendartype];
3491 calendars = locale_strings + locale->scalendartype + 1;
3493 else if (id <= CAL_UMALQURA)
3495 calendars = &cal;
3496 count = 1;
3498 else
3500 SetLastError( ERROR_INVALID_PARAMETER );
3501 return FALSE;
3504 for (i = 0; i < count; i++)
3506 id = calendars[i];
3507 if (type & CAL_RETURN_NUMBER)
3508 ret = get_calendar_info( locale, id, type, NULL, 0, (LPDWORD)buffer );
3509 else if (unicode)
3510 ret = get_calendar_info( locale, id, type, buffer, ARRAY_SIZE(buffer), NULL );
3511 else
3513 WCHAR bufW[256];
3514 ret = get_calendar_info( locale, id, type, bufW, ARRAY_SIZE(bufW), NULL );
3515 if (ret) WideCharToMultiByte( get_locale_codepage( locale, type ), 0,
3516 bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
3519 if (ret)
3521 if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
3522 else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
3523 else ret = proc( buffer );
3525 if (!ret) break;
3527 return TRUE;
3531 static BOOL call_enum_date_func( DATEFMT_ENUMPROCW proc, const NLS_LOCALE_DATA *locale, DWORD flags,
3532 DWORD str, WCHAR *buffer, CALID id, BOOL unicode,
3533 BOOL ex, BOOL exex, LPARAM lparam )
3535 char buffA[256];
3537 if (str) memcpy( buffer, locale_strings + str + 1, (locale_strings[str] + 1) * sizeof(WCHAR) );
3538 if (exex) return ((DATEFMT_ENUMPROCEXEX)proc)( buffer, id, lparam );
3539 if (ex) return ((DATEFMT_ENUMPROCEXW)proc)( buffer, id );
3540 if (unicode) return proc( buffer );
3541 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
3542 buffA, ARRAY_SIZE(buffA), NULL, NULL );
3543 return proc( (WCHAR *)buffA );
3547 /**************************************************************************
3548 * Internal_EnumDateFormats (kernelbase.@)
3550 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc,
3551 const NLS_LOCALE_DATA *locale, DWORD flags,
3552 BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
3554 WCHAR buffer[256];
3555 INT i, j, ret;
3556 DWORD pos;
3557 const struct calendar *cal;
3558 const USHORT *calendars = locale_strings + locale->scalendartype;
3559 const DWORD *array;
3561 if (!proc || !locale)
3563 SetLastError( ERROR_INVALID_PARAMETER );
3564 return FALSE;
3567 switch (flags & ~LOCALE_USE_CP_ACP)
3569 case 0:
3570 case DATE_SHORTDATE:
3571 if (!get_locale_info( locale, 0, LOCALE_SSHORTDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3572 pos = locale->sshortdate;
3573 break;
3574 case DATE_LONGDATE:
3575 if (!get_locale_info( locale, 0, LOCALE_SLONGDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3576 pos = locale->slongdate;
3577 break;
3578 case DATE_YEARMONTH:
3579 if (!get_locale_info( locale, 0, LOCALE_SYEARMONTH, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3580 pos = locale->syearmonth;
3581 break;
3582 default:
3583 SetLastError( ERROR_INVALID_PARAMETER );
3584 return FALSE;
3587 /* first the user override data */
3589 ret = call_enum_date_func( proc, locale, flags, 0, buffer, 1, unicode, ex, exex, lparam );
3591 /* then the remaining locale data */
3593 array = (const DWORD *)(locale_strings + pos + 1);
3594 for (i = 1; ret && i < locale_strings[pos]; i++)
3595 ret = call_enum_date_func( proc, locale, flags, array[i], buffer, 1, unicode, ex, exex, lparam );
3597 /* then the extra calendars */
3599 for (i = 0; ret && i < calendars[0]; i++)
3601 if (calendars[i + 1] == 1) continue;
3602 if (!(cal = get_calendar_data( locale, calendars[i + 1] ))) continue;
3603 switch (flags & ~LOCALE_USE_CP_ACP)
3605 case 0:
3606 case DATE_SHORTDATE:
3607 pos = cal->sshortdate;
3608 break;
3609 case DATE_LONGDATE:
3610 pos = cal->slongdate;
3611 break;
3612 case DATE_YEARMONTH:
3613 pos = cal->syearmonth;
3614 break;
3616 array = (const DWORD *)(locale_strings + pos + 1);
3617 for (j = 0; ret && j < locale_strings[pos]; j++)
3618 ret = call_enum_date_func( proc, locale, flags, array[j], buffer,
3619 calendars[i + 1], unicode, ex, exex, lparam );
3621 return TRUE;
3625 /******************************************************************************
3626 * Internal_EnumLanguageGroupLocales (kernelbase.@)
3628 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
3629 DWORD flags, LONG_PTR param, BOOL unicode )
3631 WCHAR name[10], value[10];
3632 DWORD name_len, value_len, type, index = 0, alt = 0;
3633 HKEY key, altkey;
3634 LCID lcid;
3636 if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
3638 SetLastError( ERROR_INVALID_PARAMETER );
3639 return FALSE;
3642 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
3643 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
3645 for (;;)
3647 name_len = ARRAY_SIZE(name);
3648 value_len = sizeof(value);
3649 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
3650 &type, (BYTE *)value, &value_len ))
3652 if (alt++) break;
3653 index = 0;
3654 continue;
3656 if (type != REG_SZ) continue;
3657 if (id != wcstoul( value, NULL, 16 )) continue;
3658 lcid = wcstoul( name, NULL, 16 );
3659 if (!unicode)
3661 char nameA[10];
3662 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3663 if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
3665 else if (!proc( id, lcid, name, param )) break;
3667 RegCloseKey( altkey );
3668 RegCloseKey( key );
3669 return TRUE;
3673 /***********************************************************************
3674 * Internal_EnumSystemCodePages (kernelbase.@)
3676 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
3677 BOOL unicode )
3679 WCHAR name[10];
3680 DWORD name_len, type, index = 0;
3681 HKEY key;
3683 if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
3685 for (;;)
3687 name_len = ARRAY_SIZE(name);
3688 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
3689 if (type != REG_SZ) continue;
3690 if (!wcstoul( name, NULL, 10 )) continue;
3691 if (!unicode)
3693 char nameA[10];
3694 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3695 if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
3697 else if (!proc( name )) break;
3699 RegCloseKey( key );
3700 return TRUE;
3704 /******************************************************************************
3705 * Internal_EnumSystemLanguageGroups (kernelbase.@)
3707 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
3708 DWORD flags, LONG_PTR param, BOOL unicode )
3710 WCHAR name[10], value[10], descr[80];
3711 DWORD name_len, value_len, type, index = 0;
3712 HKEY key;
3713 LGRPID id;
3715 if (!proc)
3717 SetLastError( ERROR_INVALID_PARAMETER );
3718 return FALSE;
3721 switch (flags)
3723 case 0:
3724 flags = LGRPID_INSTALLED;
3725 break;
3726 case LGRPID_INSTALLED:
3727 case LGRPID_SUPPORTED:
3728 break;
3729 default:
3730 SetLastError( ERROR_INVALID_FLAGS );
3731 return FALSE;
3734 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
3736 for (;;)
3738 name_len = ARRAY_SIZE(name);
3739 value_len = sizeof(value);
3740 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
3741 if (type != REG_SZ) continue;
3743 id = wcstoul( name, NULL, 16 );
3745 if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
3746 if (!LoadStringW( kernelbase_handle, id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
3747 TRACE( "%p: %lu %s %s %lx %Ix\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
3748 if (!unicode)
3750 char nameA[10], descrA[80];
3751 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3752 WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
3753 if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
3755 else if (!proc( id, name, descr, flags, param )) break;
3757 RegCloseKey( key );
3758 return TRUE;
3762 /**************************************************************************
3763 * Internal_EnumTimeFormats (kernelbase.@)
3765 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc,
3766 const NLS_LOCALE_DATA *locale, DWORD flags,
3767 BOOL unicode, BOOL ex, LPARAM lparam )
3769 WCHAR buffer[256];
3770 INT ret = TRUE;
3771 const DWORD *array;
3772 DWORD pos, i;
3774 if (!proc || !locale)
3776 SetLastError( ERROR_INVALID_PARAMETER );
3777 return FALSE;
3779 switch (flags & ~LOCALE_USE_CP_ACP)
3781 case 0:
3782 if (!get_locale_info( locale, 0, LOCALE_STIMEFORMAT, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3783 pos = locale->stimeformat;
3784 break;
3785 case TIME_NOSECONDS:
3786 if (!get_locale_info( locale, 0, LOCALE_SSHORTTIME, buffer, ARRAY_SIZE(buffer) )) return FALSE;
3787 pos = locale->sshorttime;
3788 break;
3789 default:
3790 FIXME( "Unknown time format %lx\n", flags );
3791 SetLastError( ERROR_INVALID_PARAMETER );
3792 return FALSE;
3795 array = (const DWORD *)(locale_strings + pos + 1);
3796 for (i = 0; ret && i < locale_strings[pos]; i++)
3798 if (i) memcpy( buffer, locale_strings + array[i] + 1,
3799 (locale_strings[array[i]] + 1) * sizeof(WCHAR) );
3801 if (ex) ret = ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
3802 else if (unicode) ret = proc( buffer );
3803 else
3805 char buffA[256];
3806 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
3807 buffA, ARRAY_SIZE(buffA), NULL, NULL );
3808 ret = proc( (WCHAR *)buffA );
3811 return TRUE;
3815 /******************************************************************************
3816 * Internal_EnumUILanguages (kernelbase.@)
3818 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
3819 LONG_PTR param, BOOL unicode )
3821 WCHAR nameW[LOCALE_NAME_MAX_LENGTH];
3822 char nameA[LOCALE_NAME_MAX_LENGTH];
3823 DWORD i;
3825 if (!proc)
3827 SetLastError( ERROR_INVALID_PARAMETER );
3828 return FALSE;
3830 if (flags & ~(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))
3832 SetLastError( ERROR_INVALID_FLAGS );
3833 return FALSE;
3836 for (i = 0; i < locale_table->nb_lcnames; i++)
3838 if (!lcnames_index[i].name) continue; /* skip invariant locale */
3839 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
3840 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
3841 if (SORTIDFROMLCID( lcnames_index[i].id )) continue; /* skip alternate sorts */
3842 if (flags & MUI_LANGUAGE_NAME)
3844 const WCHAR *str = locale_strings + lcnames_index[i].name;
3846 if (unicode)
3848 memcpy( nameW, str + 1, (*str + 1) * sizeof(WCHAR) );
3849 if (!proc( nameW, param )) break;
3851 else
3853 WideCharToMultiByte( CP_ACP, 0, str + 1, -1, nameA, sizeof(nameA), NULL, NULL );
3854 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
3857 else
3859 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
3860 if (unicode)
3862 swprintf( nameW, ARRAY_SIZE(nameW), L"%04lx", lcnames_index[i].id );
3863 if (!proc( nameW, param )) break;
3865 else
3867 sprintf( nameA, "%04x", lcnames_index[i].id );
3868 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
3872 return TRUE;
3876 /******************************************************************************
3877 * CompareStringEx (kernelbase.@)
3879 INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1,
3880 const WCHAR *str2, int len2, NLSVERSIONINFO *version,
3881 void *reserved, LPARAM handle )
3883 DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | SORT_STRINGSORT |
3884 NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH | LOCALE_USE_CP_ACP;
3885 DWORD semistub_flags = NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE | LINGUISTIC_IGNOREDIACRITIC |
3886 SORT_DIGITSASNUMBERS | 0x10000000;
3887 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3888 INT ret;
3889 static int once;
3891 if (version) FIXME( "unexpected version parameter\n" );
3892 if (reserved) FIXME( "unexpected reserved value\n" );
3893 if (handle) FIXME( "unexpected handle\n" );
3895 if (!str1 || !str2)
3897 SetLastError( ERROR_INVALID_PARAMETER );
3898 return 0;
3901 if (flags & ~(supported_flags | semistub_flags))
3903 SetLastError( ERROR_INVALID_FLAGS );
3904 return 0;
3907 if (flags & semistub_flags)
3909 if (!once++) FIXME( "semi-stub behavior for flag(s) 0x%lx\n", flags & semistub_flags );
3912 if (len1 < 0) len1 = lstrlenW(str1);
3913 if (len2 < 0) len2 = lstrlenW(str2);
3915 ret = compare_weights( flags, str1, len1, str2, len2, UNICODE_WEIGHT );
3916 if (!ret)
3918 if (!(flags & NORM_IGNORENONSPACE))
3919 ret = compare_weights( flags, str1, len1, str2, len2, DIACRITIC_WEIGHT );
3920 if (!ret && !(flags & NORM_IGNORECASE))
3921 ret = compare_weights( flags, str1, len1, str2, len2, CASE_WEIGHT );
3923 if (!ret) return CSTR_EQUAL;
3924 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3928 /******************************************************************************
3929 * CompareStringA (kernelbase.@)
3931 INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1,
3932 const char *str2, int len2 )
3934 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3935 WCHAR *buf2W = buf1W + 130;
3936 LPWSTR str1W, str2W;
3937 INT len1W = 0, len2W = 0, ret;
3938 UINT locale_cp = CP_ACP;
3940 if (!str1 || !str2)
3942 SetLastError( ERROR_INVALID_PARAMETER );
3943 return 0;
3946 if (flags & SORT_DIGITSASNUMBERS)
3948 SetLastError( ERROR_INVALID_FLAGS );
3949 return 0;
3952 if (len1 < 0) len1 = strlen(str1);
3953 if (len2 < 0) len2 = strlen(str2);
3955 locale_cp = get_lcid_codepage( lcid, flags );
3956 if (len1)
3958 if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 );
3959 if (len1W) str1W = buf1W;
3960 else
3962 len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 );
3963 str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) );
3964 if (!str1W)
3966 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3967 return 0;
3969 MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W );
3972 else
3974 len1W = 0;
3975 str1W = buf1W;
3978 if (len2)
3980 if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 );
3981 if (len2W) str2W = buf2W;
3982 else
3984 len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 );
3985 str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) );
3986 if (!str2W)
3988 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
3989 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3990 return 0;
3992 MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W );
3995 else
3997 len2W = 0;
3998 str2W = buf2W;
4001 ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W );
4003 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
4004 if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W );
4005 return ret;
4009 /******************************************************************************
4010 * CompareStringW (kernelbase.@)
4012 INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1,
4013 const WCHAR *str2, int len2 )
4015 return CompareStringEx( NULL, flags, str1, len1, str2, len2, NULL, NULL, 0 );
4019 /******************************************************************************
4020 * CompareStringOrdinal (kernelbase.@)
4022 INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
4023 const WCHAR *str2, INT len2, BOOL ignore_case )
4025 int ret;
4027 if (!str1 || !str2)
4029 SetLastError( ERROR_INVALID_PARAMETER );
4030 return 0;
4032 if (len1 < 0) len1 = lstrlenW( str1 );
4033 if (len2 < 0) len2 = lstrlenW( str2 );
4035 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4036 if (ret < 0) return CSTR_LESS_THAN;
4037 if (ret > 0) return CSTR_GREATER_THAN;
4038 return CSTR_EQUAL;
4042 /******************************************************************************
4043 * ConvertDefaultLocale (kernelbase.@)
4045 LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
4047 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
4048 if (locale) lcid = locale->ilanguage;
4049 return lcid;
4053 /******************************************************************************
4054 * EnumCalendarInfoW (kernelbase.@)
4056 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
4057 CALID id, CALTYPE type )
4059 return Internal_EnumCalendarInfo( proc, NlsValidateLocale( &lcid, 0 ),
4060 id, type, TRUE, FALSE, FALSE, 0 );
4064 /******************************************************************************
4065 * EnumCalendarInfoExW (kernelbase.@)
4067 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
4068 CALID id, CALTYPE type )
4070 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4071 id, type, TRUE, TRUE, FALSE, 0 );
4074 /******************************************************************************
4075 * EnumCalendarInfoExEx (kernelbase.@)
4077 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
4078 LPCWSTR reserved, CALTYPE type, LPARAM lparam )
4080 LCID lcid;
4081 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4082 id, type, TRUE, TRUE, TRUE, lparam );
4086 /**************************************************************************
4087 * EnumDateFormatsW (kernelbase.@)
4089 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4091 return Internal_EnumDateFormats( proc, NlsValidateLocale( &lcid, 0 ),
4092 flags, TRUE, FALSE, FALSE, 0 );
4096 /**************************************************************************
4097 * EnumDateFormatsExW (kernelbase.@)
4099 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
4101 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4102 flags, TRUE, TRUE, FALSE, 0 );
4106 /**************************************************************************
4107 * EnumDateFormatsExEx (kernelbase.@)
4109 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
4110 DWORD flags, LPARAM lparam )
4112 LCID lcid;
4113 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4114 flags, TRUE, TRUE, TRUE, lparam );
4119 /******************************************************************************
4120 * EnumDynamicTimeZoneInformation (kernelbase.@)
4122 DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index,
4123 DYNAMIC_TIME_ZONE_INFORMATION *info )
4125 DYNAMIC_TIME_ZONE_INFORMATION tz;
4126 LSTATUS ret;
4127 DWORD size;
4129 if (!info) return ERROR_INVALID_PARAMETER;
4131 size = ARRAY_SIZE(tz.TimeZoneKeyName);
4132 ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL );
4133 if (ret) return ret;
4135 tz.DynamicDaylightTimeDisabled = TRUE;
4136 if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError();
4138 lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName );
4139 info->DynamicDaylightTimeDisabled = FALSE;
4140 return 0;
4144 /******************************************************************************
4145 * EnumLanguageGroupLocalesW (kernelbase.@)
4147 BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
4148 DWORD flags, LONG_PTR param )
4150 return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
4154 /******************************************************************************
4155 * EnumUILanguagesW (kernelbase.@)
4157 BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
4159 return Internal_EnumUILanguages( proc, flags, param, TRUE );
4163 /***********************************************************************
4164 * EnumSystemCodePagesW (kernelbase.@)
4166 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
4168 return Internal_EnumSystemCodePages( proc, flags, TRUE );
4172 /******************************************************************************
4173 * EnumSystemGeoID (kernelbase.@)
4175 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc )
4177 INT i;
4179 TRACE( "(%ld, %ld, %p)\n", class, parent, proc );
4181 if (!proc)
4183 SetLastError( ERROR_INVALID_PARAMETER );
4184 return FALSE;
4186 if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL)
4188 SetLastError( ERROR_INVALID_FLAGS );
4189 return FALSE;
4192 for (i = 0; i < geo_ids_count; i++)
4194 if (class != GEOCLASS_ALL && geo_ids[i].class != class) continue;
4195 if (parent && geo_ids[i].parent != parent) continue;
4196 if (!proc( geo_ids[i].id )) break;
4198 return TRUE;
4202 /******************************************************************************
4203 * EnumSystemLanguageGroupsW (kernelbase.@)
4205 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
4206 DWORD flags, LONG_PTR param )
4208 return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
4212 /******************************************************************************
4213 * EnumSystemLocalesA (kernelbase.@)
4215 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
4217 char name[10];
4218 DWORD i;
4220 for (i = 0; i < locale_table->nb_lcnames; i++)
4222 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4223 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4224 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4225 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4226 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
4227 continue; /* skip alternate sorts */
4228 sprintf( name, "%08x", lcnames_index[i].id );
4229 if (!proc( name )) break;
4231 return TRUE;
4235 /******************************************************************************
4236 * EnumSystemLocalesW (kernelbase.@)
4238 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
4240 WCHAR name[10];
4241 DWORD i;
4243 for (i = 0; i < locale_table->nb_lcnames; i++)
4245 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4246 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4247 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4248 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4249 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
4250 continue; /* skip alternate sorts */
4251 swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id );
4252 if (!proc( name )) break;
4254 return TRUE;
4258 /******************************************************************************
4259 * EnumSystemLocalesEx (kernelbase.@)
4261 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
4262 LPARAM param, void *reserved )
4264 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
4265 DWORD i, flags;
4267 if (reserved)
4269 SetLastError( ERROR_INVALID_PARAMETER );
4270 return FALSE;
4273 for (i = 0; i < locale_table->nb_lcnames; i++)
4275 const NLS_LOCALE_DATA *locale = get_locale_data( lcnames_index[i].idx );
4276 const WCHAR *str = locale_strings + lcnames_index[i].name;
4278 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4279 memcpy( buffer, str + 1, (*str + 1) * sizeof(WCHAR) );
4280 if (SORTIDFROMLCID( lcnames_index[i].id ) || wcschr( str + 1, '_' ))
4281 flags = LOCALE_ALTERNATE_SORTS;
4282 else
4283 flags = LOCALE_WINDOWS | (locale->inotneutral ? LOCALE_SPECIFICDATA : LOCALE_NEUTRALDATA);
4284 if (wanted_flags && !(flags & wanted_flags)) continue;
4285 if (!proc( buffer, flags, param )) break;
4287 return TRUE;
4291 /**************************************************************************
4292 * EnumTimeFormatsW (kernelbase.@)
4294 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4296 return Internal_EnumTimeFormats( proc, NlsValidateLocale( &lcid, 0 ), flags, TRUE, FALSE, 0 );
4300 /**************************************************************************
4301 * EnumTimeFormatsEx (kernelbase.@)
4303 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
4304 DWORD flags, LPARAM lparam )
4306 LCID lcid;
4307 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4308 flags, TRUE, TRUE, lparam );
4312 /**************************************************************************
4313 * FindNLSString (kernelbase.@)
4315 INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src,
4316 int srclen, const WCHAR *value, int valuelen, int *found )
4318 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
4320 LCIDToLocaleName( lcid, locale, ARRAY_SIZE(locale), 0 );
4321 return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 );
4325 /**************************************************************************
4326 * FindNLSStringEx (kernelbase.@)
4328 INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src,
4329 int srclen, const WCHAR *value, int valuelen, int *found,
4330 NLSVERSIONINFO *version, void *reserved, LPARAM handle )
4332 /* FIXME: this function should normalize strings before calling CompareStringEx() */
4333 DWORD mask = flags;
4334 int offset, inc, count;
4336 TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale), flags,
4337 wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found,
4338 version, reserved, handle );
4340 if (version || reserved || handle || !IsValidLocaleName(locale) ||
4341 !src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1)
4343 SetLastError( ERROR_INVALID_PARAMETER );
4344 return -1;
4346 if (srclen == -1) srclen = lstrlenW(src);
4347 if (valuelen == -1) valuelen = lstrlenW(value);
4349 srclen -= valuelen;
4350 if (srclen < 0) return -1;
4352 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
4353 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? srclen + 1 : 1;
4354 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : srclen;
4355 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4356 while (count--)
4358 if (CompareStringEx( locale, mask, src + offset, valuelen,
4359 value, valuelen, NULL, NULL, 0 ) == CSTR_EQUAL)
4361 if (found) *found = valuelen;
4362 return offset;
4364 offset += inc;
4366 return -1;
4370 /******************************************************************************
4371 * FindStringOrdinal (kernelbase.@)
4373 INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
4374 const WCHAR *val, INT val_size, BOOL ignore_case )
4376 INT offset, inc, count;
4378 TRACE( "%#lx %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
4379 wine_dbgstr_w(val), val_size, ignore_case );
4381 if (!src || !val)
4383 SetLastError( ERROR_INVALID_PARAMETER );
4384 return -1;
4387 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
4389 SetLastError( ERROR_INVALID_FLAGS );
4390 return -1;
4393 if (src_size == -1) src_size = lstrlenW( src );
4394 if (val_size == -1) val_size = lstrlenW( val );
4396 SetLastError( ERROR_SUCCESS );
4397 src_size -= val_size;
4398 if (src_size < 0) return -1;
4400 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
4401 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
4402 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4403 while (count--)
4405 if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
4406 return offset;
4407 offset += inc;
4409 return -1;
4413 /******************************************************************************
4414 * FoldStringW (kernelbase.@)
4416 INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
4418 NTSTATUS status;
4419 WCHAR *buf = dst;
4420 int len = dstlen;
4422 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
4424 SetLastError( ERROR_INVALID_PARAMETER );
4425 return 0;
4427 if (srclen == -1) srclen = lstrlenW(src) + 1;
4429 if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE)))
4431 len = srclen * 4;
4432 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4434 SetLastError( ERROR_OUTOFMEMORY );
4435 return 0;
4439 for (;;)
4441 status = fold_string( flags, src, srclen, buf, &len );
4442 if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf );
4443 if (status != STATUS_BUFFER_TOO_SMALL) break;
4444 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4446 SetLastError( ERROR_OUTOFMEMORY );
4447 return 0;
4450 if (status == STATUS_INVALID_PARAMETER_1)
4452 SetLastError( ERROR_INVALID_FLAGS );
4453 return 0;
4455 if (!set_ntstatus( status )) return 0;
4457 if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
4458 return len;
4462 static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang,
4463 BOOL ansi, WCHAR **buffer )
4465 DWORD len;
4467 if (!(flags & FORMAT_MESSAGE_FROM_STRING))
4469 const MESSAGE_RESOURCE_ENTRY *entry;
4470 NTSTATUS status = STATUS_INVALID_PARAMETER;
4472 if (flags & FORMAT_MESSAGE_FROM_HMODULE)
4474 HMODULE module = (HMODULE)src;
4475 if (!module) module = GetModuleHandleW( 0 );
4476 status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry );
4478 if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
4480 /* Fold win32 hresult to its embedded error code. */
4481 if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32)
4482 id = HRESULT_CODE( id );
4483 status = RtlFindMessage( kernelbase_handle, RT_MESSAGETABLE, lang, id, &entry );
4485 if (!set_ntstatus( status )) return NULL;
4487 src = entry->Text;
4488 ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE);
4491 if (!ansi) return src;
4492 len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
4493 if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
4494 MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len );
4495 return *buffer;
4499 /***********************************************************************
4500 * FormatMessageA (kernelbase.@)
4502 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4503 char *buffer, DWORD size, va_list *args )
4505 DWORD ret = 0;
4506 ULONG len, retsize = 0;
4507 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4508 const WCHAR *src;
4509 WCHAR *result, *message = NULL;
4510 NTSTATUS status;
4512 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4514 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4516 if (!buffer)
4518 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4519 return 0;
4521 *(char **)buffer = NULL;
4523 if (size >= 32768)
4525 SetLastError( ERROR_INVALID_PARAMETER );
4526 return 0;
4529 if (width == 0xff) width = ~0u;
4531 if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0;
4533 if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 )))
4534 status = STATUS_NO_MEMORY;
4535 else
4536 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4537 TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4538 result, 65536, &retsize );
4540 HeapFree( GetProcessHeap(), 0, message );
4542 if (status == STATUS_BUFFER_OVERFLOW)
4544 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4545 goto done;
4547 if (!set_ntstatus( status )) goto done;
4549 len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL );
4550 if (len <= 1)
4552 SetLastError( ERROR_NO_WORK_DONE );
4553 goto done;
4556 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4558 char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len ));
4559 if (!buf)
4561 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4562 goto done;
4564 *(char **)buffer = buf;
4565 WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL );
4567 else if (len > size)
4569 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4570 goto done;
4572 else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL );
4574 ret = len - 1;
4576 done:
4577 HeapFree( GetProcessHeap(), 0, result );
4578 return ret;
4582 /***********************************************************************
4583 * FormatMessageW (kernelbase.@)
4585 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4586 WCHAR *buffer, DWORD size, va_list *args )
4588 ULONG retsize = 0;
4589 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4590 const WCHAR *src;
4591 WCHAR *message = NULL;
4592 NTSTATUS status;
4594 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4596 if (!buffer)
4598 SetLastError( ERROR_INVALID_PARAMETER );
4599 return 0;
4602 if (width == 0xff) width = ~0u;
4604 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL;
4606 if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0;
4608 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4610 WCHAR *result;
4611 ULONG alloc = max( size * sizeof(WCHAR), 65536 );
4613 for (;;)
4615 if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc )))
4617 status = STATUS_NO_MEMORY;
4618 break;
4620 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4621 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4622 result, alloc, &retsize );
4623 if (!status)
4625 if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );
4626 else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
4627 result, max( retsize, size * sizeof(WCHAR) ));
4628 break;
4630 HeapFree( GetProcessHeap(), 0, result );
4631 if (status != STATUS_BUFFER_OVERFLOW) break;
4632 alloc *= 2;
4635 else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4636 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4637 buffer, size * sizeof(WCHAR), &retsize );
4639 HeapFree( GetProcessHeap(), 0, message );
4641 if (status == STATUS_BUFFER_OVERFLOW)
4643 if (size) buffer[size - 1] = 0;
4644 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4645 return 0;
4647 if (!set_ntstatus( status )) return 0;
4648 if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE );
4649 return retsize / sizeof(WCHAR) - 1;
4653 /******************************************************************************
4654 * GetACP (kernelbase.@)
4656 UINT WINAPI GetACP(void)
4658 return nls_info.AnsiTableInfo.CodePage;
4662 /***********************************************************************
4663 * GetCPInfo (kernelbase.@)
4665 BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
4667 const CPTABLEINFO *table;
4669 if (!cpinfo || !(table = get_codepage_table( codepage )))
4671 SetLastError( ERROR_INVALID_PARAMETER );
4672 return FALSE;
4674 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4675 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4676 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4677 return TRUE;
4681 /***********************************************************************
4682 * GetCPInfoExW (kernelbase.@)
4684 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
4686 const CPTABLEINFO *table;
4687 int min, max, pos;
4689 if (!cpinfo || !(table = get_codepage_table( codepage )))
4691 SetLastError( ERROR_INVALID_PARAMETER );
4692 return FALSE;
4694 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4695 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4696 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4697 cpinfo->CodePage = table->CodePage;
4698 cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
4700 min = 0;
4701 max = ARRAY_SIZE(codepage_names) - 1;
4702 cpinfo->CodePageName[0] = 0;
4703 while (min <= max)
4705 pos = (min + max) / 2;
4706 if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
4707 else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
4708 else
4710 wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
4711 break;
4714 return TRUE;
4718 /***********************************************************************
4719 * GetCalendarInfoW (kernelbase.@)
4721 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
4722 WCHAR *buffer, INT len, DWORD *value )
4724 const NLS_LOCALE_DATA *locale;
4726 TRACE( "%04lx %lu 0x%lx %p %d %p\n", lcid, calendar, type, buffer, len, value );
4728 if (!(locale = NlsValidateLocale( &lcid, 0 )))
4730 SetLastError( ERROR_INVALID_PARAMETER );
4731 return 0;
4733 return get_calendar_info( locale, calendar, type, buffer, len, value );
4737 /***********************************************************************
4738 * GetCalendarInfoEx (kernelbase.@)
4740 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *name, CALID calendar, const WCHAR *reserved,
4741 CALTYPE type, WCHAR *buffer, INT len, DWORD *value )
4743 LCID lcid;
4744 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
4746 TRACE( "%s %lu 0x%lx %p %d %p\n", debugstr_w(name), calendar, type, buffer, len, value );
4748 if (!locale)
4750 SetLastError( ERROR_INVALID_PARAMETER );
4751 return 0;
4753 return get_calendar_info( locale, calendar, type, buffer, len, value );
4757 static CRITICAL_SECTION tzname_section;
4758 static CRITICAL_SECTION_DEBUG tzname_section_debug =
4760 0, 0, &tzname_section,
4761 { &tzname_section_debug.ProcessLocksList, &tzname_section_debug.ProcessLocksList },
4762 0, 0, { (DWORD_PTR)(__FILE__ ": tzname_section") }
4764 static CRITICAL_SECTION tzname_section = { &tzname_section_debug, -1, 0, 0, 0, 0 };
4765 static struct {
4766 LCID lcid;
4767 WCHAR key_name[128];
4768 WCHAR standard_name[32];
4769 WCHAR daylight_name[32];
4770 } cached_tzname;
4772 /***********************************************************************
4773 * GetDynamicTimeZoneInformation (kernelbase.@)
4775 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info )
4777 HKEY key;
4778 LARGE_INTEGER now;
4780 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info )))
4781 return TIME_ZONE_ID_INVALID;
4783 RtlEnterCriticalSection( &tzname_section );
4784 if (cached_tzname.lcid == GetThreadLocale() &&
4785 !wcscmp( info->TimeZoneKeyName, cached_tzname.key_name ))
4787 wcscpy( info->StandardName, cached_tzname.standard_name );
4788 wcscpy( info->DaylightName, cached_tzname.daylight_name );
4789 RtlLeaveCriticalSection( &tzname_section );
4791 else
4793 RtlLeaveCriticalSection( &tzname_section );
4794 if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))
4796 RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
4797 sizeof(info->StandardName), NULL, 0, system_dir );
4798 RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
4799 sizeof(info->DaylightName), NULL, 0, system_dir );
4800 RegCloseKey( key );
4802 else return TIME_ZONE_ID_INVALID;
4804 RtlEnterCriticalSection( &tzname_section );
4805 cached_tzname.lcid = GetThreadLocale();
4806 wcscpy( cached_tzname.key_name, info->TimeZoneKeyName );
4807 wcscpy( cached_tzname.standard_name, info->StandardName );
4808 wcscpy( cached_tzname.daylight_name, info->DaylightName );
4809 RtlLeaveCriticalSection( &tzname_section );
4812 NtQuerySystemTime( &now );
4813 return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE );
4817 /******************************************************************************
4818 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
4820 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info,
4821 DWORD *first, DWORD *last )
4823 HKEY key, dst_key = 0;
4824 DWORD type, count, ret = ERROR_FILE_NOT_FOUND;
4826 if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret;
4828 if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done;
4829 count = sizeof(DWORD);
4830 if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done;
4831 if (type != REG_DWORD) goto done;
4832 count = sizeof(DWORD);
4833 if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done;
4834 if (type != REG_DWORD) goto done;
4835 ret = 0;
4837 done:
4838 RegCloseKey( dst_key );
4839 RegCloseKey( key );
4840 return ret;
4844 /******************************************************************************
4845 * GetFileMUIInfo (kernelbase.@)
4847 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIInfo( DWORD flags, const WCHAR *path,
4848 FILEMUIINFO *info, DWORD *size )
4850 FIXME( "stub: %lu, %s, %p, %p\n", flags, debugstr_w(path), info, size );
4851 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4852 return FALSE;
4856 /******************************************************************************
4857 * GetFileMUIPath (kernelbase.@)
4859 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags, const WCHAR *filepath,
4860 WCHAR *language, ULONG *languagelen,
4861 WCHAR *muipath, ULONG *muipathlen,
4862 ULONGLONG *enumerator )
4864 FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
4865 debugstr_w(language), languagelen, muipath, muipathlen, enumerator );
4866 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4867 return FALSE;
4871 /******************************************************************************
4872 * GetGeoInfoW (kernelbase.@)
4874 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang )
4876 const struct geo_id *ptr = find_geo_id_entry( id );
4878 TRACE( "%ld %ld %p %d %d\n", id, type, data, count, lang );
4880 if (!ptr)
4882 SetLastError( ERROR_INVALID_PARAMETER );
4883 return 0;
4885 return get_geo_info( ptr, type, data, count, lang );
4889 /******************************************************************************
4890 * GetLocaleInfoA (kernelbase.@)
4892 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
4894 const NLS_LOCALE_DATA *locale;
4895 WCHAR *bufferW;
4896 INT lenW, ret;
4898 TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid, lctype, buffer, len );
4900 if (len < 0 || (len && !buffer))
4902 SetLastError( ERROR_INVALID_PARAMETER );
4903 return 0;
4905 if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
4907 SetLastError( ERROR_INVALID_FLAGS );
4908 return 0;
4910 if (!(locale = NlsValidateLocale( &lcid, 0 )))
4912 SetLastError( ERROR_INVALID_PARAMETER );
4913 return 0;
4915 if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
4917 ret = get_locale_info( locale, lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) );
4918 return ret * sizeof(WCHAR);
4921 if (!(lenW = get_locale_info( locale, lcid, lctype, NULL, 0 ))) return 0;
4923 if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
4925 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4926 return 0;
4928 ret = get_locale_info( locale, lcid, lctype, bufferW, lenW );
4929 if (ret) ret = WideCharToMultiByte( get_locale_codepage( locale, lctype ), 0,
4930 bufferW, ret, buffer, len, NULL, NULL );
4931 RtlFreeHeap( GetProcessHeap(), 0, bufferW );
4932 return ret;
4936 /******************************************************************************
4937 * GetLocaleInfoW (kernelbase.@)
4939 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
4941 const NLS_LOCALE_DATA *locale;
4943 if (len < 0 || (len && !buffer))
4945 SetLastError( ERROR_INVALID_PARAMETER );
4946 return 0;
4949 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
4951 if (!(locale = NlsValidateLocale( &lcid, 0 )))
4953 SetLastError( ERROR_INVALID_PARAMETER );
4954 return 0;
4956 return get_locale_info( locale, lcid, lctype, buffer, len );
4960 /******************************************************************************
4961 * GetLocaleInfoEx (kernelbase.@)
4963 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *name, LCTYPE info, WCHAR *buffer, INT len )
4965 LCID lcid;
4966 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
4968 TRACE( "%s 0x%lx %p %d\n", debugstr_w(name), info, buffer, len );
4970 if (!locale)
4972 SetLastError( ERROR_INVALID_PARAMETER );
4973 return 0;
4975 return get_locale_info( locale, lcid, info, buffer, len );
4979 /******************************************************************************
4980 * GetNLSVersion (kernelbase.@)
4982 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info )
4984 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
4986 if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId ))
4988 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4989 return FALSE;
4991 if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES ))
4993 SetLastError( ERROR_INVALID_PARAMETER );
4994 return FALSE;
4996 return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info );
5000 /******************************************************************************
5001 * GetNLSVersionEx (kernelbase.@)
5003 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale,
5004 NLSVERSIONINFOEX *info )
5006 LCID lcid = 0;
5008 if (func != COMPARE_STRING)
5010 SetLastError( ERROR_INVALID_FLAGS );
5011 return FALSE;
5013 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5014 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5016 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5017 return FALSE;
5020 if (!(lcid = LocaleNameToLCID( locale, 0 ))) return FALSE;
5022 info->dwNLSVersion = info->dwDefinedVersion = sort.version;
5023 if (info->dwNLSVersionInfoSize >= sizeof(*info))
5025 const struct sortguid *sortid = get_language_sort( locale );
5026 info->dwEffectiveId = lcid;
5027 info->guidCustomVersion = sortid ? sortid->id : default_sort_guid;
5029 return TRUE;
5033 /******************************************************************************
5034 * GetOEMCP (kernelbase.@)
5036 UINT WINAPI GetOEMCP(void)
5038 return nls_info.OemTableInfo.CodePage;
5042 /***********************************************************************
5043 * GetProcessPreferredUILanguages (kernelbase.@)
5045 BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPreferredUILanguages( DWORD flags, ULONG *count,
5046 WCHAR *buffer, ULONG *size )
5048 return set_ntstatus( RtlGetProcessPreferredUILanguages( flags, count, buffer, size ));
5052 /***********************************************************************
5053 * GetStringTypeA (kernelbase.@)
5055 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
5056 WORD *chartype )
5058 UINT cp;
5059 INT countW;
5060 LPWSTR srcW;
5061 BOOL ret = FALSE;
5063 if (count == -1) count = strlen(src) + 1;
5065 cp = get_lcid_codepage( locale, 0 );
5066 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
5067 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5069 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
5071 * NOTE: the target buffer has 1 word for each CHARACTER in the source
5072 * string, with multibyte characters there maybe be more bytes in count
5073 * than character space in the buffer!
5075 ret = GetStringTypeW(type, srcW, countW, chartype);
5076 HeapFree(GetProcessHeap(), 0, srcW);
5078 return ret;
5082 /***********************************************************************
5083 * GetStringTypeW (kernelbase.@)
5085 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
5087 if (!src)
5089 SetLastError( ERROR_INVALID_PARAMETER );
5090 return FALSE;
5092 if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3)
5094 SetLastError( ERROR_INVALID_PARAMETER );
5095 return FALSE;
5098 if (count == -1) count = lstrlenW(src) + 1;
5100 while (count--) *chartype++ = get_char_type( type, *src++ );
5102 return TRUE;
5106 /***********************************************************************
5107 * GetStringTypeExW (kernelbase.@)
5109 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
5110 WORD *chartype )
5112 /* locale is ignored for Unicode */
5113 return GetStringTypeW( type, src, count, chartype );
5117 /***********************************************************************
5118 * GetSystemDefaultLCID (kernelbase.@)
5120 LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
5122 return system_lcid;
5126 /***********************************************************************
5127 * GetSystemDefaultLangID (kernelbase.@)
5129 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
5131 return LANGIDFROMLCID( GetSystemDefaultLCID() );
5135 /***********************************************************************
5136 * GetSystemDefaultLocaleName (kernelbase.@)
5138 INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT count )
5140 return get_locale_info( system_locale, system_lcid, LOCALE_SNAME, name, count );
5144 /***********************************************************************
5145 * GetSystemDefaultUILanguage (kernelbase.@)
5147 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
5149 LANGID lang;
5150 NtQueryInstallUILanguage( &lang );
5151 return lang;
5155 /***********************************************************************
5156 * GetSystemPreferredUILanguages (kernelbase.@)
5158 BOOL WINAPI DECLSPEC_HOTPATCH GetSystemPreferredUILanguages( DWORD flags, ULONG *count,
5159 WCHAR *buffer, ULONG *size )
5161 return set_ntstatus( RtlGetSystemPreferredUILanguages( flags, 0, count, buffer, size ));
5165 /***********************************************************************
5166 * GetThreadPreferredUILanguages (kernelbase.@)
5168 BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPreferredUILanguages( DWORD flags, ULONG *count,
5169 WCHAR *buffer, ULONG *size )
5171 return set_ntstatus( RtlGetThreadPreferredUILanguages( flags, count, buffer, size ));
5175 /***********************************************************************
5176 * GetTimeZoneInformation (kernelbase.@)
5178 DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info )
5180 DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
5181 DWORD ret = GetDynamicTimeZoneInformation( &tzinfo );
5183 memcpy( info, &tzinfo, sizeof(*info) );
5184 return ret;
5188 /***********************************************************************
5189 * GetTimeZoneInformationForYear (kernelbase.@)
5191 BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year,
5192 DYNAMIC_TIME_ZONE_INFORMATION *dynamic,
5193 TIME_ZONE_INFORMATION *info )
5195 DYNAMIC_TIME_ZONE_INFORMATION local_info;
5196 HKEY key = 0, dst_key;
5197 DWORD count;
5198 LRESULT ret;
5199 struct
5201 LONG bias;
5202 LONG std_bias;
5203 LONG dlt_bias;
5204 SYSTEMTIME std_date;
5205 SYSTEMTIME dlt_date;
5206 } data;
5208 TRACE( "(%u,%p)\n", year, info );
5210 if (!dynamic)
5212 if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE;
5213 dynamic = &local_info;
5216 if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done;
5217 if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
5218 sizeof(info->StandardName), NULL, 0, system_dir ))
5220 count = sizeof(info->StandardName);
5221 if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count )))
5222 goto done;
5224 if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
5225 sizeof(info->DaylightName), NULL, 0, system_dir ))
5227 count = sizeof(info->DaylightName);
5228 if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count )))
5229 goto done;
5232 ret = ERROR_FILE_NOT_FOUND;
5233 if (!dynamic->DynamicDaylightTimeDisabled &&
5234 !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key ))
5236 WCHAR yearW[16];
5237 swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year );
5238 count = sizeof(data);
5239 ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count );
5240 RegCloseKey( dst_key );
5242 if (ret)
5244 count = sizeof(data);
5245 ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count );
5248 if (!ret)
5250 info->Bias = data.bias;
5251 info->StandardBias = data.std_bias;
5252 info->DaylightBias = data.dlt_bias;
5253 info->StandardDate = data.std_date;
5254 info->DaylightDate = data.dlt_date;
5257 done:
5258 RegCloseKey( key );
5259 if (ret) SetLastError( ret );
5260 return !ret;
5264 /***********************************************************************
5265 * GetUserDefaultLCID (kernelbase.@)
5267 LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
5269 return user_lcid;
5273 /***********************************************************************
5274 * GetUserDefaultLangID (kernelbase.@)
5276 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
5278 return LANGIDFROMLCID( GetUserDefaultLCID() );
5282 /***********************************************************************
5283 * GetUserDefaultLocaleName (kernelbase.@)
5285 INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
5287 return get_locale_info( user_locale, user_lcid, LOCALE_SNAME, name, len );
5291 /***********************************************************************
5292 * GetUserDefaultUILanguage (kernelbase.@)
5294 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
5296 return LANGIDFROMLCID( GetUserDefaultLCID() );
5300 /******************************************************************************
5301 * GetUserGeoID (kernelbase.@)
5303 GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass )
5305 GEOID ret = 39070;
5306 const WCHAR *name;
5307 WCHAR bufferW[40];
5308 HKEY hkey;
5310 switch (geoclass)
5312 case GEOCLASS_NATION:
5313 name = L"Nation";
5314 break;
5315 case GEOCLASS_REGION:
5316 name = L"Region";
5317 break;
5318 default:
5319 WARN("Unknown geoclass %ld\n", geoclass);
5320 return GEOID_NOT_AVAILABLE;
5322 if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey ))
5324 DWORD count = sizeof(bufferW);
5325 if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count ))
5326 ret = wcstol( bufferW, NULL, 10 );
5327 RegCloseKey( hkey );
5329 return ret;
5333 /******************************************************************************
5334 * GetUserPreferredUILanguages (kernelbase.@)
5336 BOOL WINAPI DECLSPEC_HOTPATCH GetUserPreferredUILanguages( DWORD flags, ULONG *count,
5337 WCHAR *buffer, ULONG *size )
5339 return set_ntstatus( RtlGetUserPreferredUILanguages( flags, 0, count, buffer, size ));
5343 /******************************************************************************
5344 * IdnToAscii (kernelbase.@)
5346 INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen,
5347 WCHAR *dst, INT dstlen )
5349 NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
5350 if (!set_ntstatus( status )) return 0;
5351 return dstlen;
5355 /******************************************************************************
5356 * IdnToNameprepUnicode (kernelbase.@)
5358 INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
5359 WCHAR *dst, INT dstlen )
5361 NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen );
5362 if (!set_ntstatus( status )) return 0;
5363 return dstlen;
5367 /******************************************************************************
5368 * IdnToUnicode (kernelbase.@)
5370 INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen,
5371 WCHAR *dst, INT dstlen )
5373 NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen );
5374 if (!set_ntstatus( status )) return 0;
5375 return dstlen;
5379 /******************************************************************************
5380 * IsCharAlphaA (kernelbase.@)
5382 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
5384 WCHAR wc;
5385 DWORD reslen;
5386 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5387 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5391 /******************************************************************************
5392 * IsCharAlphaW (kernelbase.@)
5394 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
5396 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5400 /******************************************************************************
5401 * IsCharAlphaNumericA (kernelbase.@)
5403 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
5405 WCHAR wc;
5406 DWORD reslen;
5407 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5408 return reslen && (get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5412 /******************************************************************************
5413 * IsCharAlphaNumericW (kernelbase.@)
5415 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
5417 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5421 /******************************************************************************
5422 * IsCharBlankW (kernelbase.@)
5424 BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
5426 return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK);
5430 /******************************************************************************
5431 * IsCharCntrlW (kernelbase.@)
5433 BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
5435 return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL);
5439 /******************************************************************************
5440 * IsCharDigitW (kernelbase.@)
5442 BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
5444 return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT);
5448 /******************************************************************************
5449 * IsCharLowerA (kernelbase.@)
5451 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
5453 WCHAR wc;
5454 DWORD reslen;
5455 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5456 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5460 /******************************************************************************
5461 * IsCharLowerW (kernelbase.@)
5463 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
5465 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5469 /******************************************************************************
5470 * IsCharPunctW (kernelbase.@)
5472 BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
5474 return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT);
5478 /******************************************************************************
5479 * IsCharSpaceA (kernelbase.@)
5481 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
5483 WCHAR wc;
5484 DWORD reslen;
5485 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5486 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5490 /******************************************************************************
5491 * IsCharSpaceW (kernelbase.@)
5493 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
5495 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5499 /******************************************************************************
5500 * IsCharUpperA (kernelbase.@)
5502 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
5504 WCHAR wc;
5505 DWORD reslen;
5506 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
5507 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5511 /******************************************************************************
5512 * IsCharUpperW (kernelbase.@)
5514 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
5516 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5520 /******************************************************************************
5521 * IsCharXDigitW (kernelbase.@)
5523 BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
5525 return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT);
5529 /******************************************************************************
5530 * IsDBCSLeadByte (kernelbase.@)
5532 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
5534 return nls_info.AnsiTableInfo.DBCSCodePage && nls_info.AnsiTableInfo.DBCSOffsets[testchar];
5538 /******************************************************************************
5539 * IsDBCSLeadByteEx (kernelbase.@)
5541 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
5543 const CPTABLEINFO *table = get_codepage_table( codepage );
5544 return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
5548 /******************************************************************************
5549 * IsNormalizedString (kernelbase.@)
5551 BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
5553 BOOLEAN res;
5554 if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
5555 return res;
5559 /******************************************************************************
5560 * IsValidCodePage (kernelbase.@)
5562 BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
5564 switch (codepage)
5566 case CP_ACP:
5567 case CP_OEMCP:
5568 case CP_MACCP:
5569 case CP_THREAD_ACP:
5570 return FALSE;
5571 default:
5572 return get_codepage_table( codepage ) != NULL;
5577 /******************************************************************************
5578 * IsValidLanguageGroup (kernelbase.@)
5580 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
5582 WCHAR name[10], value[10];
5583 DWORD type, value_len = sizeof(value);
5584 BOOL ret = FALSE;
5585 HKEY key;
5587 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
5589 swprintf( name, ARRAY_SIZE(name), L"%x", id );
5590 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
5591 ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
5593 RegCloseKey( key );
5594 return ret;
5598 /******************************************************************************
5599 * IsValidLocale (kernelbase.@)
5601 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
5603 switch (lcid)
5605 case LOCALE_NEUTRAL:
5606 case LOCALE_USER_DEFAULT:
5607 case LOCALE_SYSTEM_DEFAULT:
5608 return FALSE;
5609 default:
5610 return !!NlsValidateLocale( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES );
5615 /******************************************************************************
5616 * IsValidLocaleName (kernelbase.@)
5618 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
5620 if (locale == LOCALE_NAME_USER_DEFAULT) return FALSE;
5621 return !!find_lcname_entry( locale );
5625 /******************************************************************************
5626 * IsValidNLSVersion (kernelbase.@)
5628 DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale,
5629 NLSVERSIONINFOEX *info )
5631 static const GUID GUID_NULL;
5632 NLSVERSIONINFOEX infoex;
5633 DWORD ret;
5635 if (func != COMPARE_STRING)
5637 SetLastError( ERROR_INVALID_PARAMETER );
5638 return FALSE;
5640 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5641 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5643 SetLastError( ERROR_INVALID_PARAMETER );
5644 return FALSE;
5646 infoex.dwNLSVersionInfoSize = sizeof(infoex);
5647 if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE;
5649 ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff);
5650 if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL ))
5651 ret = find_sortguid( &info->guidCustomVersion ) != NULL;
5653 if (!ret) SetLastError( ERROR_SUCCESS );
5654 return ret;
5658 /***********************************************************************
5659 * LCIDToLocaleName (kernelbase.@)
5661 INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
5663 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, flags );
5665 if (!locale)
5667 SetLastError( ERROR_INVALID_PARAMETER );
5668 return 0;
5670 return get_locale_info( locale, lcid, LOCALE_SNAME, name, count );
5674 /***********************************************************************
5675 * LCMapStringEx (kernelbase.@)
5677 INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
5678 WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
5679 void *reserved, LPARAM handle )
5681 const struct sortguid *sortid = NULL;
5682 LPWSTR dst_ptr;
5683 INT len;
5685 if (version) FIXME( "unsupported version structure %p\n", version );
5686 if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
5687 if (handle)
5689 static int once;
5690 if (!once++) FIXME( "unsupported lparam %Ix\n", handle );
5693 if (!src || !srclen || dstlen < 0)
5695 SetLastError( ERROR_INVALID_PARAMETER );
5696 return 0;
5699 /* mutually exclusive flags */
5700 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
5701 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
5702 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
5703 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
5704 !flags)
5706 SetLastError( ERROR_INVALID_FLAGS );
5707 return 0;
5710 if (!dstlen) dst = NULL;
5712 if (flags & LCMAP_LINGUISTIC_CASING && !(sortid = get_language_sort( locale )))
5714 FIXME( "unknown locale %s\n", debugstr_w(locale) );
5715 SetLastError( ERROR_INVALID_PARAMETER );
5716 return 0;
5719 if (flags & LCMAP_SORTKEY)
5721 INT ret;
5723 if (src == dst)
5725 SetLastError( ERROR_INVALID_FLAGS );
5726 return 0;
5728 if (srclen < 0) srclen = lstrlenW(src);
5730 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5731 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5733 if ((ret = get_sortkey( flags, src, srclen, (char *)dst, dstlen ))) ret++;
5734 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
5735 return ret;
5738 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
5739 if (flags & SORT_STRINGSORT)
5741 SetLastError( ERROR_INVALID_FLAGS );
5742 return 0;
5744 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
5745 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
5746 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
5747 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
5749 SetLastError( ERROR_INVALID_FLAGS );
5750 return 0;
5753 if (srclen < 0) srclen = lstrlenW(src) + 1;
5755 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5756 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5758 if (!dst) /* return required string length */
5760 if (flags & NORM_IGNORESYMBOLS)
5762 for (len = 0; srclen; src++, srclen--)
5763 if (!(get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE))) len++;
5765 else if (flags & LCMAP_FULLWIDTH)
5767 WCHAR wch;
5768 for (len = 0; srclen; src++, srclen--, len++)
5770 if (map_to_fullwidth( src, srclen, &wch ) == 2)
5772 src++;
5773 srclen--;
5777 else if (flags & LCMAP_HALFWIDTH)
5779 for (len = 0; srclen; src++, srclen--, len++)
5781 WCHAR wch = *src;
5782 /* map Hiragana to Katakana before decomposition if needed */
5783 if (flags & LCMAP_KATAKANA) wch = casemap( charmaps[CHARMAP_KATAKANA], wch );
5784 if (map_to_halfwidth( wch, NULL, 0 ) == 2) len++;
5787 else len = srclen;
5788 return len;
5791 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
5793 SetLastError( ERROR_INVALID_FLAGS );
5794 return 0;
5797 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
5799 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
5801 if ((flags & NORM_IGNORESYMBOLS) && (get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE)))
5802 continue;
5803 *dst_ptr++ = *src;
5804 len--;
5806 goto done;
5809 if (flags & LCMAP_TRADITIONAL_CHINESE)
5811 for (len = dstlen, dst_ptr = dst; srclen && len; srclen--, len--)
5812 *dst_ptr++ = casemap( charmaps[CHARMAP_TRADITIONAL], *src++ );
5814 if (flags & LCMAP_SIMPLIFIED_CHINESE)
5816 for (len = dstlen, dst_ptr = dst; srclen && len; srclen--, len--)
5817 *dst_ptr++ = casemap( charmaps[CHARMAP_SIMPLIFIED], *src++ );
5820 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
5822 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
5824 WCHAR wch;
5825 if (flags & LCMAP_FULLWIDTH)
5827 /* map half-width character to full-width one,
5828 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
5829 if (map_to_fullwidth( src, srclen, &wch ) == 2)
5831 src++;
5832 srclen--;
5835 else wch = *src;
5837 if (flags & LCMAP_KATAKANA) wch = casemap( charmaps[CHARMAP_KATAKANA], wch );
5838 else if (flags & LCMAP_HIRAGANA) wch = casemap( charmaps[CHARMAP_HIRAGANA], wch );
5840 if (flags & LCMAP_HALFWIDTH)
5842 /* map full-width character to half-width one,
5843 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
5844 if (map_to_halfwidth(wch, dst_ptr, len) == 2)
5846 len--;
5847 dst_ptr++;
5848 if (!len) break;
5851 else *dst_ptr = wch;
5853 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen) goto done;
5855 srclen = dst_ptr - dst;
5856 src = dst;
5859 if (flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
5861 const USHORT *table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
5862 table = table + 2 + (flags & LCMAP_LOWERCASE ? table[1] : 0);
5863 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--, len--)
5864 *dst_ptr++ = casemap( table, *src );
5866 else
5868 len = min( srclen, dstlen );
5869 memcpy( dst, src, len * sizeof(WCHAR) );
5870 dst_ptr = dst + len;
5871 srclen -= len;
5874 done:
5875 if (srclen)
5877 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5878 return 0;
5881 return dst_ptr - dst;
5885 /***********************************************************************
5886 * LCMapStringA (kernelbase.@)
5888 INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
5889 char *dst, int dstlen )
5891 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
5892 LPWSTR srcW, dstW;
5893 INT ret = 0, srclenW, dstlenW;
5894 UINT locale_cp = CP_ACP;
5896 if (!src || !srclen || dstlen < 0)
5898 SetLastError( ERROR_INVALID_PARAMETER );
5899 return 0;
5902 locale_cp = get_lcid_codepage( lcid, flags );
5904 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
5905 if (srclenW) srcW = bufW;
5906 else
5908 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
5909 srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
5910 if (!srcW)
5912 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5913 return 0;
5915 MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
5918 if (flags & LCMAP_SORTKEY)
5920 if (src == dst)
5922 SetLastError( ERROR_INVALID_FLAGS );
5923 goto done;
5925 ret = LCMapStringEx( NULL, flags, srcW, srclenW, (WCHAR *)dst, dstlen, NULL, NULL, 0 );
5926 goto done;
5929 if (flags & SORT_STRINGSORT)
5931 SetLastError( ERROR_INVALID_FLAGS );
5932 goto done;
5935 dstlenW = LCMapStringEx( NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0 );
5936 if (!dstlenW) goto done;
5938 dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
5939 if (!dstW)
5941 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5942 goto done;
5944 LCMapStringEx( NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0 );
5945 ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
5946 HeapFree( GetProcessHeap(), 0, dstW );
5948 done:
5949 if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
5950 return ret;
5954 /***********************************************************************
5955 * LCMapStringW (kernelbase.@)
5957 INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
5958 WCHAR *dst, int dstlen )
5960 return LCMapStringEx( NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
5964 /***********************************************************************
5965 * LocaleNameToLCID (kernelbase.@)
5967 LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
5969 LCID lcid;
5970 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5972 if (!locale)
5974 SetLastError( ERROR_INVALID_PARAMETER );
5975 return 0;
5977 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
5978 lcid = locale->idefaultlanguage;
5979 return lcid;
5983 /******************************************************************************
5984 * MultiByteToWideChar (kernelbase.@)
5986 INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
5987 WCHAR *dst, INT dstlen )
5989 const CPTABLEINFO *info;
5990 int ret;
5992 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
5994 SetLastError( ERROR_INVALID_PARAMETER );
5995 return 0;
5997 if (srclen < 0) srclen = strlen(src) + 1;
5999 switch (codepage)
6001 case CP_SYMBOL:
6002 ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
6003 break;
6004 case CP_UTF7:
6005 ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
6006 break;
6007 case CP_UNIXCP:
6008 codepage = unix_cp;
6009 /* fall through */
6010 default:
6011 if (!(info = get_codepage_table( codepage )))
6013 SetLastError( ERROR_INVALID_PARAMETER );
6014 return 0;
6016 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
6018 SetLastError( ERROR_INVALID_FLAGS );
6019 return 0;
6021 if (info->CodePage == CP_UTF8)
6022 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6023 else
6024 ret = mbstowcs_codepage( info, flags, src, srclen, dst, dstlen );
6025 break;
6027 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
6028 return ret;
6032 /******************************************************************************
6033 * NormalizeString (kernelbase.@)
6035 INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
6036 WCHAR *dst, INT dst_len)
6038 NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len );
6040 switch (status)
6042 case STATUS_OBJECT_NAME_NOT_FOUND:
6043 status = STATUS_INVALID_PARAMETER;
6044 break;
6045 case STATUS_BUFFER_TOO_SMALL:
6046 case STATUS_NO_UNICODE_TRANSLATION:
6047 dst_len = -dst_len;
6048 break;
6050 SetLastError( RtlNtStatusToDosError( status ));
6051 return dst_len;
6055 /******************************************************************************
6056 * ResolveLocaleName (kernelbase.@)
6058 INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
6060 LCID lcid;
6061 UINT pos, datalen;
6062 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6064 if (!locale)
6066 static const WCHAR valid[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
6067 WCHAR *p, tmp[LOCALE_NAME_MAX_LENGTH];
6069 if (wcsspn( name, valid ) < wcslen( name ))
6071 SetLastError( ERROR_INVALID_PARAMETER );
6072 return 0;
6074 lstrcpynW( tmp, name, LOCALE_NAME_MAX_LENGTH );
6075 while (!locale)
6077 for (p = tmp + wcslen(tmp) - 1; p >= tmp; p--) if (*p == '-' || *p == '_') break;
6078 if (p <= tmp) break;
6079 *p = 0;
6080 locale = get_locale_by_name( tmp, &lcid );
6084 pos = locale ? (locale->inotneutral ? locale->sname : locale->ssortlocale) : 0;
6085 datalen = locale_strings[pos] + 1;
6087 if (!len) return datalen;
6088 lstrcpynW( buffer, locale_strings + pos + 1, len );
6089 if (datalen > len)
6091 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6092 return 0;
6094 return datalen;
6098 /******************************************************************************
6099 * SetLocaleInfoW (kernelbase.@)
6101 BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
6103 WCHAR *str, tmp[80];
6105 if (!data)
6107 SetLastError( ERROR_INVALID_PARAMETER );
6108 return FALSE;
6111 switch (LOWORD(lctype))
6113 case LOCALE_ICALENDARTYPE: return set_registry_entry( &entry_icalendartype, data );
6114 case LOCALE_ICURRDIGITS: return set_registry_entry( &entry_icurrdigits, data );
6115 case LOCALE_ICURRENCY: return set_registry_entry( &entry_icurrency, data );
6116 case LOCALE_IDIGITS: return set_registry_entry( &entry_idigits, data );
6117 case LOCALE_IDIGITSUBSTITUTION: return set_registry_entry( &entry_idigitsubstitution, data );
6118 case LOCALE_IFIRSTDAYOFWEEK: return set_registry_entry( &entry_ifirstdayofweek, data );
6119 case LOCALE_IFIRSTWEEKOFYEAR: return set_registry_entry( &entry_ifirstweekofyear, data );
6120 case LOCALE_ILZERO: return set_registry_entry( &entry_ilzero, data );
6121 case LOCALE_IMEASURE: return set_registry_entry( &entry_imeasure, data );
6122 case LOCALE_INEGCURR: return set_registry_entry( &entry_inegcurr, data );
6123 case LOCALE_INEGNUMBER: return set_registry_entry( &entry_inegnumber, data );
6124 case LOCALE_IPAPERSIZE: return set_registry_entry( &entry_ipapersize, data );
6125 case LOCALE_S1159: return set_registry_entry( &entry_s1159, data );
6126 case LOCALE_S2359: return set_registry_entry( &entry_s2359, data );
6127 case LOCALE_SCURRENCY: return set_registry_entry( &entry_scurrency, data );
6128 case LOCALE_SDECIMAL: return set_registry_entry( &entry_sdecimal, data );
6129 case LOCALE_SGROUPING: return set_registry_entry( &entry_sgrouping, data );
6130 case LOCALE_SLIST: return set_registry_entry( &entry_slist, data );
6131 case LOCALE_SLONGDATE: return set_registry_entry( &entry_slongdate, data );
6132 case LOCALE_SMONDECIMALSEP: return set_registry_entry( &entry_smondecimalsep, data );
6133 case LOCALE_SMONGROUPING: return set_registry_entry( &entry_smongrouping, data );
6134 case LOCALE_SMONTHOUSANDSEP: return set_registry_entry( &entry_smonthousandsep, data );
6135 case LOCALE_SNATIVEDIGITS: return set_registry_entry( &entry_snativedigits, data );
6136 case LOCALE_SNEGATIVESIGN: return set_registry_entry( &entry_snegativesign, data );
6137 case LOCALE_SPOSITIVESIGN: return set_registry_entry( &entry_spositivesign, data );
6138 case LOCALE_SSHORTTIME: return set_registry_entry( &entry_sshorttime, data );
6139 case LOCALE_STHOUSAND: return set_registry_entry( &entry_sthousand, data );
6140 case LOCALE_SYEARMONTH: return set_registry_entry( &entry_syearmonth, data );
6142 case LOCALE_SDATE:
6143 if (!get_locale_info( user_locale, user_lcid, LOCALE_SSHORTDATE, tmp, ARRAY_SIZE(tmp) )) break;
6144 data = locale_replace_separator( tmp, data );
6145 /* fall through */
6146 case LOCALE_SSHORTDATE:
6147 if (!set_registry_entry( &entry_sshortdate, data )) return FALSE;
6148 update_registry_value( LOCALE_IDATE, NULL, L"iDate" );
6149 update_registry_value( LOCALE_SDATE, NULL, L"sDate" );
6150 return TRUE;
6152 case LOCALE_STIME:
6153 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6154 data = locale_replace_separator( tmp, data );
6155 /* fall through */
6156 case LOCALE_STIMEFORMAT:
6157 if (!set_registry_entry( &entry_stimeformat, data )) return FALSE;
6158 update_registry_value( LOCALE_ITIME, NULL, L"iTime" );
6159 update_registry_value( LOCALE_ITIMEMARKPOSN, NULL, L"iTimePrefix" );
6160 update_registry_value( LOCALE_ITLZERO, NULL, L"iTLZero" );
6161 update_registry_value( LOCALE_STIME, NULL, L"sTime" );
6162 return TRUE;
6164 case LOCALE_ITIME:
6165 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6166 if (!(str = find_format( tmp, L"Hh" ))) break;
6167 while (*str == 'h' || *str == 'H') *str++ = (*data == '0' ? 'h' : 'H');
6168 if (!set_registry_entry( &entry_stimeformat, tmp )) break;
6169 update_registry_value( LOCALE_ITIME, NULL, L"iTime" );
6170 return TRUE;
6172 case LOCALE_SINTLSYMBOL:
6173 if (!set_registry_entry( &entry_sintlsymbol, data )) return FALSE;
6174 /* if restoring the original value, restore the original LOCALE_SCURRENCY as well */
6175 if (!wcsicmp( data, locale_strings + user_locale->sintlsymbol + 1 ))
6176 data = locale_strings + user_locale->scurrency + 1;
6177 set_registry_entry( &entry_scurrency, data );
6178 return TRUE;
6180 SetLastError( ERROR_INVALID_FLAGS );
6181 return FALSE;
6185 /***********************************************************************
6186 * SetCalendarInfoW (kernelbase.@)
6188 INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
6190 FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
6191 return 0;
6195 /***********************************************************************
6196 * SetProcessPreferredUILanguages (kernelbase.@)
6198 BOOL WINAPI DECLSPEC_HOTPATCH SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6200 return set_ntstatus( RtlSetProcessPreferredUILanguages( flags, buffer, count ));
6204 /***********************************************************************
6205 * SetThreadPreferredUILanguages (kernelbase.@)
6207 BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6209 return set_ntstatus( RtlSetThreadPreferredUILanguages( flags, buffer, count ));
6213 /***********************************************************************
6214 * SetTimeZoneInformation (kernelbase.@)
6216 BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info )
6218 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info ));
6222 /******************************************************************************
6223 * SetUserGeoID (kernelbase.@)
6225 BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id )
6227 const struct geo_id *geo = find_geo_id_entry( id );
6228 WCHAR bufferW[10];
6229 HKEY hkey;
6231 if (!geo)
6233 SetLastError( ERROR_INVALID_PARAMETER );
6234 return FALSE;
6236 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
6238 const WCHAR *name = geo->class == GEOCLASS_NATION ? L"Nation" : L"Region";
6239 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geo->id );
6240 RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6242 if (geo->class == GEOCLASS_NATION || wcscmp( geo->iso2, L"XX" ))
6243 lstrcpyW( bufferW, geo->iso2 );
6244 else
6245 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", geo->uncode );
6246 RegSetValueExW( hkey, L"Name", 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6247 RegCloseKey( hkey );
6249 return TRUE;
6253 /***********************************************************************
6254 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
6256 BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info,
6257 const SYSTEMTIME *system,
6258 SYSTEMTIME *local )
6260 TIME_ZONE_INFORMATION tzinfo;
6261 LARGE_INTEGER ft;
6263 if (!info)
6265 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6266 info = &tzinfo;
6269 if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE;
6270 switch (get_timezone_id( info, ft, FALSE ))
6272 case TIME_ZONE_ID_UNKNOWN:
6273 ft.QuadPart -= info->Bias * (LONGLONG)600000000;
6274 break;
6275 case TIME_ZONE_ID_STANDARD:
6276 ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6277 break;
6278 case TIME_ZONE_ID_DAYLIGHT:
6279 ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6280 break;
6281 default:
6282 return FALSE;
6284 return FileTimeToSystemTime( (FILETIME *)&ft, local );
6288 /***********************************************************************
6289 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
6291 BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info,
6292 const SYSTEMTIME *local,
6293 SYSTEMTIME *system )
6295 TIME_ZONE_INFORMATION tzinfo;
6296 LARGE_INTEGER ft;
6298 if (!info)
6300 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6301 info = &tzinfo;
6304 if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE;
6305 switch (get_timezone_id( info, ft, TRUE ))
6307 case TIME_ZONE_ID_UNKNOWN:
6308 ft.QuadPart += info->Bias * (LONGLONG)600000000;
6309 break;
6310 case TIME_ZONE_ID_STANDARD:
6311 ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6312 break;
6313 case TIME_ZONE_ID_DAYLIGHT:
6314 ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6315 break;
6316 default:
6317 return FALSE;
6319 return FileTimeToSystemTime( (FILETIME *)&ft, system );
6323 /***********************************************************************
6324 * VerLanguageNameA (kernelbase.@)
6326 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
6328 return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6332 /***********************************************************************
6333 * VerLanguageNameW (kernelbase.@)
6335 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
6337 return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6341 /***********************************************************************
6342 * WideCharToMultiByte (kernelbase.@)
6344 INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
6345 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
6347 const CPTABLEINFO *info;
6348 int ret;
6350 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6352 SetLastError( ERROR_INVALID_PARAMETER );
6353 return 0;
6356 if (srclen < 0) srclen = lstrlenW(src) + 1;
6358 switch (codepage)
6360 case CP_SYMBOL:
6361 ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
6362 break;
6363 case CP_UTF7:
6364 ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
6365 break;
6366 case CP_UNIXCP:
6367 codepage = unix_cp;
6368 /* fall through */
6369 default:
6370 if (!(info = get_codepage_table( codepage )))
6372 SetLastError( ERROR_INVALID_PARAMETER );
6373 return 0;
6375 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
6376 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
6378 SetLastError( ERROR_INVALID_FLAGS );
6379 return 0;
6381 if (info->CodePage == CP_UTF8)
6382 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
6383 else
6384 ret = wcstombs_codepage( info, flags, src, srclen, dst, dstlen, defchar, used );
6385 break;
6387 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
6388 return ret;
6392 /***********************************************************************
6393 * GetUserDefaultGeoName (kernelbase.@)
6395 INT WINAPI GetUserDefaultGeoName(LPWSTR geo_name, int count)
6397 WCHAR buffer[32];
6398 LSTATUS status;
6399 DWORD size;
6400 HKEY key;
6402 TRACE( "geo_name %p, count %d.\n", geo_name, count );
6404 if (count && !geo_name)
6406 SetLastError( ERROR_INVALID_PARAMETER );
6407 return 0;
6409 if (!(status = RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &key )))
6411 size = sizeof(buffer);
6412 status = RegQueryValueExW( key, L"Name", NULL, NULL, (BYTE *)buffer, &size );
6413 RegCloseKey( key );
6415 if (status)
6417 const struct geo_id *geo = find_geo_id_entry( GetUserGeoID( GEOCLASS_NATION ));
6418 if (geo && geo->id != 39070)
6419 lstrcpyW( buffer, geo->iso2 );
6420 else
6421 lstrcpyW( buffer, L"001" );
6423 size = lstrlenW( buffer ) + 1;
6424 if (count < size)
6426 if (!count)
6427 return size;
6428 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6429 return 0;
6431 lstrcpyW( geo_name, buffer );
6432 return size;
6436 /***********************************************************************
6437 * SetUserDefaultGeoName (kernelbase.@)
6439 BOOL WINAPI SetUserGeoName(PWSTR geo_name)
6441 const struct geo_id *geo;
6443 TRACE( "geo_name %s.\n", debugstr_w( geo_name ));
6445 if (!geo_name || !(geo = find_geo_name_entry( geo_name )))
6447 SetLastError( ERROR_INVALID_PARAMETER );
6448 return FALSE;
6450 return SetUserGeoID( geo->id );
6454 static void grouping_to_string( UINT grouping, WCHAR *buffer )
6456 WCHAR tmp[10], *p = tmp;
6458 while (grouping)
6460 *p++ = '0' + grouping % 10;
6461 grouping /= 10;
6463 while (p > tmp)
6465 *buffer++ = *(--p);
6466 if (p > tmp) *buffer++ = ';';
6468 *buffer = 0;
6472 static WCHAR *prepend_str( WCHAR *end, const WCHAR *str )
6474 UINT len = wcslen( str );
6475 return memcpy( end - len, str, len * sizeof(WCHAR) );
6479 /* format a positive number with decimal part; helper for get_number_format */
6480 static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decimal_sep,
6481 const WCHAR *thousand_sep, const WCHAR *grouping, UINT digits, BOOL lzero )
6483 const WCHAR *frac = NULL;
6484 BOOL round = FALSE;
6485 UINT i, len = 0;
6487 *(--end) = 0;
6489 for (i = 0; value[i]; i++)
6491 if (value[i] >= '0' && value[i] <= '9') continue;
6492 if (value[i] != '.') return NULL;
6493 if (frac) return NULL;
6494 frac = value + i + 1;
6497 /* format fractional part */
6499 len = frac ? wcslen( frac ) : 0;
6501 if (len > digits)
6503 round = frac[digits] >= '5';
6504 len = digits;
6506 while (digits > len)
6508 (*--end) = '0';
6509 digits--;
6511 while (len)
6513 WCHAR ch = frac[--len];
6514 if (round)
6516 if (ch != '9')
6518 ch++;
6519 round = FALSE;
6521 else ch = '0';
6523 *(--end) = ch;
6525 if (*end) end = prepend_str( end, decimal_sep );
6527 /* format integer part */
6529 len = frac ? frac - value - 1 : wcslen( value );
6531 while (len && *value == '0')
6533 value++;
6534 len--;
6536 if (len) lzero = FALSE;
6538 while (len)
6540 UINT limit = *grouping == '0' ? ~0u : *grouping - '0';
6541 while (len && limit--)
6543 WCHAR ch = value[--len];
6544 if (round)
6546 if (ch != '9')
6548 ch++;
6549 round = FALSE;
6551 else ch = '0';
6553 *(--end) = ch;
6555 if (len) end = prepend_str( end, thousand_sep );
6556 if (grouping[1] == ';') grouping += 2;
6558 if (round) *(--end) = '1';
6559 else if (lzero) *(--end) = '0';
6560 return end;
6564 static int get_number_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value,
6565 const NUMBERFMTW *format, WCHAR *buffer, int len )
6567 WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_neg[5], grouping[20], output[256];
6568 const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand;
6569 DWORD digits, lzero, order;
6570 int ret = 0;
6571 BOOL negative = (*value == '-');
6573 flags &= LOCALE_NOUSEROVERRIDE;
6575 if (!format)
6577 get_locale_info( locale, 0, LOCALE_SGROUPING | flags, grouping, ARRAY_SIZE(grouping) );
6578 get_locale_info( locale, 0, LOCALE_SDECIMAL | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) );
6579 get_locale_info( locale, 0, LOCALE_STHOUSAND | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) );
6580 get_locale_info( locale, 0, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER | flags,
6581 (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) );
6582 get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags,
6583 (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) );
6584 get_locale_info( locale, 0, LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER | flags,
6585 (WCHAR *)&order, sizeof(DWORD)/sizeof(WCHAR) );
6587 else
6589 if (flags)
6591 SetLastError( ERROR_INVALID_FLAGS );
6592 return 0;
6594 decimal_sep = format->lpDecimalSep;
6595 thousand_sep = format->lpThousandSep;
6596 grouping_to_string( format->Grouping, grouping );
6597 digits = format->NumDigits;
6598 lzero = format->LeadingZero;
6599 order = format->NegativeOrder;
6600 if (!decimal_sep || !thousand_sep)
6602 SetLastError( ERROR_INVALID_PARAMETER );
6603 return 0;
6607 if (negative)
6609 value++;
6610 get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) );
6613 if (!(num = format_number( output + ARRAY_SIZE(output) - 6, value,
6614 decimal_sep, thousand_sep, grouping, digits, lzero )))
6616 SetLastError( ERROR_INVALID_PARAMETER );
6617 return 0;
6620 if (negative)
6622 switch (order)
6624 case 0: /* (1.1) */
6625 num = prepend_str( num, L"(" );
6626 wcscat( num, L")" );
6627 break;
6628 case 2: /* - 1.1 */
6629 num = prepend_str( num, L" " );
6630 /* fall through */
6631 case 1: /* -1.1 */
6632 num = prepend_str( num, fmt_neg );
6633 break;
6634 case 4: /* 1.1 - */
6635 wcscat( num, L" " );
6636 /* fall through */
6637 case 3: /* 1.1- */
6638 wcscat( num, fmt_neg );
6639 break;
6640 default:
6641 SetLastError( ERROR_INVALID_PARAMETER );
6642 return 0;
6646 ret = wcslen( num ) + 1;
6647 if (!len) return ret;
6648 lstrcpynW( buffer, num, len );
6649 if (ret > len)
6651 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6652 return 0;
6654 return ret;
6658 static int get_currency_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value,
6659 const CURRENCYFMTW *format, WCHAR *buffer, int len )
6661 WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_symbol[13], fmt_neg[5], grouping[20], output[256];
6662 const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand, *symbol = fmt_symbol;
6663 DWORD digits, lzero, pos_order, neg_order;
6664 int ret = 0;
6665 BOOL negative = (*value == '-');
6667 flags &= LOCALE_NOUSEROVERRIDE;
6669 if (!format)
6671 get_locale_info( locale, 0, LOCALE_SCURRENCY | flags, fmt_symbol, ARRAY_SIZE(fmt_symbol) );
6672 get_locale_info( locale, 0, LOCALE_SMONGROUPING | flags, grouping, ARRAY_SIZE(grouping) );
6673 get_locale_info( locale, 0, LOCALE_SMONDECIMALSEP | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) );
6674 get_locale_info( locale, 0, LOCALE_SMONTHOUSANDSEP | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) );
6675 get_locale_info( locale, 0, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER | flags,
6676 (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) );
6677 get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags,
6678 (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) );
6679 get_locale_info( locale, 0, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | flags,
6680 (WCHAR *)&pos_order, sizeof(DWORD)/sizeof(WCHAR) );
6681 get_locale_info( locale, 0, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | flags,
6682 (WCHAR *)&neg_order, sizeof(DWORD)/sizeof(WCHAR) );
6684 else
6686 if (flags)
6688 SetLastError( ERROR_INVALID_FLAGS );
6689 return 0;
6691 decimal_sep = format->lpDecimalSep;
6692 thousand_sep = format->lpThousandSep;
6693 symbol = format->lpCurrencySymbol;
6694 grouping_to_string( format->Grouping, grouping );
6695 digits = format->NumDigits;
6696 lzero = format->LeadingZero;
6697 pos_order = format->PositiveOrder;
6698 neg_order = format->NegativeOrder;
6699 if (!decimal_sep || !thousand_sep || !symbol)
6701 SetLastError( ERROR_INVALID_PARAMETER );
6702 return 0;
6706 if (negative)
6708 value++;
6709 get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) );
6712 if (!(num = format_number( output + ARRAY_SIZE(output) - 20, value,
6713 decimal_sep, thousand_sep, grouping, digits, lzero )))
6715 SetLastError( ERROR_INVALID_PARAMETER );
6716 return 0;
6719 if (negative)
6721 switch (neg_order)
6723 case 14: /* ($ 1.1) */
6724 num = prepend_str( num, L" " );
6725 /* fall through */
6726 case 0: /* ($1.1) */
6727 num = prepend_str( num, symbol );
6728 num = prepend_str( num, L"(" );
6729 wcscat( num, L")" );
6730 break;
6731 case 9: /* -$ 1.1 */
6732 num = prepend_str( num, L" " );
6733 /* fall through */
6734 case 1: /* -$1.1 */
6735 num = prepend_str( num, symbol );
6736 num = prepend_str( num, fmt_neg );
6737 break;
6738 case 2: /* $-1.1 */
6739 num = prepend_str( num, fmt_neg );
6740 num = prepend_str( num, symbol );
6741 break;
6742 case 11: /* $ 1.1- */
6743 num = prepend_str( num, L" " );
6744 /* fall through */
6745 case 3: /* $1.1- */
6746 num = prepend_str( num, symbol );
6747 wcscat( num, fmt_neg );
6748 break;
6749 case 15: /* (1.1 $) */
6750 wcscat( num, L" " );
6751 /* fall through */
6752 case 4: /* (1.1$) */
6753 wcscat( num, symbol );
6754 num = prepend_str( num, L"(" );
6755 wcscat( num, L")" );
6756 break;
6757 case 8: /* -1.1 $ */
6758 wcscat( num, L" " );
6759 /* fall through */
6760 case 5: /* -1.1$ */
6761 num = prepend_str( num, fmt_neg );
6762 wcscat( num, symbol );
6763 break;
6764 case 6: /* 1.1-$ */
6765 wcscat( num, fmt_neg );
6766 wcscat( num, symbol );
6767 break;
6768 case 10: /* 1.1 $- */
6769 wcscat( num, L" " );
6770 /* fall through */
6771 case 7: /* 1.1$- */
6772 wcscat( num, symbol );
6773 wcscat( num, fmt_neg );
6774 break;
6775 case 12: /* $ -1.1 */
6776 num = prepend_str( num, fmt_neg );
6777 num = prepend_str( num, L" " );
6778 num = prepend_str( num, symbol );
6779 break;
6780 case 13: /* 1.1- $ */
6781 wcscat( num, fmt_neg );
6782 wcscat( num, L" " );
6783 wcscat( num, symbol );
6784 break;
6785 default:
6786 SetLastError( ERROR_INVALID_PARAMETER );
6787 return 0;
6790 else
6792 switch (pos_order)
6794 case 2: /* $ 1.1 */
6795 num = prepend_str( num, L" " );
6796 /* fall through */
6797 case 0: /* $1.1 */
6798 num = prepend_str( num, symbol );
6799 break;
6800 case 3: /* 1.1 $ */
6801 wcscat( num, L" " );
6802 /* fall through */
6803 case 1: /* 1.1$ */
6804 wcscat( num, symbol );
6805 break;
6806 default:
6807 SetLastError( ERROR_INVALID_PARAMETER );
6808 return 0;
6812 ret = wcslen( num ) + 1;
6813 if (!len) return ret;
6814 lstrcpynW( buffer, num, len );
6815 if (ret > len)
6817 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6818 return 0;
6820 return ret;
6824 /* get the length of a date/time formatting pattern */
6825 static int get_pattern_len( const WCHAR *pattern, const WCHAR *accept )
6827 int i;
6829 if (*pattern == '\'')
6831 for (i = 1; pattern[i]; i++)
6833 if (pattern[i] != '\'') continue;
6834 if (pattern[++i] != '\'') return i;
6836 return i;
6838 if (!wcschr( accept, *pattern )) return 1;
6839 for (i = 1; pattern[i]; i++) if (pattern[i] != pattern[0]) break;
6840 return i;
6844 static int get_date_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime,
6845 const WCHAR *format, WCHAR *buffer, int len )
6847 DWORD override = flags & LOCALE_NOUSEROVERRIDE;
6848 DWORD genitive = 0;
6849 WCHAR *p, fmt[80], output[256];
6850 SYSTEMTIME time;
6851 int ret, val, count, i;
6853 if (!format)
6855 if (flags & DATE_USE_ALT_CALENDAR) FIXME( "alt calendar not supported\n" );
6856 switch (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY))
6858 case 0:
6859 case DATE_SHORTDATE:
6860 get_locale_info( locale, 0, LOCALE_SSHORTDATE | override, fmt, ARRAY_SIZE(fmt) );
6861 break;
6862 case DATE_LONGDATE:
6863 get_locale_info( locale, 0, LOCALE_SLONGDATE | override, fmt, ARRAY_SIZE(fmt) );
6864 break;
6865 case DATE_YEARMONTH:
6866 get_locale_info( locale, 0, LOCALE_SYEARMONTH | override, fmt, ARRAY_SIZE(fmt) );
6867 break;
6868 case DATE_MONTHDAY:
6869 get_locale_info( locale, 0, LOCALE_SMONTHDAY | override, fmt, ARRAY_SIZE(fmt) );
6870 break;
6871 default:
6872 SetLastError( ERROR_INVALID_FLAGS );
6873 return 0;
6875 format = fmt;
6877 else if (override || (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY)))
6879 SetLastError( ERROR_INVALID_FLAGS );
6880 return 0;
6883 if (systime)
6885 FILETIME ft;
6887 time = *systime;
6888 time.wHour = time.wMinute = time.wSecond = time.wMilliseconds = 0;
6889 if (!SystemTimeToFileTime( &time, &ft ) || !FileTimeToSystemTime( &ft, &time )) return 0;
6891 else GetLocalTime( &time );
6893 for (p = output; *format; format += count)
6895 count = get_pattern_len( format, L"yMd" );
6897 switch (*format)
6899 case '\'':
6900 for (i = 1; i < count; i++)
6902 if (format[i] == '\'') i++;
6903 if (i < count) *p++ = format[i];
6905 break;
6907 case 'y':
6908 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%02u",
6909 (count <= 2) ? time.wYear % 100 : time.wYear );
6910 break;
6912 case 'M':
6913 if (count <= 2)
6915 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wMonth );
6916 break;
6918 val = (count == 3 ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1) + time.wMonth - 1;
6919 if (!genitive)
6921 for (i = count; format[i]; i += get_pattern_len( format, L"yMd" ))
6923 if (format[i] != 'd') continue;
6924 if (format[i + 1] != 'd' || format[i + 2] != 'd')
6925 genitive = LOCALE_RETURN_GENITIVE_NAMES;
6926 break;
6929 p += get_locale_info( locale, 0, val | override | genitive,
6930 p, output + ARRAY_SIZE(output) - p ) - 1;
6931 break;
6933 case 'd':
6934 if (count <= 2)
6936 genitive = LOCALE_RETURN_GENITIVE_NAMES;
6937 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wDay );
6938 break;
6940 genitive = 0;
6941 val = (count == 3 ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1) + (time.wDayOfWeek + 6) % 7;
6942 p += get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p ) - 1;
6943 break;
6945 case 'g':
6946 p += locale_return_string( count >= 2 ? locale->serastring : locale->sabbreverastring,
6947 override, p, output + ARRAY_SIZE(output) - p ) - 1;
6948 break;
6950 default:
6951 *p++ = *format;
6952 break;
6955 *p++ = 0;
6956 ret = p - output;
6958 if (!len) return ret;
6959 lstrcpynW( buffer, output, len );
6960 if (ret > len)
6962 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6963 return 0;
6965 return ret;
6969 static int get_time_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime,
6970 const WCHAR *format, WCHAR *buffer, int len )
6972 DWORD override = flags & LOCALE_NOUSEROVERRIDE;
6973 WCHAR *p, *last, fmt[80], output[256];
6974 SYSTEMTIME time;
6975 int i, ret, val, count;
6976 BOOL skip = FALSE;
6978 if (!format)
6980 get_locale_info( locale, 0, LOCALE_STIMEFORMAT | override, fmt, ARRAY_SIZE(fmt) );
6981 format = fmt;
6983 else if (override)
6985 SetLastError( ERROR_INVALID_FLAGS );
6986 return 0;
6989 if (systime)
6991 time = *systime;
6992 if (time.wMilliseconds > 999 || time.wSecond > 59 || time.wMinute > 59 || time.wHour > 23)
6994 SetLastError( ERROR_INVALID_PARAMETER );
6995 return 0;
6998 else GetLocalTime( &time );
7000 for (p = last = output; *format; format += count)
7002 count = get_pattern_len( format, L"Hhmst" );
7004 switch (*format)
7006 case '\'':
7007 for (i = 1; i < count; i++)
7009 if (format[i] == '\'') i++;
7010 if (!skip && i < count) *p++ = format[i];
7012 continue;
7014 case 'H':
7015 val = time.wHour;
7016 break;
7018 case 'h':
7019 val = time.wHour;
7020 if (!(flags & TIME_FORCE24HOURFORMAT))
7022 val %= 12;
7023 if (!val) val = 12;
7025 break;
7027 case 'm':
7028 if (flags & TIME_NOMINUTESORSECONDS)
7030 p = last;
7031 skip = TRUE;
7032 continue;
7034 val = time.wMinute;
7035 break;
7037 case 's':
7038 if (flags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS))
7040 p = last;
7041 skip = TRUE;
7042 continue;
7044 val = time.wSecond;
7045 break;
7047 case 't':
7048 if (flags & TIME_NOTIMEMARKER)
7050 p = last;
7051 skip = TRUE;
7052 continue;
7054 val = time.wHour < 12 ? LOCALE_S1159 : LOCALE_S2359;
7055 ret = get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p );
7056 p += (count > 1) ? ret - 1 : 1;
7057 skip = FALSE;
7058 continue;
7060 default:
7061 if (!skip || *format == ' ') *p++ = *format;
7062 continue;
7065 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", min( 2, count ), val );
7066 last = p;
7067 skip = FALSE;
7069 *p++ = 0;
7070 ret = p - output;
7072 if (!len) return ret;
7073 lstrcpynW( buffer, output, len );
7074 if (ret > len)
7076 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7077 return 0;
7079 return ret;
7083 /**************************************************************************
7084 * GetNumberFormatW (kernelbase.@)
7086 int WINAPI GetNumberFormatW( LCID lcid, DWORD flags, const WCHAR *value,
7087 const NUMBERFMTW *format, WCHAR *buffer, int len )
7089 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7091 if (len < 0 || (len && !buffer) || !value || !locale)
7093 SetLastError( ERROR_INVALID_PARAMETER );
7094 return 0;
7097 TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len );
7098 return get_number_format( locale, flags, value, format, buffer, len );
7102 /**************************************************************************
7103 * GetNumberFormatEx (kernelbase.@)
7105 int WINAPI GetNumberFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value,
7106 const NUMBERFMTW *format, WCHAR *buffer, int len )
7108 LCID lcid;
7109 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
7111 if (len < 0 || (len && !buffer) || !value || !locale)
7113 SetLastError( ERROR_INVALID_PARAMETER );
7114 return 0;
7117 TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len );
7118 return get_number_format( locale, flags, value, format, buffer, len );
7122 /***********************************************************************
7123 * GetCurrencyFormatW (kernelbase.@)
7125 int WINAPI GetCurrencyFormatW( LCID lcid, DWORD flags, const WCHAR *value,
7126 const CURRENCYFMTW *format, WCHAR *buffer, int len )
7128 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7130 if (len < 0 || (len && !buffer) || !value || !locale)
7132 SetLastError( ERROR_INVALID_PARAMETER );
7133 return 0;
7136 TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len );
7137 return get_currency_format( locale, flags, value, format, buffer, len );
7141 /***********************************************************************
7142 * GetCurrencyFormatEx (kernelbase.@)
7144 int WINAPI GetCurrencyFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value,
7145 const CURRENCYFMTW *format, WCHAR *buffer, int len )
7147 LCID lcid;
7148 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
7150 if (len < 0 || (len && !buffer) || !value || !locale)
7152 SetLastError( ERROR_INVALID_PARAMETER );
7153 return 0;
7156 TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len );
7157 return get_currency_format( locale, flags, value, format, buffer, len );
7161 /******************************************************************************
7162 * GetDateFormatA (KERNEL32.@)
7164 int WINAPI GetDateFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time,
7165 const char *format, char *buffer, int len )
7167 UINT cp = get_lcid_codepage( lcid, flags );
7168 WCHAR formatW[128], output[128];
7169 int ret;
7171 TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len );
7173 if (len < 0 || (len && !buffer))
7175 SetLastError( ERROR_INVALID_PARAMETER );
7176 return 0;
7178 if (format)
7180 MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) );
7181 ret = GetDateFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) );
7183 else ret = GetDateFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) );
7185 if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 );
7186 return ret;
7190 /***********************************************************************
7191 * GetDateFormatW (kernelbase.@)
7193 int WINAPI GetDateFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime,
7194 const WCHAR *format, WCHAR *buffer, int len )
7196 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7198 if (len < 0 || (len && !buffer) || !locale)
7200 SetLastError( ERROR_INVALID_PARAMETER );
7201 return 0;
7204 TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len );
7205 return get_date_format( locale, flags, systime, format, buffer, len );
7209 /***********************************************************************
7210 * GetDateFormatEx (kernelbase.@)
7212 int WINAPI GetDateFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime,
7213 const WCHAR *format, WCHAR *buffer, int len, const WCHAR *calendar )
7215 LCID lcid;
7216 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
7218 if (len < 0 || (len && !buffer) || !locale || calendar)
7220 SetLastError( ERROR_INVALID_PARAMETER );
7221 return 0;
7224 TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len );
7225 return get_date_format( locale, flags, systime, format, buffer, len );
7229 /******************************************************************************
7230 * GetTimeFormatA (kernelbase.@)
7232 int WINAPI GetTimeFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time,
7233 const char *format, char *buffer, int len )
7235 UINT cp = get_lcid_codepage( lcid, flags );
7236 WCHAR formatW[128], output[128];
7237 int ret;
7239 TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len );
7241 if (len < 0 || (len && !buffer))
7243 SetLastError( ERROR_INVALID_PARAMETER );
7244 return 0;
7246 if (format)
7248 MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) );
7249 ret = GetTimeFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) );
7251 else ret = GetTimeFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) );
7253 if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 );
7254 return ret;
7258 /***********************************************************************
7259 * GetTimeFormatW (kernelbase.@)
7261 int WINAPI GetTimeFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime,
7262 const WCHAR *format, WCHAR *buffer, int len )
7264 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7266 if (len < 0 || (len && !buffer) || !locale)
7268 SetLastError( ERROR_INVALID_PARAMETER );
7269 return 0;
7272 TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len );
7273 return get_time_format( locale, flags, systime, format, buffer, len );
7277 /***********************************************************************
7278 * GetTimeFormatEx (kernelbase.@)
7280 int WINAPI GetTimeFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime,
7281 const WCHAR *format, WCHAR *buffer, int len )
7283 LCID lcid;
7284 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
7286 if (len < 0 || (len && !buffer) || !locale)
7288 SetLastError( ERROR_INVALID_PARAMETER );
7289 return 0;
7292 TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len );
7293 return get_time_format( locale, flags, systime, format, buffer, len );