ntdll: Connect syscall frames across user callbacks on x86-64.
[wine.git] / dlls / kernelbase / locale.c
blobe8060fe9205ba045908c52701af25df5cfc3a71e
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 static HMODULE kernelbase_handle;
47 struct registry_entry
49 const WCHAR *value;
50 const WCHAR *subkey;
51 enum { NOT_CACHED, CACHED, MISSING } status;
52 WCHAR data[80];
55 static const WCHAR world_subkey[] = { 0xd83c, 0xdf0e, 0xd83c, 0xdf0f, 0xd83c, 0xdf0d, 0 }; /* 🌎🌏🌍 */
57 static struct registry_entry entry_icalendartype = { L"iCalendarType" };
58 static struct registry_entry entry_icountry = { L"iCountry" };
59 static struct registry_entry entry_icurrdigits = { L"iCurrDigits" };
60 static struct registry_entry entry_icurrency = { L"iCurrency" };
61 static struct registry_entry entry_idigits = { L"iDigits" };
62 static struct registry_entry entry_idigitsubstitution = { L"NumShape" };
63 static struct registry_entry entry_ifirstdayofweek = { L"iFirstDayOfWeek" };
64 static struct registry_entry entry_ifirstweekofyear = { L"iFirstWeekOfYear" };
65 static struct registry_entry entry_ilzero = { L"iLZero" };
66 static struct registry_entry entry_imeasure = { L"iMeasure" };
67 static struct registry_entry entry_inegcurr = { L"iNegCurr" };
68 static struct registry_entry entry_inegnumber = { L"iNegNumber" };
69 static struct registry_entry entry_ipapersize = { L"iPaperSize" };
70 static struct registry_entry entry_s1159 = { L"s1159" };
71 static struct registry_entry entry_s2359 = { L"s2359" };
72 static struct registry_entry entry_scurrency = { L"sCurrency" };
73 static struct registry_entry entry_sdecimal = { L"sDecimal" };
74 static struct registry_entry entry_sgrouping = { L"sGrouping" };
75 static struct registry_entry entry_sintlsymbol = { L"Currencies", world_subkey };
76 static struct registry_entry entry_slist = { L"sList" };
77 static struct registry_entry entry_slongdate = { L"sLongDate" };
78 static struct registry_entry entry_smondecimalsep = { L"sMonDecimalSep" };
79 static struct registry_entry entry_smongrouping = { L"sMonGrouping" };
80 static struct registry_entry entry_smonthousandsep = { L"sMonThousandSep" };
81 static struct registry_entry entry_snativedigits = { L"sNativeDigits" };
82 static struct registry_entry entry_snegativesign = { L"sNegativeSign" };
83 static struct registry_entry entry_spositivesign = { L"sPositiveSign" };
84 static struct registry_entry entry_sshortdate = { L"sShortDate" };
85 static struct registry_entry entry_sshorttime = { L"sShortTime" };
86 static struct registry_entry entry_sthousand = { L"sThousand" };
87 static struct registry_entry entry_stimeformat = { L"sTimeFormat" };
88 static struct registry_entry entry_syearmonth = { L"sYearMonth" };
91 static const struct { UINT cp; const WCHAR *name; } codepage_names[] =
93 { 37, L"IBM EBCDIC US Canada" },
94 { 424, L"IBM EBCDIC Hebrew" },
95 { 437, L"OEM United States" },
96 { 500, L"IBM EBCDIC International" },
97 { 708, L"Arabic ASMO" },
98 { 720, L"Arabic (Transparent ASMO)" },
99 { 737, L"OEM Greek 437G" },
100 { 775, L"OEM Baltic" },
101 { 850, L"OEM Multilingual Latin 1" },
102 { 852, L"OEM Slovak Latin 2" },
103 { 855, L"OEM Cyrillic" },
104 { 856, L"Hebrew PC" },
105 { 857, L"OEM Turkish" },
106 { 860, L"OEM Portuguese" },
107 { 861, L"OEM Icelandic" },
108 { 862, L"OEM Hebrew" },
109 { 863, L"OEM Canadian French" },
110 { 864, L"OEM Arabic" },
111 { 865, L"OEM Nordic" },
112 { 866, L"OEM Russian" },
113 { 869, L"OEM Greek" },
114 { 874, L"ANSI/OEM Thai" },
115 { 875, L"IBM EBCDIC Greek" },
116 { 878, L"Russian KOI8" },
117 { 932, L"ANSI/OEM Japanese Shift-JIS" },
118 { 936, L"ANSI/OEM Simplified Chinese GBK" },
119 { 949, L"ANSI/OEM Korean Unified Hangul" },
120 { 950, L"ANSI/OEM Traditional Chinese Big5" },
121 { 1006, L"IBM Arabic" },
122 { 1026, L"IBM EBCDIC Latin 5 Turkish" },
123 { 1250, L"ANSI Eastern Europe" },
124 { 1251, L"ANSI Cyrillic" },
125 { 1252, L"ANSI Latin 1" },
126 { 1253, L"ANSI Greek" },
127 { 1254, L"ANSI Turkish" },
128 { 1255, L"ANSI Hebrew" },
129 { 1256, L"ANSI Arabic" },
130 { 1257, L"ANSI Baltic" },
131 { 1258, L"ANSI/OEM Viet Nam" },
132 { 1361, L"Korean Johab" },
133 { 10000, L"Mac Roman" },
134 { 10001, L"Mac Japanese" },
135 { 10002, L"Mac Traditional Chinese" },
136 { 10003, L"Mac Korean" },
137 { 10004, L"Mac Arabic" },
138 { 10005, L"Mac Hebrew" },
139 { 10006, L"Mac Greek" },
140 { 10007, L"Mac Cyrillic" },
141 { 10008, L"Mac Simplified Chinese" },
142 { 10010, L"Mac Romanian" },
143 { 10017, L"Mac Ukrainian" },
144 { 10021, L"Mac Thai" },
145 { 10029, L"Mac Latin 2" },
146 { 10079, L"Mac Icelandic" },
147 { 10081, L"Mac Turkish" },
148 { 10082, L"Mac Croatian" },
149 { 20127, L"US-ASCII (7bit)" },
150 { 20866, L"Russian KOI8" },
151 { 20932, L"EUC-JP" },
152 { 20949, L"Korean Wansung" },
153 { 21866, L"Ukrainian KOI8" },
154 { 28591, L"ISO 8859-1 Latin 1" },
155 { 28592, L"ISO 8859-2 Latin 2 (East European)" },
156 { 28593, L"ISO 8859-3 Latin 3 (South European)" },
157 { 28594, L"ISO 8859-4 Latin 4 (Baltic old)" },
158 { 28595, L"ISO 8859-5 Cyrillic" },
159 { 28596, L"ISO 8859-6 Arabic" },
160 { 28597, L"ISO 8859-7 Greek" },
161 { 28598, L"ISO 8859-8 Hebrew" },
162 { 28599, L"ISO 8859-9 Latin 5 (Turkish)" },
163 { 28600, L"ISO 8859-10 Latin 6 (Nordic)" },
164 { 28601, L"ISO 8859-11 Latin (Thai)" },
165 { 28603, L"ISO 8859-13 Latin 7 (Baltic)" },
166 { 28604, L"ISO 8859-14 Latin 8 (Celtic)" },
167 { 28605, L"ISO 8859-15 Latin 9 (Euro)" },
168 { 28606, L"ISO 8859-16 Latin 10 (Balkan)" },
169 { 65000, L"65000 (UTF-7)" },
170 { 65001, L"65001 (UTF-8)" }
173 /* Unicode expanded ligatures */
174 static const WCHAR ligatures[][5] =
176 { 0x00c6, 'A','E',0 },
177 { 0x00de, 'T','H',0 },
178 { 0x00df, 's','s',0 },
179 { 0x00e6, 'a','e',0 },
180 { 0x00fe, 't','h',0 },
181 { 0x0132, 'I','J',0 },
182 { 0x0133, 'i','j',0 },
183 { 0x0152, 'O','E',0 },
184 { 0x0153, 'o','e',0 },
185 { 0x01c4, 'D',0x017d,0 },
186 { 0x01c5, 'D',0x017e,0 },
187 { 0x01c6, 'd',0x017e,0 },
188 { 0x01c7, 'L','J',0 },
189 { 0x01c8, 'L','j',0 },
190 { 0x01c9, 'l','j',0 },
191 { 0x01ca, 'N','J',0 },
192 { 0x01cb, 'N','j',0 },
193 { 0x01cc, 'n','j',0 },
194 { 0x01e2, 0x0100,0x0112,0 },
195 { 0x01e3, 0x0101,0x0113,0 },
196 { 0x01f1, 'D','Z',0 },
197 { 0x01f2, 'D','z',0 },
198 { 0x01f3, 'd','z',0 },
199 { 0x01fc, 0x00c1,0x00c9,0 },
200 { 0x01fd, 0x00e1,0x00e9,0 },
201 { 0x05f0, 0x05d5,0x05d5,0 },
202 { 0x05f1, 0x05d5,0x05d9,0 },
203 { 0x05f2, 0x05d9,0x05d9,0 },
204 { 0xfb00, 'f','f',0 },
205 { 0xfb01, 'f','i',0 },
206 { 0xfb02, 'f','l',0 },
207 { 0xfb03, 'f','f','i',0 },
208 { 0xfb04, 'f','f','l',0 },
209 { 0xfb05, 0x017f,'t',0 },
210 { 0xfb06, 's','t',0 },
213 struct calendar
215 USHORT icalintvalue; /* 00 */
216 USHORT itwodigityearmax; /* 02 */
217 UINT sshortdate; /* 04 */
218 UINT syearmonth; /* 08 */
219 UINT slongdate; /* 0c */
220 UINT serastring; /* 10 */
221 UINT iyearoffsetrange; /* 14 */
222 UINT sdayname; /* 18 */
223 UINT sabbrevdayname; /* 1c */
224 UINT smonthname; /* 20 */
225 UINT sabbrevmonthname; /* 24 */
226 UINT scalname; /* 28 */
227 UINT smonthday; /* 2c */
228 UINT sabbreverastring; /* 30 */
229 UINT sshortestdayname; /* 34 */
230 UINT srelativelongdate; /* 38 */
231 UINT unused[3]; /* 3c */
234 static const struct geo_id
236 GEOID id;
237 WCHAR latitude[12];
238 WCHAR longitude[12];
239 GEOCLASS class;
240 GEOID parent;
241 WCHAR iso2[4];
242 WCHAR iso3[4];
243 USHORT uncode;
244 USHORT dialcode;
245 WCHAR currcode[4];
246 WCHAR currsymbol[8];
247 } *geo_ids;
249 static const struct geo_index
251 WCHAR name[4];
252 UINT idx;
253 } *geo_index;
255 static unsigned int geo_ids_count;
256 static unsigned int geo_index_count;
258 enum charmaps
260 CHARMAP_FOLDDIGITS,
261 CHARMAP_COMPAT,
262 CHARMAP_HIRAGANA,
263 CHARMAP_KATAKANA,
264 CHARMAP_HALFWIDTH,
265 CHARMAP_FULLWIDTH,
266 CHARMAP_TRADITIONAL,
267 CHARMAP_SIMPLIFIED,
268 NB_CHARMAPS
271 static const USHORT *charmaps[NB_CHARMAPS];
273 /* NLS normalization file */
274 struct norm_table
276 WCHAR name[13]; /* 00 file name */
277 USHORT checksum[3]; /* 1a checksum? */
278 USHORT version[4]; /* 20 Unicode version */
279 USHORT form; /* 28 normalization form */
280 USHORT len_factor; /* 2a factor for length estimates */
281 USHORT unknown1; /* 2c */
282 USHORT decomp_size; /* 2e decomposition hash size */
283 USHORT comp_size; /* 30 composition hash size */
284 USHORT unknown2; /* 32 */
285 USHORT classes; /* 34 combining classes table offset */
286 USHORT props_level1; /* 36 char properties table level 1 offset */
287 USHORT props_level2; /* 38 char properties table level 2 offset */
288 USHORT decomp_hash; /* 3a decomposition hash table offset */
289 USHORT decomp_map; /* 3c decomposition character map table offset */
290 USHORT decomp_seq; /* 3e decomposition character sequences offset */
291 USHORT comp_hash; /* 40 composition hash table offset */
292 USHORT comp_seq; /* 42 composition character sequences offset */
293 /* BYTE[] combining class values */
294 /* BYTE[0x2200] char properties index level 1 */
295 /* BYTE[] char properties index level 2 */
296 /* WORD[] decomposition hash table */
297 /* WORD[] decomposition character map */
298 /* WORD[] decomposition character sequences */
299 /* WORD[] composition hash table */
300 /* WORD[] composition character sequences */
303 static CPTABLEINFO ansi_cpinfo;
304 static CPTABLEINFO oem_cpinfo;
305 static UINT unix_cp = CP_UTF8;
306 static LCID system_lcid;
307 static LCID user_lcid;
308 static HKEY intl_key;
309 static HKEY nls_key;
310 static HKEY tz_key;
311 static const NLS_LOCALE_LCID_INDEX *lcids_index;
312 static const NLS_LOCALE_LCNAME_INDEX *lcnames_index;
313 static const NLS_LOCALE_HEADER *locale_table;
314 static const WCHAR *locale_strings;
315 static const NLS_LOCALE_DATA *system_locale;
316 static const NLS_LOCALE_DATA *user_locale;
318 static CPTABLEINFO codepages[128];
319 static unsigned int nb_codepages;
321 static struct norm_table *norm_info;
323 struct sortguid
325 GUID id; /* sort GUID */
326 UINT flags; /* flags */
327 UINT compr; /* offset to compression table */
328 UINT except; /* exception table offset in sortkey table */
329 UINT ling_except; /* exception table offset for linguistic casing */
330 UINT casemap; /* linguistic casemap table offset */
333 /* flags for sortguid */
334 #define FLAG_HAS_3_BYTE_WEIGHTS 0x01
335 #define FLAG_REVERSEDIACRITICS 0x10
336 #define FLAG_DOUBLECOMPRESSION 0x20
337 #define FLAG_INVERSECASING 0x40
339 struct sort_expansion
341 WCHAR exp[2];
344 struct jamo_sort
346 BYTE is_old;
347 BYTE leading;
348 BYTE vowel;
349 BYTE trailing;
350 BYTE weight;
351 BYTE pad[3];
354 struct sort_compression
356 UINT offset;
357 WCHAR minchar, maxchar;
358 WORD len[8];
361 static inline int compression_size( int len ) { return 2 + len + (len & 1); }
363 union char_weights
365 UINT val;
366 struct { BYTE primary, script, diacritic, _case; };
369 /* bits for case weights */
370 #define CASE_FULLWIDTH 0x01 /* full width kana (vs. half width) */
371 #define CASE_FULLSIZE 0x02 /* full size kana (vs. small) */
372 #define CASE_SUBSCRIPT 0x08 /* sub/super script */
373 #define CASE_UPPER 0x10 /* upper case */
374 #define CASE_KATAKANA 0x20 /* katakana (vs. hiragana) */
375 #define CASE_COMPR_2 0x40 /* compression exists for >= 2 chars */
376 #define CASE_COMPR_4 0x80 /* compression exists for >= 4 chars */
377 #define CASE_COMPR_6 0xc0 /* compression exists for >= 6 chars */
379 enum sortkey_script
381 SCRIPT_UNSORTABLE = 0,
382 SCRIPT_NONSPACE_MARK = 1,
383 SCRIPT_EXPANSION = 2,
384 SCRIPT_EASTASIA_SPECIAL = 3,
385 SCRIPT_JAMO_SPECIAL = 4,
386 SCRIPT_EXTENSION_A = 5,
387 SCRIPT_PUNCTUATION = 6,
388 SCRIPT_SYMBOL_1 = 7,
389 SCRIPT_SYMBOL_2 = 8,
390 SCRIPT_SYMBOL_3 = 9,
391 SCRIPT_SYMBOL_4 = 10,
392 SCRIPT_SYMBOL_5 = 11,
393 SCRIPT_SYMBOL_6 = 12,
394 SCRIPT_DIGIT = 13,
395 SCRIPT_LATIN = 14,
396 SCRIPT_GREEK = 15,
397 SCRIPT_CYRILLIC = 16,
398 SCRIPT_KANA = 34,
399 SCRIPT_HEBREW = 40,
400 SCRIPT_ARABIC = 41,
401 SCRIPT_PUA_FIRST = 169,
402 SCRIPT_PUA_LAST = 175,
403 SCRIPT_CJK_FIRST = 192,
404 SCRIPT_CJK_LAST = 239,
407 static const struct sortguid **locale_sorts;
408 static const struct sortguid *current_locale_sort;
410 static struct
412 UINT version; /* NLS version */
413 UINT guid_count; /* number of sort GUIDs */
414 UINT exp_count; /* number of character expansions */
415 UINT compr_count; /* number of compression tables */
416 const UINT *keys; /* sortkey table, indexed by char */
417 const USHORT *casemap; /* casemap table, in l_intl.nls format */
418 const WORD *ctypes; /* CT_CTYPE1,2,3 values */
419 const BYTE *ctype_idx; /* index to map char to ctypes array entry */
420 const struct sortguid *guids; /* table of sort GUIDs */
421 const struct sort_expansion *expansions; /* character expansions */
422 const struct sort_compression *compressions; /* character compression tables */
423 const WCHAR *compr_data; /* data for individual compressions */
424 const struct jamo_sort *jamo; /* table for Jamo compositions */
425 } sort;
427 static CRITICAL_SECTION locale_section;
428 static CRITICAL_SECTION_DEBUG critsect_debug =
430 0, 0, &locale_section,
431 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
432 0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") }
434 static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
437 static void load_locale_nls(void)
439 struct
441 UINT ctypes;
442 UINT unknown1;
443 UINT unknown2;
444 UINT unknown3;
445 UINT locales;
446 UINT charmaps;
447 UINT geoids;
448 UINT scripts;
449 } *header;
450 struct geo_header
452 WCHAR signature[4]; /* L"geo" */
453 UINT total_size;
454 UINT ids_offset;
455 UINT ids_count;
456 UINT index_offset;
457 UINT index_count;
458 } *geo_header;
460 LARGE_INTEGER dummy;
461 const USHORT *map_ptr;
462 unsigned int i;
464 RtlGetLocaleFileMappingAddress( (void **)&header, &system_lcid, &dummy );
465 locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales);
466 lcids_index = (const NLS_LOCALE_LCID_INDEX *)((char *)locale_table + locale_table->lcids_offset);
467 lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset);
468 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
469 geo_header = (struct geo_header *)((char *)header + header->geoids);
470 geo_ids = (const struct geo_id *)((char *)geo_header + geo_header->ids_offset);
471 geo_index = (const struct geo_index *)((char *)geo_header + geo_header->index_offset);
472 geo_ids_count = geo_header->ids_count;
473 geo_index_count = geo_header->index_count;
474 map_ptr = (const USHORT *)((char *)header + header->charmaps);
475 for (i = 0; i < NB_CHARMAPS; i++, map_ptr += *map_ptr) charmaps[i] = map_ptr + 1;
479 static void load_sortdefault_nls(void)
481 const struct
483 UINT sortkeys;
484 UINT casemaps;
485 UINT ctypes;
486 UINT sortids;
487 } *header;
489 const WORD *ctype;
490 const UINT *table;
491 UINT i;
492 SIZE_T size;
493 const struct sort_compression *last_compr;
495 NtGetNlsSectionPtr( 9, 0, NULL, (void **)&header, &size );
497 sort.keys = (UINT *)((char *)header + header->sortkeys);
498 sort.casemap = (USHORT *)((char *)header + header->casemaps);
500 ctype = (WORD *)((char *)header + header->ctypes);
501 sort.ctypes = ctype + 2;
502 sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2;
504 table = (UINT *)((char *)header + header->sortids);
505 sort.version = table[0];
506 sort.guid_count = table[1];
507 sort.guids = (struct sortguid *)(table + 2);
509 table = (UINT *)(sort.guids + sort.guid_count);
510 sort.exp_count = table[0];
511 sort.expansions = (struct sort_expansion *)(table + 1);
513 table = (UINT *)(sort.expansions + sort.exp_count);
514 sort.compr_count = table[0];
515 sort.compressions = (struct sort_compression *)(table + 1);
516 sort.compr_data = (WCHAR *)(sort.compressions + sort.compr_count);
518 last_compr = sort.compressions + sort.compr_count - 1;
519 table = (UINT *)(sort.compr_data + last_compr->offset);
520 for (i = 0; i < 7; i++) table += last_compr->len[i] * ((i + 5) / 2);
521 table += 1 + table[0] / 2; /* skip multiple weights */
522 sort.jamo = (struct jamo_sort *)(table + 1);
524 locale_sorts = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
525 locale_table->nb_lcnames * sizeof(*locale_sorts) );
529 static const struct sortguid *find_sortguid( const GUID *guid )
531 int pos, ret, min = 0, max = sort.guid_count - 1;
533 while (min <= max)
535 pos = (min + max) / 2;
536 ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) );
537 if (!ret) return &sort.guids[pos];
538 if (ret > 0) min = pos + 1;
539 else max = pos - 1;
541 ERR( "no sort found for %s\n", debugstr_guid( guid ));
542 return NULL;
546 static const NLS_LOCALE_DATA *get_locale_data( UINT idx )
548 ULONG offset = locale_table->locales_offset + idx * locale_table->locale_size;
549 return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
553 static const struct calendar *get_calendar_data( const NLS_LOCALE_DATA *locale, UINT id )
555 if (id == CAL_HIJRI) id = locale->islamic_cal[0];
556 else if (id == CAL_PERSIAN) id = locale->islamic_cal[1];
558 if (!id || id > locale_table->nb_calendars) return NULL;
559 return (const struct calendar *)((const char *)locale_table + locale_table->calendars_offset +
560 (id - 1) * locale_table->calendar_size);
564 static int compare_locale_names( const WCHAR *n1, const WCHAR *n2 )
566 for (;;)
568 WCHAR ch1 = *n1++;
569 WCHAR ch2 = *n2++;
570 if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A';
571 else if (ch1 == '_') ch1 = '-';
572 if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A';
573 else if (ch2 == '_') ch2 = '-';
574 if (!ch1 || ch1 != ch2) return ch1 - ch2;
579 static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry( const WCHAR *name )
581 int min = 0, max = locale_table->nb_lcnames - 1;
583 while (min <= max)
585 int res, pos = (min + max) / 2;
586 const WCHAR *str = locale_strings + lcnames_index[pos].name;
587 res = compare_locale_names( name, str + 1 );
588 if (res < 0) max = pos - 1;
589 else if (res > 0) min = pos + 1;
590 else return &lcnames_index[pos];
592 return NULL;
596 static const NLS_LOCALE_LCID_INDEX *find_lcid_entry( LCID lcid )
598 int min = 0, max = locale_table->nb_lcids - 1;
600 while (min <= max)
602 int pos = (min + max) / 2;
603 if (lcid < lcids_index[pos].id) max = pos - 1;
604 else if (lcid > lcids_index[pos].id) min = pos + 1;
605 else return &lcids_index[pos];
607 return NULL;
611 static const struct geo_id *find_geo_id_entry( GEOID id )
613 int min = 0, max = geo_ids_count - 1;
615 while (min <= max)
617 int pos = (min + max) / 2;
618 if (id < geo_ids[pos].id) max = pos - 1;
619 else if (id > geo_ids[pos].id) min = pos + 1;
620 else return &geo_ids[pos];
622 return NULL;
626 static const struct geo_id *find_geo_name_entry( const WCHAR *name )
628 int min = 0, max = geo_index_count - 1;
630 while (min <= max)
632 int res, pos = (min + max) / 2;
633 res = wcsicmp( name, geo_index[pos].name );
634 if (res < 0) max = pos - 1;
635 else if (res > 0) min = pos + 1;
636 else return &geo_ids[geo_index[pos].idx];
638 return NULL;
642 static const NLS_LOCALE_DATA *get_locale_by_name( const WCHAR *name, LCID *lcid )
644 const NLS_LOCALE_LCNAME_INDEX *entry;
646 if (name == LOCALE_NAME_USER_DEFAULT)
648 *lcid = user_lcid;
649 return user_locale;
651 if (!(entry = find_lcname_entry( name ))) return NULL;
652 *lcid = entry->id;
653 return get_locale_data( entry->idx );
657 static const struct sortguid *get_language_sort( const WCHAR *name )
659 const NLS_LOCALE_LCNAME_INDEX *entry;
660 const NLS_LOCALE_DATA *locale;
661 WCHAR guidstr[39];
662 const struct sortguid *ret;
663 UNICODE_STRING str;
664 LCID lcid;
665 GUID guid;
666 HKEY key = 0;
667 DWORD size, type;
669 if (name == LOCALE_NAME_USER_DEFAULT)
671 if (current_locale_sort) return current_locale_sort;
672 name = locale_strings + user_locale->sname + 1;
675 if (!(entry = find_lcname_entry( name )))
677 WARN( "unknown locale %s\n", debugstr_w(name) );
678 SetLastError( ERROR_INVALID_PARAMETER );
679 return NULL;
681 if ((ret = locale_sorts[entry - lcnames_index])) return ret;
683 lcid = entry->id;
684 name = locale_strings + entry->name + 1;
685 locale = get_locale_data( entry->idx );
686 if (!RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key ))
688 for (;;)
690 size = sizeof(guidstr);
691 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ)
693 RtlInitUnicodeString( &str, guidstr );
694 if (!RtlGUIDFromString( &str, &guid )) ret = find_sortguid( &guid );
695 break;
697 if (!name[0]) break;
698 name = locale_strings + (SORTIDFROMLCID( lcid ) ? locale->sname : locale->sparent) + 1;
699 if (!(locale = get_locale_by_name( name, &lcid ))) break;
701 RegCloseKey( key );
703 if (!ret) ret = &sort.guids[0];
704 locale_sorts[entry - lcnames_index] = ret;
705 return ret;
709 /******************************************************************************
710 * NlsValidateLocale (kernelbase.@)
712 * Note: it seems to return some internal data on Windows, we simply return the locale.nls data pointer.
714 const NLS_LOCALE_DATA * WINAPI NlsValidateLocale( LCID *lcid, ULONG flags )
716 const NLS_LOCALE_LCNAME_INDEX *name_entry;
717 const NLS_LOCALE_LCID_INDEX *entry;
718 const NLS_LOCALE_DATA *locale;
720 switch (*lcid)
722 case LOCALE_SYSTEM_DEFAULT:
723 *lcid = system_lcid;
724 return system_locale;
725 case LOCALE_NEUTRAL:
726 case LOCALE_USER_DEFAULT:
727 case LOCALE_CUSTOM_DEFAULT:
728 case LOCALE_CUSTOM_UNSPECIFIED:
729 case LOCALE_CUSTOM_UI_DEFAULT:
730 *lcid = user_lcid;
731 return user_locale;
732 default:
733 if (!(entry = find_lcid_entry( *lcid ))) return NULL;
734 locale = get_locale_data( entry->idx );
735 if ((flags & LOCALE_ALLOW_NEUTRAL_NAMES) || locale->inotneutral) return locale;
736 if ((name_entry = find_lcname_entry( locale_strings + locale->ssortlocale + 1 )))
737 locale = get_locale_data( name_entry->idx );
738 return locale;
743 static int locale_return_data( const WCHAR *data, int datalen, LCTYPE type, WCHAR *buffer, int len )
745 if (type & LOCALE_RETURN_NUMBER)
747 SetLastError( ERROR_INVALID_FLAGS );
748 return 0;
751 if (!len) return datalen;
752 if (datalen > len)
754 SetLastError( ERROR_INSUFFICIENT_BUFFER );
755 return 0;
757 memcpy( buffer, data, datalen * sizeof(WCHAR) );
758 return datalen;
762 static BOOL set_registry_entry( struct registry_entry *entry, const WCHAR *data )
764 DWORD size = (wcslen(data) + 1) * sizeof(WCHAR);
765 LSTATUS ret;
767 if (size > sizeof(entry->data))
769 SetLastError( ERROR_INVALID_FLAGS );
770 return FALSE;
772 TRACE( "setting %s to %s\n", debugstr_w(entry->value), debugstr_w(data) );
774 RtlEnterCriticalSection( &locale_section );
775 if (!(ret = RegSetKeyValueW( intl_key, entry->subkey, entry->value, REG_SZ, (BYTE *)data, size )))
777 wcscpy( entry->data, data );
778 entry->status = CACHED;
780 RtlLeaveCriticalSection( &locale_section );
781 if (ret) SetLastError( ret );
782 return !ret;
786 static int locale_return_reg_string( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
788 DWORD size;
789 LRESULT res;
790 int ret = -1;
792 if (type & LOCALE_NOUSEROVERRIDE) return -1;
794 RtlEnterCriticalSection( &locale_section );
795 switch (entry->status)
797 case NOT_CACHED:
798 size = sizeof(entry->data);
799 if (entry->subkey)
801 HKEY key;
802 if (!(res = RegOpenKeyExW( intl_key, entry->subkey, 0, KEY_READ, &key )))
804 res = RegQueryValueExW( key, entry->value, NULL, NULL, (BYTE *)entry->data, &size );
805 RegCloseKey( key );
808 else res = RegQueryValueExW( intl_key, entry->value, NULL, NULL, (BYTE *)entry->data, &size );
810 if (res)
812 entry->status = MISSING;
813 break;
815 entry->status = CACHED;
816 /* fall through */
817 case CACHED:
818 ret = locale_return_data( entry->data, wcslen(entry->data) + 1, type, buffer, len );
819 break;
820 case MISSING:
821 break;
823 RtlLeaveCriticalSection( &locale_section );
824 return ret;
828 static int locale_return_string( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
830 return locale_return_data( locale_strings + pos + 1, locale_strings[pos] + 1, type, buffer, len );
834 static int locale_return_number( UINT val, LCTYPE type, WCHAR *buffer, int len )
836 int ret;
837 WCHAR tmp[80];
839 if (!(type & LOCALE_RETURN_NUMBER))
841 switch (LOWORD(type))
843 case LOCALE_ILANGUAGE:
844 case LOCALE_IDEFAULTLANGUAGE:
845 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%04x", val ) + 1;
846 break;
847 case LOCALE_IDEFAULTEBCDICCODEPAGE:
848 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", val ) + 1;
849 break;
850 default:
851 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val ) + 1;
852 break;
855 else ret = sizeof(UINT) / sizeof(WCHAR);
857 if (!len) return ret;
858 if (ret > len)
860 SetLastError( ERROR_INSUFFICIENT_BUFFER );
861 return 0;
864 if (type & LOCALE_RETURN_NUMBER) memcpy( buffer, &val, sizeof(val) );
865 else wcscpy( buffer, tmp );
867 return ret;
871 static int locale_return_reg_number( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len )
873 int ret, val;
874 WCHAR *end, tmp[80];
876 if (type & LOCALE_RETURN_NUMBER)
878 ret = locale_return_reg_string( entry, type & ~LOCALE_RETURN_NUMBER, tmp, ARRAY_SIZE( tmp ));
879 if (ret == -1) return ret;
880 val = wcstol( tmp, &end, 10 );
881 if (*end) /* invalid number */
883 SetLastError( ERROR_INVALID_FLAGS );
884 return 0;
886 return locale_return_number( val, type, buffer, len );
888 return locale_return_reg_string( entry, type, buffer, len );
892 static int locale_return_grouping( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
894 WORD i, count = locale_strings[pos];
895 const WCHAR *str = locale_strings + pos + 1;
896 int ret;
898 if (type & LOCALE_RETURN_NUMBER)
900 SetLastError( ERROR_INVALID_FLAGS );
901 return 0;
903 ret = 2 * count;
904 if (str[count - 1]) ret += 2; /* for final zero */
906 if (!len) return ret;
907 if (ret > len)
909 SetLastError( ERROR_INSUFFICIENT_BUFFER );
910 return 0;
912 for (i = 0; i < count; i++)
914 if (!str[i]) /* explicit null termination */
916 buffer[-1] = 0;
917 return ret;
919 *buffer++ = '0' + str[i];
920 *buffer++ = ';';
922 *buffer++ = '0';
923 *buffer = 0;
924 return ret;
928 static int locale_return_strarray( DWORD pos, WORD idx, LCTYPE type, WCHAR *buffer, int len )
930 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
931 WORD count = locale_strings[pos];
933 return locale_return_string( idx < count ? array[idx] : 0, type, buffer, len );
937 static int locale_return_strarray_concat( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
939 WORD i, count = locale_strings[pos];
940 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
941 int ret;
943 if (type & LOCALE_RETURN_NUMBER)
945 SetLastError( ERROR_INVALID_FLAGS );
946 return 0;
948 for (i = 0, ret = 1; i < count; i++) ret += locale_strings[array[i]];
950 if (!len) return ret;
951 if (ret > len)
953 SetLastError( ERROR_INSUFFICIENT_BUFFER );
954 return 0;
956 for (i = 0; i < count; i++)
958 memcpy( buffer, locale_strings + array[i] + 1, locale_strings[array[i]] * sizeof(WCHAR) );
959 buffer += locale_strings[array[i]];
961 *buffer = 0;
962 return ret;
966 static int cal_return_number( UINT val, CALTYPE type, WCHAR *buffer, int len, DWORD *value )
968 WCHAR tmp[12];
969 int ret;
971 if (type & CAL_RETURN_NUMBER)
973 *value = val;
974 return sizeof(UINT) / sizeof(WCHAR);
976 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val );
977 return locale_return_data( tmp, ret + 1, 0, buffer, len );
981 /* find the first format char in a format string */
982 static WCHAR *find_format( WCHAR *str, const WCHAR *accept )
984 for ( ; *str; str++)
986 if (*str == '\'')
988 if (!(str = wcschr( str + 1, '\'' ))) return NULL;
990 else if (wcschr( accept, *str ))
992 /* ignore "ddd" and "dddd" */
993 if (str[0] != 'd' || str[1] != 'd' || str[2] != 'd') return str;
994 str += 2;
995 while (str[1] == 'd') str++;
998 return NULL;
1002 /* replace the separator in a date/time format string */
1003 static WCHAR *locale_replace_separator( WCHAR *buffer, const WCHAR *sep )
1005 UINT pos = 0;
1006 WCHAR res[80];
1007 WCHAR *next, *str = find_format( buffer, L"dMyHhms" );
1009 if (!str) return buffer;
1010 pos = str - buffer;
1011 memcpy( res, buffer, pos * sizeof(WCHAR) );
1012 for (;;)
1014 res[pos++] = *str++;
1015 while (str[0] == str[-1]) res[pos++] = *str++; /* copy repeated chars */
1016 if (!(next = find_format( str, L"dMyHhms" ))) break;
1017 wcscpy( res + pos, sep );
1018 pos += wcslen(sep);
1019 str = next;
1021 wcscpy( res + pos, str );
1022 return wcscpy( buffer, res );
1026 /* FIXME: hardcoded, sortname is apparently not available in locale.nls */
1027 static const WCHAR *get_locale_sortname( LCID lcid )
1029 switch (PRIMARYLANGID( lcid ))
1031 case LANG_CHINESE:
1032 switch (SORTIDFROMLCID( lcid ))
1034 case SORT_CHINESE_PRCP:
1035 switch (SUBLANGID( lcid ))
1037 case SUBLANG_CHINESE_TRADITIONAL:
1038 case SUBLANG_CHINESE_HONGKONG:
1039 case 0x1f:
1040 return L"Stroke Count";
1041 default:
1042 return L"Pronunciation";
1044 case SORT_CHINESE_UNICODE: return L"Unicode";
1045 case SORT_CHINESE_PRC: return L"Stroke Count";
1046 case SORT_CHINESE_BOPOMOFO: return L"Bopomofo";
1047 case SORT_CHINESE_RADICALSTROKE: return L"Radical/Stroke";
1048 case 5: return L"Surname";
1050 break;
1052 case LANG_GEORGIAN:
1053 if (SORTIDFROMLCID( lcid ) == SORT_GEORGIAN_MODERN) return L"Modern";
1054 return L"Traditional";
1056 case LANG_GERMAN:
1057 switch (SUBLANGID( lcid ))
1059 case SUBLANG_NEUTRAL:
1060 case SUBLANG_DEFAULT:
1061 if (SORTIDFROMLCID( lcid ) == SORT_GERMAN_PHONE_BOOK) return L"Phone Book (DIN)";
1062 return L"Dictionary";
1064 break;
1066 case LANG_HUNGARIAN:
1067 if (SORTIDFROMLCID( lcid ) == SORT_HUNGARIAN_TECHNICAL) return L"Technical";
1068 break;
1070 case LANG_INVARIANT:
1071 if (SORTIDFROMLCID( lcid ) == SORT_INVARIANT_MATH) return L"Default";
1072 return L"Maths Alphanumerics";
1074 case LANG_JAPANESE:
1075 switch (SORTIDFROMLCID( lcid ))
1077 case SORT_JAPANESE_XJIS: return L"XJIS";
1078 case SORT_JAPANESE_UNICODE: return L"Unicode";
1079 case SORT_JAPANESE_RADICALSTROKE: return L"Radical/Stroke";
1081 break;
1083 case LANG_KOREAN:
1084 if (SORTIDFROMLCID( lcid ) == SORT_KOREAN_UNICODE) return L"Unicode";
1085 return L"Dictionary";
1087 case LANG_SPANISH:
1088 switch (SUBLANGID( lcid ))
1090 case SUBLANG_NEUTRAL:
1091 case SUBLANG_SPANISH_MODERN:
1092 return L"International";
1093 case SUBLANG_DEFAULT:
1094 return L"Traditional";
1096 break;
1098 return L"Default";
1102 /* get locale information from the locale.nls file */
1103 static int get_locale_info( const NLS_LOCALE_DATA *locale, LCID lcid, LCTYPE type,
1104 WCHAR *buffer, int len )
1106 static const WCHAR spermille[] = { 0x2030, 0 }; /* this one seems hardcoded */
1107 static const BYTE ipossignposn[] = { 3, 3, 4, 2, 1, 1, 3, 4, 1, 3, 4, 2, 4, 3, 3, 1 };
1108 static const BYTE inegsignposn[] = { 0, 3, 4, 2, 0, 1, 3, 4, 1, 3, 4, 2, 4, 3, 0, 0 };
1109 static const BYTE inegsymprecedes[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, };
1110 const WCHAR *sort;
1111 WCHAR *str, *end, tmp[80];
1112 UINT val;
1113 int ret;
1115 if (locale != user_locale) type |= LOCALE_NOUSEROVERRIDE;
1117 switch (LOWORD(type))
1119 case LOCALE_ILANGUAGE:
1120 /* return default language for neutral locales */
1121 val = locale->inotneutral ? locale->ilanguage : locale->idefaultlanguage;
1122 return locale_return_number( val, type, buffer, len );
1124 case LOCALE_SLOCALIZEDDISPLAYNAME:
1125 /* FIXME: localization */
1126 return locale_return_string( locale->sengdisplayname, type, buffer, len );
1128 case LOCALE_SABBREVLANGNAME:
1129 return locale_return_string( locale->sabbrevlangname, type, buffer, len );
1131 case LOCALE_SNATIVELANGNAME:
1132 return locale_return_string( locale->snativelangname, type, buffer, len );
1134 case LOCALE_ICOUNTRY:
1135 if ((ret = locale_return_reg_number( &entry_icountry, type, buffer, len )) != -1) return ret;
1136 return locale_return_number( locale->icountry, type, buffer, len );
1138 case LOCALE_SLOCALIZEDCOUNTRYNAME:
1139 /* FIXME: localization */
1140 return locale_return_string( locale->sengcountry, type, buffer, len );
1142 case LOCALE_SABBREVCTRYNAME:
1143 return locale_return_string( locale->sabbrevctryname, type, buffer, len );
1145 case LOCALE_SNATIVECTRYNAME:
1146 return locale_return_string( locale->snativectryname, type, buffer, len );
1148 case LOCALE_IDEFAULTLANGUAGE:
1149 return locale_return_number( locale->idefaultlanguage, type, buffer, len );
1151 case LOCALE_IDEFAULTCOUNTRY:
1152 return locale_return_number( locale->icountry, type, buffer, len );
1154 case LOCALE_IDEFAULTCODEPAGE:
1155 val = locale->idefaultcodepage == CP_UTF8 ? CP_OEMCP : locale->idefaultcodepage;
1156 return locale_return_number( val, type, buffer, len );
1158 case LOCALE_SLIST:
1159 if ((ret = locale_return_reg_string( &entry_slist, type, buffer, len )) != -1) return ret;
1160 return locale_return_string( locale->slist, type, buffer, len );
1162 case LOCALE_IMEASURE:
1163 if ((ret = locale_return_reg_number( &entry_imeasure, type, buffer, len )) != -1) return ret;
1164 return locale_return_number( locale->imeasure, type, buffer, len );
1166 case LOCALE_SDECIMAL:
1167 if ((ret = locale_return_reg_string( &entry_sdecimal, type, buffer, len )) != -1) return ret;
1168 return locale_return_string( locale->sdecimal, type, buffer, len );
1170 case LOCALE_STHOUSAND:
1171 if ((ret = locale_return_reg_string( &entry_sthousand, type, buffer, len )) != -1) return ret;
1172 return locale_return_string( locale->sthousand, type, buffer, len );
1174 case LOCALE_SGROUPING:
1175 if ((ret = locale_return_reg_string( &entry_sgrouping, type, buffer, len )) != -1) return ret;
1176 return locale_return_grouping( locale->sgrouping, type, buffer, len );
1178 case LOCALE_IDIGITS:
1179 if ((ret = locale_return_reg_number( &entry_idigits, type, buffer, len )) != -1) return ret;
1180 return locale_return_number( locale->idigits, type, buffer, len );
1182 case LOCALE_ILZERO:
1183 if ((ret = locale_return_reg_number( &entry_ilzero, type, buffer, len )) != -1) return ret;
1184 return locale_return_number( locale->ilzero, type, buffer, len );
1186 case LOCALE_SNATIVEDIGITS:
1187 if ((ret = locale_return_reg_string( &entry_snativedigits, type, buffer, len )) != -1) return ret;
1188 return locale_return_strarray_concat( locale->snativedigits, type, buffer, len );
1190 case LOCALE_SCURRENCY:
1191 if ((ret = locale_return_reg_string( &entry_scurrency, type, buffer, len )) != -1) return ret;
1192 return locale_return_string( locale->scurrency, type, buffer, len );
1194 case LOCALE_SINTLSYMBOL:
1195 if ((ret = locale_return_reg_string( &entry_sintlsymbol, type, buffer, len )) != -1) return ret;
1196 return locale_return_string( locale->sintlsymbol, type, buffer, len );
1198 case LOCALE_SMONDECIMALSEP:
1199 if ((ret = locale_return_reg_string( &entry_smondecimalsep, type, buffer, len )) != -1) return ret;
1200 return locale_return_string( locale->smondecimalsep, type, buffer, len );
1202 case LOCALE_SMONTHOUSANDSEP:
1203 if ((ret = locale_return_reg_string( &entry_smonthousandsep, type, buffer, len )) != -1) return ret;
1204 return locale_return_string( locale->smonthousandsep, type, buffer, len );
1206 case LOCALE_SMONGROUPING:
1207 if ((ret = locale_return_reg_string( &entry_smongrouping, type, buffer, len )) != -1) return ret;
1208 return locale_return_grouping( locale->smongrouping, type, buffer, len );
1210 case LOCALE_ICURRDIGITS:
1211 case LOCALE_IINTLCURRDIGITS:
1212 if ((ret = locale_return_reg_number( &entry_icurrdigits, type, buffer, len )) != -1) return ret;
1213 return locale_return_number( locale->icurrdigits, type, buffer, len );
1215 case LOCALE_ICURRENCY:
1216 if ((ret = locale_return_reg_number( &entry_icurrency, type, buffer, len )) != -1) return ret;
1217 return locale_return_number( locale->icurrency, type, buffer, len );
1219 case LOCALE_INEGCURR:
1220 if ((ret = locale_return_reg_number( &entry_inegcurr, type, buffer, len )) != -1) return ret;
1221 return locale_return_number( locale->inegcurr, type, buffer, len );
1223 case LOCALE_SDATE:
1224 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1225 tmp, ARRAY_SIZE( tmp ))) break;
1226 if (!(str = find_format( tmp, L"dMy" ))) break;
1227 while (str[1] == str[0]) str++; /* skip repeated chars */
1228 if (!(end = find_format( ++str, L"dMy" ))) break;
1229 *end++ = 0;
1230 return locale_return_data( str, end - str, type, buffer, len );
1232 case LOCALE_STIME:
1233 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1234 tmp, ARRAY_SIZE( tmp ))) break;
1235 if (!(str = find_format( tmp, L"Hhms" ))) break;
1236 while (str[1] == str[0]) str++; /* skip repeated chars */
1237 if (!(end = find_format( ++str, L"Hhms" ))) break;
1238 *end++ = 0;
1239 return locale_return_data( str, end - str, type, buffer, len );
1241 case LOCALE_SSHORTDATE:
1242 if ((ret = locale_return_reg_string( &entry_sshortdate, type, buffer, len )) != -1) return ret;
1243 return locale_return_strarray( locale->sshortdate, 0, type, buffer, len );
1245 case LOCALE_SLONGDATE:
1246 if ((ret = locale_return_reg_string( &entry_slongdate, type, buffer, len )) != -1) return ret;
1247 return locale_return_strarray( locale->slongdate, 0, type, buffer, len );
1249 case LOCALE_IDATE:
1250 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1251 tmp, ARRAY_SIZE( tmp ))) break;
1252 /* if both year and day are found before month, the last one takes precedence */
1253 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1255 if (*str == 'M') break;
1256 val = (*str == 'y' ? 2 : 1);
1258 return locale_return_number( val, type, buffer, len );
1260 case LOCALE_ILDATE:
1261 if (!get_locale_info( locale, lcid, LOCALE_SLONGDATE | (type & LOCALE_NOUSEROVERRIDE),
1262 tmp, ARRAY_SIZE( tmp ))) break;
1263 /* if both year and day are found before month, the last one takes precedence */
1264 for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" ))
1266 if (*str == 'M') break;
1267 val = (*str == 'y' ? 2 : 1);
1269 return locale_return_number( val, type, buffer, len );
1271 case LOCALE_ITIME:
1272 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1273 tmp, ARRAY_SIZE( tmp ))) break;
1274 if (!(str = find_format( tmp, L"Hh" ))) break;
1275 return locale_return_number( *str == 'H', type, buffer, len );
1277 case LOCALE_ICENTURY:
1278 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1279 tmp, ARRAY_SIZE( tmp ))) break;
1280 if (!(str = find_format( tmp, L"y" ))) break;
1281 return locale_return_number( !wcsncmp( str, L"yyyy", 4 ), type, buffer, len );
1283 case LOCALE_ITLZERO:
1284 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1285 tmp, ARRAY_SIZE( tmp ))) break;
1286 if (!(str = find_format( tmp, L"Hh" ))) break;
1287 return locale_return_number( str[1] == str[0], type, buffer, len );
1289 case LOCALE_IDAYLZERO:
1290 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1291 tmp, ARRAY_SIZE( tmp ))) break;
1292 if (!(str = find_format( tmp, L"d" ))) break;
1293 return locale_return_number( str[1] == 'd', type, buffer, len );
1295 case LOCALE_IMONLZERO:
1296 if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE),
1297 tmp, ARRAY_SIZE( tmp ))) break;
1298 if (!(str = find_format( tmp, L"M" ))) break;
1299 return locale_return_number( str[1] == 'M', type, buffer, len );
1301 case LOCALE_S1159:
1302 if ((ret = locale_return_reg_string( &entry_s1159, type, buffer, len )) != -1) return ret;
1303 return locale_return_string( locale->s1159, type, buffer, len );
1305 case LOCALE_S2359:
1306 if ((ret = locale_return_reg_string( &entry_s2359, type, buffer, len )) != -1) return ret;
1307 return locale_return_string( locale->s2359, type, buffer, len );
1309 case LOCALE_SDAYNAME1:
1310 case LOCALE_SDAYNAME2:
1311 case LOCALE_SDAYNAME3:
1312 case LOCALE_SDAYNAME4:
1313 case LOCALE_SDAYNAME5:
1314 case LOCALE_SDAYNAME6:
1315 case LOCALE_SDAYNAME7:
1316 return locale_return_strarray( locale->sdayname,
1317 LOWORD(type - LOCALE_SDAYNAME1 + 1) % 7, type, buffer, len );
1319 case LOCALE_SABBREVDAYNAME1:
1320 case LOCALE_SABBREVDAYNAME2:
1321 case LOCALE_SABBREVDAYNAME3:
1322 case LOCALE_SABBREVDAYNAME4:
1323 case LOCALE_SABBREVDAYNAME5:
1324 case LOCALE_SABBREVDAYNAME6:
1325 case LOCALE_SABBREVDAYNAME7:
1326 return locale_return_strarray( locale->sabbrevdayname,
1327 LOWORD(type - LOCALE_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1329 case LOCALE_SMONTHNAME1:
1330 case LOCALE_SMONTHNAME2:
1331 case LOCALE_SMONTHNAME3:
1332 case LOCALE_SMONTHNAME4:
1333 case LOCALE_SMONTHNAME5:
1334 case LOCALE_SMONTHNAME6:
1335 case LOCALE_SMONTHNAME7:
1336 case LOCALE_SMONTHNAME8:
1337 case LOCALE_SMONTHNAME9:
1338 case LOCALE_SMONTHNAME10:
1339 case LOCALE_SMONTHNAME11:
1340 case LOCALE_SMONTHNAME12:
1341 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1342 locale->sgenitivemonth : locale->smonthname,
1343 type - LOCALE_SMONTHNAME1, type, buffer, len );
1345 case LOCALE_SABBREVMONTHNAME1:
1346 case LOCALE_SABBREVMONTHNAME2:
1347 case LOCALE_SABBREVMONTHNAME3:
1348 case LOCALE_SABBREVMONTHNAME4:
1349 case LOCALE_SABBREVMONTHNAME5:
1350 case LOCALE_SABBREVMONTHNAME6:
1351 case LOCALE_SABBREVMONTHNAME7:
1352 case LOCALE_SABBREVMONTHNAME8:
1353 case LOCALE_SABBREVMONTHNAME9:
1354 case LOCALE_SABBREVMONTHNAME10:
1355 case LOCALE_SABBREVMONTHNAME11:
1356 case LOCALE_SABBREVMONTHNAME12:
1357 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1358 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1359 type - LOCALE_SABBREVMONTHNAME1, type, buffer, len );
1361 case LOCALE_SPOSITIVESIGN:
1362 if ((ret = locale_return_reg_string( &entry_spositivesign, type, buffer, len )) != -1) return ret;
1363 return locale_return_string( locale->spositivesign, type, buffer, len );
1365 case LOCALE_SNEGATIVESIGN:
1366 if ((ret = locale_return_reg_string( &entry_snegativesign, type, buffer, len )) != -1) return ret;
1367 return locale_return_string( locale->snegativesign, type, buffer, len );
1369 case LOCALE_IPOSSIGNPOSN:
1370 if (!get_locale_info( locale, lcid,
1371 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1372 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1373 return locale_return_number( ipossignposn[val], type, buffer, len );
1375 case LOCALE_INEGSIGNPOSN:
1376 if (!get_locale_info( locale, lcid,
1377 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1378 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1379 return locale_return_number( inegsignposn[val], type, buffer, len );
1381 case LOCALE_IPOSSYMPRECEDES:
1382 if (!get_locale_info( locale, lcid,
1383 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1384 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1385 return locale_return_number( !(val & 1), type, buffer, len );
1387 case LOCALE_IPOSSEPBYSPACE:
1388 if (!get_locale_info( locale, lcid,
1389 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1390 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1391 return locale_return_number( !!(val & 2), type, buffer, len );
1393 case LOCALE_INEGSYMPRECEDES:
1394 if (!get_locale_info( locale, lcid,
1395 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1396 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1397 return locale_return_number( inegsymprecedes[val], type, buffer, len );
1399 case LOCALE_INEGSEPBYSPACE:
1400 if (!get_locale_info( locale, lcid,
1401 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE),
1402 (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break;
1403 return locale_return_number( (val >= 8), type, buffer, len );
1405 case LOCALE_FONTSIGNATURE:
1406 return locale_return_data( locale_strings + locale->fontsignature + 1,
1407 locale_strings[locale->fontsignature], type, buffer, len );
1409 case LOCALE_SISO639LANGNAME:
1410 return locale_return_string( locale->siso639langname, type, buffer, len );
1412 case LOCALE_SISO3166CTRYNAME:
1413 return locale_return_string( locale->siso3166ctryname, type, buffer, len );
1415 case LOCALE_IGEOID:
1416 return locale_return_number( locale->igeoid, type, buffer, len );
1418 case LOCALE_SNAME:
1419 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1421 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1422 if (entry) return locale_return_string( entry->name, type, buffer, len );
1424 return locale_return_string( locale->sname, type, buffer, len );
1426 case LOCALE_SDURATION:
1427 return locale_return_strarray( locale->sduration, 0, type, buffer, len );
1429 case LOCALE_SKEYBOARDSTOINSTALL:
1430 return locale_return_string( locale->skeyboardstoinstall, type, buffer, len );
1432 case LOCALE_SSHORTESTDAYNAME1:
1433 case LOCALE_SSHORTESTDAYNAME2:
1434 case LOCALE_SSHORTESTDAYNAME3:
1435 case LOCALE_SSHORTESTDAYNAME4:
1436 case LOCALE_SSHORTESTDAYNAME5:
1437 case LOCALE_SSHORTESTDAYNAME6:
1438 case LOCALE_SSHORTESTDAYNAME7:
1439 return locale_return_strarray( locale->sshortestdayname,
1440 LOWORD(type - LOCALE_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1442 case LOCALE_SISO639LANGNAME2:
1443 return locale_return_string( locale->siso639langname2, type, buffer, len );
1445 case LOCALE_SISO3166CTRYNAME2:
1446 return locale_return_string( locale->siso3166ctryname2, type, buffer, len );
1448 case LOCALE_SNAN:
1449 return locale_return_string( locale->snan, type, buffer, len );
1451 case LOCALE_SPOSINFINITY:
1452 return locale_return_string( locale->sposinfinity, type, buffer, len );
1454 case LOCALE_SNEGINFINITY:
1455 return locale_return_string( locale->sneginfinity, type, buffer, len );
1457 case LOCALE_SSCRIPTS:
1458 return locale_return_string( locale->sscripts, type, buffer, len );
1460 case LOCALE_SPARENT:
1461 return locale_return_string( locale->sparent, type, buffer, len );
1463 case LOCALE_SCONSOLEFALLBACKNAME:
1464 return locale_return_string( locale->sconsolefallbackname, type, buffer, len );
1466 case LOCALE_SLOCALIZEDLANGUAGENAME:
1467 /* FIXME: localization */
1468 return locale_return_string( locale->senglanguage, type, buffer, len );
1470 case LOCALE_IREADINGLAYOUT:
1471 return locale_return_number( locale->ireadinglayout, type, buffer, len );
1473 case LOCALE_INEUTRAL:
1474 return locale_return_number( !locale->inotneutral, type, buffer, len );
1476 case LOCALE_SENGLISHDISPLAYNAME:
1477 return locale_return_string( locale->sengdisplayname, type, buffer, len );
1479 case LOCALE_SNATIVEDISPLAYNAME:
1480 return locale_return_string( locale->snativedisplayname, type, buffer, len );
1482 case LOCALE_INEGATIVEPERCENT:
1483 return locale_return_number( locale->inegativepercent, type, buffer, len );
1485 case LOCALE_IPOSITIVEPERCENT:
1486 return locale_return_number( locale->ipositivepercent, type, buffer, len );
1488 case LOCALE_SPERCENT:
1489 return locale_return_string( locale->spercent, type, buffer, len );
1491 case LOCALE_SPERMILLE:
1492 return locale_return_data( spermille, ARRAY_SIZE(spermille), type, buffer, len );
1494 case LOCALE_SMONTHDAY:
1495 return locale_return_strarray( locale->smonthday, 0, type, buffer, len );
1497 case LOCALE_SSHORTTIME:
1498 if ((ret = locale_return_reg_string( &entry_sshorttime, type, buffer, len )) != -1) return ret;
1499 return locale_return_strarray( locale->sshorttime, 0, type, buffer, len );
1501 case LOCALE_SOPENTYPELANGUAGETAG:
1502 return locale_return_string( locale->sopentypelanguagetag, type, buffer, len );
1504 case LOCALE_SSORTLOCALE:
1505 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1507 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1508 if (entry) return locale_return_string( entry->name, type, buffer, len );
1510 return locale_return_string( locale->ssortlocale, type, buffer, len );
1512 case LOCALE_SRELATIVELONGDATE:
1513 return locale_return_string( locale->srelativelongdate, type, buffer, len );
1515 case 0x007d: /* undocumented */
1516 return locale_return_number( 0, type, buffer, len );
1518 case LOCALE_SSHORTESTAM:
1519 return locale_return_string( locale->sshortestam, type, buffer, len );
1521 case LOCALE_SSHORTESTPM:
1522 return locale_return_string( locale->sshortestpm, type, buffer, len );
1524 case LOCALE_SENGLANGUAGE:
1525 return locale_return_string( locale->senglanguage, type, buffer, len );
1527 case LOCALE_SENGCOUNTRY:
1528 return locale_return_string( locale->sengcountry, type, buffer, len );
1530 case LOCALE_STIMEFORMAT:
1531 if ((ret = locale_return_reg_string( &entry_stimeformat, type, buffer, len )) != -1) return ret;
1532 return locale_return_strarray( locale->stimeformat, 0, type, buffer, len );
1534 case LOCALE_IDEFAULTANSICODEPAGE:
1535 val = locale->idefaultansicodepage == CP_UTF8 ? CP_ACP : locale->idefaultansicodepage;
1536 return locale_return_number( val, type, buffer, len );
1538 case LOCALE_ITIMEMARKPOSN:
1539 if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE),
1540 tmp, ARRAY_SIZE( tmp ))) break;
1541 if (!(str = find_format( tmp, L"Hhmst" ))) break;
1542 return locale_return_number( *str == 't', type, buffer, len );
1544 case LOCALE_SYEARMONTH:
1545 if ((ret = locale_return_reg_string( &entry_syearmonth, type, buffer, len )) != -1) return ret;
1546 return locale_return_strarray( locale->syearmonth, 0, type, buffer, len );
1548 case LOCALE_SENGCURRNAME:
1549 return locale_return_string( locale->sengcurrname, type, buffer, len );
1551 case LOCALE_SNATIVECURRNAME:
1552 return locale_return_string( locale->snativecurrname, type, buffer, len );
1554 case LOCALE_ICALENDARTYPE:
1555 if ((ret = locale_return_reg_number( &entry_icalendartype, type, buffer, len )) != -1) return ret;
1556 return locale_return_number( locale_strings[locale->scalendartype + 1], type, buffer, len );
1558 case LOCALE_IPAPERSIZE:
1559 if ((ret = locale_return_reg_number( &entry_ipapersize, type, buffer, len )) != -1) return ret;
1560 return locale_return_number( locale->ipapersize, type, buffer, len );
1562 case LOCALE_IOPTIONALCALENDAR:
1563 return locale_return_number( locale_strings[locale->scalendartype + 2], type, buffer, len );
1565 case LOCALE_IFIRSTDAYOFWEEK:
1566 if ((ret = locale_return_reg_number( &entry_ifirstdayofweek, type, buffer, len )) != -1) return ret;
1567 return locale_return_number( (locale->ifirstdayofweek + 6) % 7, type, buffer, len );
1569 case LOCALE_IFIRSTWEEKOFYEAR:
1570 if ((ret = locale_return_reg_number( &entry_ifirstweekofyear, type, buffer, len )) != -1) return ret;
1571 return locale_return_number( locale->ifirstweekofyear, type, buffer, len );
1573 case LOCALE_SMONTHNAME13:
1574 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1575 locale->sgenitivemonth : locale->smonthname,
1576 12, type, buffer, len );
1578 case LOCALE_SABBREVMONTHNAME13:
1579 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1580 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1581 12, type, buffer, len );
1583 case LOCALE_INEGNUMBER:
1584 if ((ret = locale_return_reg_number( &entry_inegnumber, type, buffer, len )) != -1) return ret;
1585 return locale_return_number( locale->inegnumber, type, buffer, len );
1587 case LOCALE_IDEFAULTMACCODEPAGE:
1588 val = locale->idefaultmaccodepage == CP_UTF8 ? CP_MACCP : locale->idefaultmaccodepage;
1589 return locale_return_number( val, type, buffer, len );
1591 case LOCALE_IDEFAULTEBCDICCODEPAGE:
1592 return locale_return_number( locale->idefaultebcdiccodepage, type, buffer, len );
1594 case LOCALE_SSORTNAME:
1595 sort = get_locale_sortname( lcid );
1596 return locale_return_data( sort, wcslen(sort) + 1, type, buffer, len );
1598 case LOCALE_IDIGITSUBSTITUTION:
1599 if ((ret = locale_return_reg_number( &entry_idigitsubstitution, type, buffer, len )) != -1) return ret;
1600 return locale_return_number( locale->idigitsubstitution, type, buffer, len );
1602 SetLastError( ERROR_INVALID_FLAGS );
1603 return 0;
1607 /* get calendar information from the locale.nls file */
1608 static int get_calendar_info( const NLS_LOCALE_DATA *locale, CALID id, CALTYPE type,
1609 WCHAR *buffer, int len, DWORD *value )
1611 unsigned int i, val = 0;
1612 const struct calendar *cal;
1614 if (type & CAL_RETURN_NUMBER)
1616 if (buffer || len || !value) goto invalid;
1618 else if (len < 0 || value) goto invalid;
1620 if (id != CAL_GREGORIAN)
1622 const USHORT *ids = locale_strings + locale->scalendartype;
1623 for (i = 0; i < ids[0]; i++) if (ids[1 + i] == id) break;
1624 if (i == ids[0]) goto invalid;
1626 if (!(cal = get_calendar_data( locale, id ))) goto invalid;
1628 switch (LOWORD(type))
1630 case CAL_ICALINTVALUE:
1631 return cal_return_number( cal->icalintvalue, type, buffer, len, value );
1633 case CAL_SCALNAME:
1634 return locale_return_strarray( locale->calnames, id - 1, type, buffer, len );
1636 case CAL_IYEAROFFSETRANGE:
1637 if (cal->iyearoffsetrange)
1639 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1640 const short *info = (const short *)locale_strings + array[0];
1641 val = (info[5] < 0) ? -info[5] : info[5] + 1; /* year zero */
1643 return cal_return_number( val, type, buffer, len, value );
1645 case CAL_SERASTRING:
1646 if (id == CAL_GREGORIAN) return locale_return_string( locale->serastring, type, buffer, len );
1647 if (cal->iyearoffsetrange)
1649 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1650 const short *info = (const short *)locale_strings + array[0];
1651 val = info[1] - 1;
1653 return locale_return_strarray( cal->serastring, val, type, buffer, len );
1655 case CAL_SSHORTDATE:
1656 val = (id == CAL_GREGORIAN) ? locale->sshortdate : cal->sshortdate;
1657 return locale_return_strarray( val, 0, type, buffer, len );
1659 case CAL_SLONGDATE:
1660 val = (id == CAL_GREGORIAN) ? locale->slongdate : cal->slongdate;
1661 return locale_return_strarray( val, 0, type, buffer, len );
1663 case CAL_SDAYNAME1:
1664 case CAL_SDAYNAME2:
1665 case CAL_SDAYNAME3:
1666 case CAL_SDAYNAME4:
1667 case CAL_SDAYNAME5:
1668 case CAL_SDAYNAME6:
1669 case CAL_SDAYNAME7:
1670 val = (id == CAL_GREGORIAN) ? locale->sdayname : cal->sdayname;
1671 return locale_return_strarray( val, (LOWORD(type) - CAL_SDAYNAME1 + 1) % 7, type, buffer, len );
1673 case CAL_SABBREVDAYNAME1:
1674 case CAL_SABBREVDAYNAME2:
1675 case CAL_SABBREVDAYNAME3:
1676 case CAL_SABBREVDAYNAME4:
1677 case CAL_SABBREVDAYNAME5:
1678 case CAL_SABBREVDAYNAME6:
1679 case CAL_SABBREVDAYNAME7:
1680 val = (id == CAL_GREGORIAN) ? locale->sabbrevdayname : cal->sabbrevdayname;
1681 return locale_return_strarray( val, (LOWORD(type) - CAL_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1682 case CAL_SMONTHNAME1:
1683 case CAL_SMONTHNAME2:
1684 case CAL_SMONTHNAME3:
1685 case CAL_SMONTHNAME4:
1686 case CAL_SMONTHNAME5:
1687 case CAL_SMONTHNAME6:
1688 case CAL_SMONTHNAME7:
1689 case CAL_SMONTHNAME8:
1690 case CAL_SMONTHNAME9:
1691 case CAL_SMONTHNAME10:
1692 case CAL_SMONTHNAME11:
1693 case CAL_SMONTHNAME12:
1694 case CAL_SMONTHNAME13:
1695 if (id != CAL_GREGORIAN) val = cal->smonthname;
1696 else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) val = locale->sgenitivemonth;
1697 else val = locale->smonthname;
1698 return locale_return_strarray( val, LOWORD(type) - CAL_SMONTHNAME1, type, buffer, len );
1700 case CAL_SABBREVMONTHNAME1:
1701 case CAL_SABBREVMONTHNAME2:
1702 case CAL_SABBREVMONTHNAME3:
1703 case CAL_SABBREVMONTHNAME4:
1704 case CAL_SABBREVMONTHNAME5:
1705 case CAL_SABBREVMONTHNAME6:
1706 case CAL_SABBREVMONTHNAME7:
1707 case CAL_SABBREVMONTHNAME8:
1708 case CAL_SABBREVMONTHNAME9:
1709 case CAL_SABBREVMONTHNAME10:
1710 case CAL_SABBREVMONTHNAME11:
1711 case CAL_SABBREVMONTHNAME12:
1712 case CAL_SABBREVMONTHNAME13:
1713 if (id != CAL_GREGORIAN) val = cal->sabbrevmonthname;
1714 else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) val = locale->sabbrevgenitivemonth;
1715 else val = locale->sabbrevmonthname;
1716 return locale_return_strarray( val, LOWORD(type) - CAL_SABBREVMONTHNAME1, type, buffer, len );
1718 case CAL_SYEARMONTH:
1719 val = (id == CAL_GREGORIAN) ? locale->syearmonth : cal->syearmonth;
1720 return locale_return_strarray( val, 0, type, buffer, len );
1722 case CAL_ITWODIGITYEARMAX:
1723 return cal_return_number( cal->itwodigityearmax, type, buffer, len, value );
1725 case CAL_SSHORTESTDAYNAME1:
1726 case CAL_SSHORTESTDAYNAME2:
1727 case CAL_SSHORTESTDAYNAME3:
1728 case CAL_SSHORTESTDAYNAME4:
1729 case CAL_SSHORTESTDAYNAME5:
1730 case CAL_SSHORTESTDAYNAME6:
1731 case CAL_SSHORTESTDAYNAME7:
1732 val = (id == CAL_GREGORIAN) ? locale->sshortestdayname : cal->sshortestdayname;
1733 return locale_return_strarray( val, (LOWORD(type) - CAL_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1735 case CAL_SMONTHDAY:
1736 val = (id == CAL_GREGORIAN) ? locale->smonthday : cal->smonthday;
1737 return locale_return_strarray( val, 0, type, buffer, len );
1739 case CAL_SABBREVERASTRING:
1740 if (id == CAL_GREGORIAN) return locale_return_string( locale->sabbreverastring, type, buffer, len );
1741 if (cal->iyearoffsetrange)
1743 const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1);
1744 const short *info = (const short *)locale_strings + array[0];
1745 val = info[1] - 1;
1747 return locale_return_strarray( cal->sabbreverastring, val, type, buffer, len );
1749 case CAL_SRELATIVELONGDATE:
1750 val = (id == CAL_GREGORIAN) ? locale->srelativelongdate : cal->srelativelongdate;
1751 return locale_return_string( val, type, buffer, len );
1753 case CAL_SENGLISHERANAME:
1754 case CAL_SENGLISHABBREVERANAME:
1755 /* not supported on Windows */
1756 break;
1758 SetLastError( ERROR_INVALID_FLAGS );
1759 return 0;
1761 invalid:
1762 SetLastError( ERROR_INVALID_PARAMETER );
1763 return 0;
1767 /* get geo information from the locale.nls file */
1768 static int get_geo_info( const struct geo_id *geo, enum SYSGEOTYPE type,
1769 WCHAR *buffer, int len, LANGID lang )
1771 WCHAR tmp[12];
1772 const WCHAR *str = tmp;
1773 int ret;
1775 switch (type)
1777 case GEO_NATION:
1778 if (geo->class != GEOCLASS_NATION) return 0;
1779 /* fall through */
1780 case GEO_ID:
1781 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->id );
1782 break;
1783 case GEO_ISO_UN_NUMBER:
1784 swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", geo->uncode );
1785 break;
1786 case GEO_PARENT:
1787 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->parent );
1788 break;
1789 case GEO_DIALINGCODE:
1790 swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->dialcode );
1791 break;
1792 case GEO_ISO2:
1793 str = geo->iso2;
1794 break;
1795 case GEO_ISO3:
1796 str = geo->iso3;
1797 break;
1798 case GEO_LATITUDE:
1799 str = geo->latitude;
1800 break;
1801 case GEO_LONGITUDE:
1802 str = geo->longitude;
1803 break;
1804 case GEO_CURRENCYCODE:
1805 str = geo->currcode;
1806 break;
1807 case GEO_CURRENCYSYMBOL:
1808 str = geo->currsymbol;
1809 break;
1810 case GEO_RFC1766:
1811 case GEO_LCID:
1812 case GEO_FRIENDLYNAME:
1813 case GEO_OFFICIALNAME:
1814 case GEO_TIMEZONES:
1815 case GEO_OFFICIALLANGUAGES:
1816 case GEO_NAME:
1817 FIXME( "type %u is not supported\n", type );
1818 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1819 return 0;
1820 default:
1821 SetLastError( ERROR_INVALID_FLAGS );
1822 return 0;
1825 ret = lstrlenW(str) + 1;
1826 if (!buffer || !len) return ret;
1828 memcpy( buffer, str, min( ret, len ) * sizeof(WCHAR) );
1829 if (len < ret) SetLastError( ERROR_INSUFFICIENT_BUFFER );
1830 return len < ret ? 0 : ret;
1834 /* update a registry value based on the current user locale info */
1835 static void update_registry_value( UINT type, const WCHAR *subkey, const WCHAR *value )
1837 WCHAR buffer[80];
1838 UINT len = get_locale_info( user_locale, user_lcid, type, buffer, ARRAY_SIZE(buffer) );
1839 if (len) RegSetKeyValueW( intl_key, subkey, value, REG_SZ, (BYTE *)buffer, len * sizeof(WCHAR) );
1843 /* update all registry values upon user locale change */
1844 static void update_locale_registry(void)
1846 WCHAR buffer[80];
1847 UINT len;
1849 len = swprintf( buffer, ARRAY_SIZE(buffer), L"%08x", GetUserDefaultLCID() );
1850 RegSetValueExW( intl_key, L"Locale", 0, REG_SZ, (BYTE *)buffer, (len + 1) * sizeof(WCHAR) );
1852 #define UPDATE(val,entry) update_registry_value( LOCALE_NOUSEROVERRIDE | (val), (entry).subkey, (entry).value )
1853 UPDATE( LOCALE_ICALENDARTYPE, entry_icalendartype );
1854 UPDATE( LOCALE_ICOUNTRY, entry_icountry );
1855 UPDATE( LOCALE_ICURRDIGITS, entry_icurrdigits );
1856 UPDATE( LOCALE_ICURRENCY, entry_icurrency );
1857 UPDATE( LOCALE_IDIGITS, entry_idigits );
1858 UPDATE( LOCALE_IDIGITSUBSTITUTION, entry_idigitsubstitution );
1859 UPDATE( LOCALE_IFIRSTDAYOFWEEK, entry_ifirstdayofweek );
1860 UPDATE( LOCALE_IFIRSTWEEKOFYEAR, entry_ifirstweekofyear );
1861 UPDATE( LOCALE_ILZERO, entry_ilzero );
1862 UPDATE( LOCALE_IMEASURE, entry_imeasure );
1863 UPDATE( LOCALE_INEGCURR, entry_inegcurr );
1864 UPDATE( LOCALE_INEGNUMBER, entry_inegnumber );
1865 UPDATE( LOCALE_IPAPERSIZE, entry_ipapersize );
1866 UPDATE( LOCALE_S1159, entry_s1159 );
1867 UPDATE( LOCALE_S2359, entry_s2359 );
1868 UPDATE( LOCALE_SCURRENCY, entry_scurrency );
1869 UPDATE( LOCALE_SDECIMAL, entry_sdecimal );
1870 UPDATE( LOCALE_SGROUPING, entry_sgrouping );
1871 UPDATE( LOCALE_SINTLSYMBOL, entry_sintlsymbol );
1872 UPDATE( LOCALE_SLIST, entry_slist );
1873 UPDATE( LOCALE_SLONGDATE, entry_slongdate );
1874 UPDATE( LOCALE_SMONDECIMALSEP, entry_smondecimalsep );
1875 UPDATE( LOCALE_SMONGROUPING, entry_smongrouping );
1876 UPDATE( LOCALE_SMONTHOUSANDSEP, entry_smonthousandsep );
1877 UPDATE( LOCALE_SNATIVEDIGITS, entry_snativedigits );
1878 UPDATE( LOCALE_SNEGATIVESIGN, entry_snegativesign );
1879 UPDATE( LOCALE_SPOSITIVESIGN, entry_spositivesign );
1880 UPDATE( LOCALE_SSHORTDATE, entry_sshortdate );
1881 UPDATE( LOCALE_SSHORTTIME, entry_sshorttime );
1882 UPDATE( LOCALE_STHOUSAND, entry_sthousand );
1883 UPDATE( LOCALE_STIMEFORMAT, entry_stimeformat );
1884 UPDATE( LOCALE_SYEARMONTH, entry_syearmonth );
1885 #undef UPDATE
1886 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDATE, NULL, L"iDate" );
1887 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIME, NULL, L"iTime" );
1888 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIMEMARKPOSN, NULL, L"iTimePrefix" );
1889 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITLZERO, NULL, L"iTLZero" );
1890 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SDATE, NULL, L"sDate" );
1891 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STIME, NULL, L"sTime" );
1892 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SABBREVLANGNAME, NULL, L"sLanguage" );
1893 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SCOUNTRY, NULL, L"sCountry" );
1894 update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNAME, NULL, L"LocaleName" );
1895 SetUserGeoID( user_locale->igeoid );
1899 /***********************************************************************
1900 * init_locale
1902 void init_locale( HMODULE module )
1904 USHORT utf8[2] = { 0, CP_UTF8 };
1905 USHORT *ansi_ptr, *oem_ptr;
1906 WCHAR bufferW[LOCALE_NAME_MAX_LENGTH];
1907 DYNAMIC_TIME_ZONE_INFORMATION timezone;
1908 const WCHAR *user_locale_name;
1909 DWORD count;
1910 SIZE_T size;
1911 HKEY hkey;
1913 kernelbase_handle = module;
1914 load_locale_nls();
1915 load_sortdefault_nls();
1917 if (system_lcid == LOCALE_CUSTOM_UNSPECIFIED) system_lcid = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT );
1918 system_locale = NlsValidateLocale( &system_lcid, 0 );
1920 NtQueryDefaultLocale( TRUE, &user_lcid );
1921 if (!(user_locale = NlsValidateLocale( &user_lcid, 0 )))
1923 if (GetEnvironmentVariableW( L"WINEUSERLOCALE", bufferW, ARRAY_SIZE(bufferW) ))
1924 user_locale = get_locale_by_name( bufferW, &user_lcid );
1925 if (!user_locale) user_locale = system_locale;
1927 user_lcid = user_locale->ilanguage;
1928 if (user_lcid == LOCALE_CUSTOM_UNSPECIFIED) user_lcid = LOCALE_CUSTOM_DEFAULT;
1930 if (GetEnvironmentVariableW( L"WINEUNIXCP", bufferW, ARRAY_SIZE(bufferW) ))
1931 unix_cp = wcstoul( bufferW, NULL, 10 );
1933 NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size );
1935 ansi_ptr = NtCurrentTeb()->Peb->AnsiCodePageData ? NtCurrentTeb()->Peb->AnsiCodePageData : utf8;
1936 oem_ptr = NtCurrentTeb()->Peb->OemCodePageData ? NtCurrentTeb()->Peb->OemCodePageData : utf8;
1937 RtlInitCodePageTable( ansi_ptr, &ansi_cpinfo );
1938 RtlInitCodePageTable( oem_ptr, &oem_cpinfo );
1940 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
1941 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
1942 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1943 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tz_key, NULL );
1944 RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International",
1945 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL );
1947 current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT );
1949 if (GetDynamicTimeZoneInformation( &timezone ) != TIME_ZONE_ID_INVALID &&
1950 !RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\TimeZoneInformation",
1951 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1953 RegSetValueExW( hkey, L"StandardName", 0, REG_SZ, (BYTE *)timezone.StandardName,
1954 (lstrlenW(timezone.StandardName) + 1) * sizeof(WCHAR) );
1955 RegSetValueExW( hkey, L"TimeZoneKeyName", 0, REG_SZ, (BYTE *)timezone.TimeZoneKeyName,
1956 (lstrlenW(timezone.TimeZoneKeyName) + 1) * sizeof(WCHAR) );
1957 RegCloseKey( hkey );
1960 /* Update registry contents if the user locale has changed.
1961 * This simulates the action of the Windows control panel. */
1963 user_locale_name = locale_strings + user_locale->sname + 1;
1964 count = sizeof(bufferW);
1965 if (!RegQueryValueExW( intl_key, L"LocaleName", NULL, NULL, (BYTE *)bufferW, &count ))
1967 if (!wcscmp( bufferW, user_locale_name )) return; /* unchanged */
1968 TRACE( "updating registry, locale changed %s -> %s\n",
1969 debugstr_w(bufferW), debugstr_w(user_locale_name) );
1971 else TRACE( "updating registry, locale changed none -> %s\n", debugstr_w(user_locale_name) );
1973 update_locale_registry();
1975 if (!RegCreateKeyExW( nls_key, L"Codepage",
1976 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1978 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", GetACP() );
1979 RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1980 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", GetOEMCP() );
1981 RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1982 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", system_locale->idefaultmaccodepage );
1983 RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1984 RegCloseKey( hkey );
1989 static inline WCHAR casemap( const USHORT *table, WCHAR ch )
1991 return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
1995 static inline unsigned int casemap_high( const USHORT *table, WCHAR high, WCHAR low )
1997 unsigned int off = table[table[256 + (high - 0xd800)] + ((low >> 5) & 0x1f)] + 2 * (low & 0x1f);
1998 return 0x10000 + ((high - 0xd800) << 10) + (low - 0xdc00) + MAKELONG( table[off], table[off+1] );
2002 static inline BOOL table_has_high_planes( const USHORT *table )
2004 return table[0] >= 0x500;
2008 static inline int put_utf16( WCHAR *dst, int pos, int dstlen, unsigned int ch )
2010 if (ch >= 0x10000)
2012 if (pos < dstlen - 1)
2014 ch -= 0x10000;
2015 dst[pos] = 0xd800 | (ch >> 10);
2016 dst[pos + 1] = 0xdc00 | (ch & 0x3ff);
2018 return 2;
2020 if (pos < dstlen) dst[pos] = ch;
2021 return 1;
2025 static inline WORD get_char_type( DWORD type, WCHAR ch )
2027 const BYTE *ptr = sort.ctype_idx + ((const WORD *)sort.ctype_idx)[ch >> 8];
2028 ptr = sort.ctype_idx + ((const WORD *)ptr)[(ch >> 4) & 0x0f] + (ch & 0x0f);
2029 return sort.ctypes[*ptr * 3 + type / 2];
2033 static inline void map_byterev( const WCHAR *src, int len, WCHAR *dst )
2035 while (len--) *dst++ = RtlUshortByteSwap( *src++ );
2039 static int casemap_string( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
2041 if (table_has_high_planes( table ))
2043 unsigned int ch;
2044 int pos = 0;
2046 while (srclen)
2048 if (srclen > 1 && IS_SURROGATE_PAIR( src[0], src[1] ))
2050 ch = casemap_high( table, src[0], src[1] );
2051 src += 2;
2052 srclen -= 2;
2054 else
2056 ch = casemap( table, *src );
2057 src++;
2058 srclen--;
2060 pos += put_utf16( dst, pos, dstlen, ch );
2062 return pos;
2064 else
2066 int pos, ret = srclen;
2068 for (pos = 0; pos < dstlen && srclen; pos++, src++, srclen--)
2069 dst[pos] = casemap( table, *src );
2070 return ret;
2075 static union char_weights get_char_weights( WCHAR c, UINT except )
2077 union char_weights ret;
2079 ret.val = except ? sort.keys[sort.keys[except + (c >> 8)] + (c & 0xff)] : sort.keys[c];
2080 return ret;
2084 static BYTE rol( BYTE val, BYTE count )
2086 return (val << count) | (val >> (8 - count));
2090 static BYTE get_char_props( const struct norm_table *info, unsigned int ch )
2092 const BYTE *level1 = (const BYTE *)((const USHORT *)info + info->props_level1);
2093 const BYTE *level2 = (const BYTE *)((const USHORT *)info + info->props_level2);
2094 BYTE off = level1[ch / 128];
2096 if (!off || off >= 0xfb) return rol( off, 5 );
2097 return level2[(off - 1) * 128 + ch % 128];
2101 static const WCHAR *get_decomposition( WCHAR ch, unsigned int *ret_len )
2103 const struct pair { WCHAR src; USHORT dst; } *pairs;
2104 const USHORT *hash_table = (const USHORT *)norm_info + norm_info->decomp_hash;
2105 const WCHAR *ret;
2106 unsigned int i, pos, end, len, hash;
2108 *ret_len = 1;
2109 hash = ch % norm_info->decomp_size;
2110 pos = hash_table[hash];
2111 if (pos >> 13)
2113 if (get_char_props( norm_info, ch ) != 0xbf) return NULL;
2114 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pos & 0x1fff);
2115 len = pos >> 13;
2117 else
2119 pairs = (const struct pair *)((const USHORT *)norm_info + norm_info->decomp_map);
2121 /* find the end of the hash bucket */
2122 for (i = hash + 1; i < norm_info->decomp_size; i++) if (!(hash_table[i] >> 13)) break;
2123 if (i < norm_info->decomp_size) end = hash_table[i];
2124 else for (end = pos; pairs[end].src; end++) ;
2126 for ( ; pos < end; pos++)
2128 if (pairs[pos].src != (WCHAR)ch) continue;
2129 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pairs[pos].dst & 0x1fff);
2130 len = pairs[pos].dst >> 13;
2131 break;
2133 if (pos >= end) return NULL;
2136 if (len == 7) while (ret[len]) len++;
2137 if (!ret[0]) len = 0; /* ignored char */
2138 *ret_len = len;
2139 return ret;
2143 static WCHAR compose_chars( WCHAR ch1, WCHAR ch2 )
2145 const USHORT *table = (const USHORT *)norm_info + norm_info->comp_hash;
2146 const WCHAR *chars = (const USHORT *)norm_info + norm_info->comp_seq;
2147 unsigned int hash, start, end, i;
2148 WCHAR ch[3];
2150 hash = (ch1 + 95 * ch2) % norm_info->comp_size;
2151 start = table[hash];
2152 end = table[hash + 1];
2153 while (start < end)
2155 for (i = 0; i < 3; i++, start++)
2157 ch[i] = chars[start];
2158 if (IS_HIGH_SURROGATE( ch[i] )) start++;
2160 if (ch[0] == ch1 && ch[1] == ch2) return ch[2];
2162 return 0;
2166 static UINT get_locale_codepage( const NLS_LOCALE_DATA *locale, ULONG flags )
2168 UINT ret = locale->idefaultansicodepage;
2169 if ((flags & LOCALE_USE_CP_ACP) || ret == CP_UTF8) ret = ansi_cpinfo.CodePage;
2170 return ret;
2174 static UINT get_lcid_codepage( LCID lcid, ULONG flags )
2176 UINT ret = ansi_cpinfo.CodePage;
2178 if (!(flags & LOCALE_USE_CP_ACP) && lcid != system_lcid)
2180 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
2181 if (locale) ret = locale->idefaultansicodepage;
2183 return ret;
2187 static const CPTABLEINFO *get_codepage_table( UINT codepage )
2189 static const CPTABLEINFO utf7_cpinfo = { CP_UTF7, 5, '?', 0xfffd, '?', '?' };
2190 static const CPTABLEINFO utf8_cpinfo = { CP_UTF8, 4, '?', 0xfffd, '?', '?' };
2191 unsigned int i;
2192 USHORT *ptr;
2193 SIZE_T size;
2195 switch (codepage)
2197 case CP_ACP:
2198 return &ansi_cpinfo;
2199 case CP_OEMCP:
2200 return &oem_cpinfo;
2201 case CP_MACCP:
2202 codepage = system_locale->idefaultmaccodepage;
2203 break;
2204 case CP_THREAD_ACP:
2205 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 );
2206 break;
2208 if (codepage == ansi_cpinfo.CodePage) return &ansi_cpinfo;
2209 if (codepage == oem_cpinfo.CodePage) return &oem_cpinfo;
2210 if (codepage == CP_UTF8) return &utf8_cpinfo;
2211 if (codepage == CP_UTF7) return &utf7_cpinfo;
2213 RtlEnterCriticalSection( &locale_section );
2215 for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done;
2217 if (i == ARRAY_SIZE( codepages ))
2219 RtlLeaveCriticalSection( &locale_section );
2220 ERR( "too many codepages\n" );
2221 return NULL;
2223 if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size ))
2225 RtlLeaveCriticalSection( &locale_section );
2226 SetLastError( ERROR_INVALID_PARAMETER );
2227 return NULL;
2229 RtlInitCodePageTable( ptr, &codepages[i] );
2230 nb_codepages++;
2231 done:
2232 RtlLeaveCriticalSection( &locale_section );
2233 return &codepages[i];
2237 static const WCHAR *get_ligature( WCHAR wc )
2239 int low = 0, high = ARRAY_SIZE( ligatures ) -1;
2240 while (low <= high)
2242 int pos = (low + high) / 2;
2243 if (ligatures[pos][0] < wc) low = pos + 1;
2244 else if (ligatures[pos][0] > wc) high = pos - 1;
2245 else return ligatures[pos] + 1;
2247 return NULL;
2251 static NTSTATUS expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2253 int i, len, pos = 0;
2254 NTSTATUS ret = STATUS_SUCCESS;
2255 const WCHAR *expand;
2257 for (i = 0; i < srclen; i++)
2259 if (!(expand = get_ligature( src[i] )))
2261 expand = src + i;
2262 len = 1;
2264 else len = lstrlenW( expand );
2266 if (*dstlen && ret == STATUS_SUCCESS)
2268 if (pos + len <= *dstlen) memcpy( dst + pos, expand, len * sizeof(WCHAR) );
2269 else ret = STATUS_BUFFER_TOO_SMALL;
2271 pos += len;
2273 *dstlen = pos;
2274 return ret;
2278 static NTSTATUS fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2280 NTSTATUS ret = STATUS_SUCCESS;
2281 int len = casemap_string( charmaps[CHARMAP_FOLDDIGITS], src, srclen, dst, *dstlen );
2283 if (*dstlen && *dstlen < len) ret = STATUS_BUFFER_TOO_SMALL;
2284 *dstlen = len;
2285 return ret;
2289 static NTSTATUS fold_string( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
2291 NTSTATUS ret;
2292 WCHAR *tmp;
2294 switch (flags)
2296 case MAP_PRECOMPOSED:
2297 return RtlNormalizeString( NormalizationC, src, srclen, dst, dstlen );
2298 case MAP_FOLDCZONE:
2299 case MAP_PRECOMPOSED | MAP_FOLDCZONE:
2300 return RtlNormalizeString( NormalizationKC, src, srclen, dst, dstlen );
2301 case MAP_COMPOSITE:
2302 return RtlNormalizeString( NormalizationD, src, srclen, dst, dstlen );
2303 case MAP_COMPOSITE | MAP_FOLDCZONE:
2304 return RtlNormalizeString( NormalizationKD, src, srclen, dst, dstlen );
2305 case MAP_FOLDDIGITS:
2306 return fold_digits( src, srclen, dst, dstlen );
2307 case MAP_EXPAND_LIGATURES:
2308 case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
2309 return expand_ligatures( src, srclen, dst, dstlen );
2310 case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
2311 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2312 return STATUS_NO_MEMORY;
2313 fold_digits( src, srclen, tmp, &srclen );
2314 ret = RtlNormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
2315 break;
2316 case MAP_FOLDDIGITS | MAP_FOLDCZONE:
2317 case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
2318 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2319 return STATUS_NO_MEMORY;
2320 fold_digits( src, srclen, tmp, &srclen );
2321 ret = RtlNormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
2322 break;
2323 case MAP_FOLDDIGITS | MAP_COMPOSITE:
2324 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2325 return STATUS_NO_MEMORY;
2326 fold_digits( src, srclen, tmp, &srclen );
2327 ret = RtlNormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
2328 break;
2329 case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
2330 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2331 return STATUS_NO_MEMORY;
2332 fold_digits( src, srclen, tmp, &srclen );
2333 ret = RtlNormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
2334 break;
2335 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
2336 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
2337 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
2338 return STATUS_NO_MEMORY;
2339 fold_digits( src, srclen, tmp, &srclen );
2340 ret = expand_ligatures( tmp, srclen, dst, dstlen );
2341 break;
2342 default:
2343 return STATUS_INVALID_PARAMETER_1;
2345 RtlFreeHeap( GetProcessHeap(), 0, tmp );
2346 return ret;
2350 static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2352 int len, i;
2354 if (flags)
2356 SetLastError( ERROR_INVALID_FLAGS );
2357 return 0;
2359 if (!dstlen) return srclen;
2360 len = min( srclen, dstlen );
2361 for (i = 0; i < len; i++)
2363 unsigned char c = src[i];
2364 dst[i] = (c < 0x20) ? c : c + 0xf000;
2366 if (len < srclen)
2368 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2369 return 0;
2371 return len;
2375 static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2377 static const signed char base64_decoding_table[] =
2379 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2380 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2381 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2382 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2383 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2384 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2385 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2386 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2389 const char *source_end = src + srclen;
2390 int offset = 0, pos = 0;
2391 DWORD byte_pair = 0;
2393 if (flags)
2395 SetLastError( ERROR_INVALID_FLAGS );
2396 return 0;
2398 #define OUTPUT(ch) \
2399 do { \
2400 if (dstlen > 0) \
2402 if (pos >= dstlen) goto overflow; \
2403 dst[pos] = (ch); \
2405 pos++; \
2406 } while(0)
2408 while (src < source_end)
2410 if (*src == '+')
2412 src++;
2413 if (src >= source_end) break;
2414 if (*src == '-')
2416 /* just a plus sign escaped as +- */
2417 OUTPUT( '+' );
2418 src++;
2419 continue;
2424 signed char sextet = *src;
2425 if (sextet == '-')
2427 /* skip over the dash and end base64 decoding
2428 * the current, unfinished byte pair is discarded */
2429 src++;
2430 offset = 0;
2431 break;
2433 if (sextet < 0)
2435 /* the next character of src is < 0 and therefore not part of a base64 sequence
2436 * the current, unfinished byte pair is NOT discarded in this case
2437 * this is probably a bug in Windows */
2438 break;
2440 sextet = base64_decoding_table[sextet];
2441 if (sextet == -1)
2443 /* -1 means that the next character of src is not part of a base64 sequence
2444 * in other words, all sextets in this base64 sequence have been processed
2445 * the current, unfinished byte pair is discarded */
2446 offset = 0;
2447 break;
2450 byte_pair = (byte_pair << 6) | sextet;
2451 offset += 6;
2452 if (offset >= 16)
2454 /* this byte pair is done */
2455 OUTPUT( byte_pair >> (offset - 16) );
2456 offset -= 16;
2458 src++;
2460 while (src < source_end);
2462 else
2464 OUTPUT( (unsigned char)*src );
2465 src++;
2468 return pos;
2470 overflow:
2471 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2472 return 0;
2473 #undef OUTPUT
2477 static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
2479 DWORD reslen;
2480 NTSTATUS status;
2482 if (!dstlen) dst = NULL;
2483 status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen );
2484 if (status == STATUS_SOME_NOT_MAPPED)
2486 if (flags & MB_ERR_INVALID_CHARS)
2488 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2489 return 0;
2492 else if (!set_ntstatus( status )) reslen = 0;
2494 return reslen / sizeof(WCHAR);
2498 static inline int is_private_use_area_char( WCHAR code )
2500 return (code >= 0xe000 && code <= 0xf8ff);
2504 static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen )
2506 if (info->DBCSOffsets)
2508 for ( ; srclen; src++, srclen-- )
2510 USHORT off = info->DBCSOffsets[*src];
2511 if (off)
2513 if (srclen == 1) break; /* partial char, error */
2514 if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar &&
2515 ((src[0] << 8) | src[1]) != info->TransUniDefaultChar) break;
2516 src++;
2517 srclen--;
2518 continue;
2520 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2521 break;
2522 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2525 else
2527 for ( ; srclen; src++, srclen-- )
2529 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
2530 break;
2531 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
2534 return !!srclen;
2539 static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2540 WCHAR *dst, int dstlen )
2542 WCHAR ch;
2543 USHORT off;
2544 int len;
2545 const WCHAR *decomp;
2546 unsigned int decomp_len;
2548 if (info->DBCSOffsets)
2550 if (!dstlen) /* compute length */
2552 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2554 if ((off = info->DBCSOffsets[*src]))
2556 if (srclen > 1 && src[1])
2558 src++;
2559 srclen--;
2560 ch = info->DBCSOffsets[off + *src];
2562 else ch = info->UniDefaultChar;
2564 else ch = info->MultiByteTable[*src];
2565 get_decomposition( ch, &decomp_len );
2567 return len;
2570 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2572 if ((off = info->DBCSOffsets[*src]))
2574 if (srclen > 1 && src[1])
2576 src++;
2577 srclen--;
2578 ch = info->DBCSOffsets[off + *src];
2580 else ch = info->UniDefaultChar;
2582 else ch = info->MultiByteTable[*src];
2584 if ((decomp = get_decomposition( ch, &decomp_len )))
2586 if (len < decomp_len) break;
2587 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2589 else *dst = ch;
2592 else
2594 if (!dstlen) /* compute length */
2596 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2597 get_decomposition( info->MultiByteTable[*src], &decomp_len );
2598 return len;
2601 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2603 ch = info->MultiByteTable[*src];
2604 if ((decomp = get_decomposition( ch, &decomp_len )))
2606 if (len < decomp_len) break;
2607 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2609 else *dst = ch;
2613 if (srclen)
2615 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2616 return 0;
2618 return dstlen - len;
2622 static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2623 WCHAR *dst, int dstlen )
2625 const USHORT *table = info->MultiByteTable;
2626 int ret = srclen;
2628 if (!dstlen) return srclen;
2630 if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */
2632 srclen = dstlen;
2633 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2634 ret = 0;
2637 while (srclen >= 16)
2639 dst[0] = table[src[0]];
2640 dst[1] = table[src[1]];
2641 dst[2] = table[src[2]];
2642 dst[3] = table[src[3]];
2643 dst[4] = table[src[4]];
2644 dst[5] = table[src[5]];
2645 dst[6] = table[src[6]];
2646 dst[7] = table[src[7]];
2647 dst[8] = table[src[8]];
2648 dst[9] = table[src[9]];
2649 dst[10] = table[src[10]];
2650 dst[11] = table[src[11]];
2651 dst[12] = table[src[12]];
2652 dst[13] = table[src[13]];
2653 dst[14] = table[src[14]];
2654 dst[15] = table[src[15]];
2655 src += 16;
2656 dst += 16;
2657 srclen -= 16;
2660 /* now handle the remaining characters */
2661 src += srclen;
2662 dst += srclen;
2663 switch (srclen)
2665 case 15: dst[-15] = table[src[-15]];
2666 case 14: dst[-14] = table[src[-14]];
2667 case 13: dst[-13] = table[src[-13]];
2668 case 12: dst[-12] = table[src[-12]];
2669 case 11: dst[-11] = table[src[-11]];
2670 case 10: dst[-10] = table[src[-10]];
2671 case 9: dst[-9] = table[src[-9]];
2672 case 8: dst[-8] = table[src[-8]];
2673 case 7: dst[-7] = table[src[-7]];
2674 case 6: dst[-6] = table[src[-6]];
2675 case 5: dst[-5] = table[src[-5]];
2676 case 4: dst[-4] = table[src[-4]];
2677 case 3: dst[-3] = table[src[-3]];
2678 case 2: dst[-2] = table[src[-2]];
2679 case 1: dst[-1] = table[src[-1]];
2680 case 0: break;
2682 return ret;
2686 static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2687 WCHAR *dst, int dstlen )
2689 USHORT off;
2690 int i;
2692 if (!dstlen)
2694 for (i = 0; srclen; i++, src++, srclen--)
2695 if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; }
2696 return i;
2699 for (i = dstlen; srclen && i; i--, srclen--, src++, dst++)
2701 if ((off = info->DBCSOffsets[*src]))
2703 if (srclen > 1 && src[1])
2705 src++;
2706 srclen--;
2707 *dst = info->DBCSOffsets[off + *src];
2709 else *dst = info->UniDefaultChar;
2711 else *dst = info->MultiByteTable[*src];
2713 if (srclen)
2715 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2716 return 0;
2718 return dstlen - i;
2722 static int mbstowcs_codepage( const CPTABLEINFO *info, DWORD flags, const char *src, int srclen,
2723 WCHAR *dst, int dstlen )
2725 CPTABLEINFO local_info;
2726 const unsigned char *str = (const unsigned char *)src;
2728 if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256)
2730 local_info = *info;
2731 local_info.MultiByteTable += 257;
2732 info = &local_info;
2734 if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen ))
2736 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2737 return 0;
2740 if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen );
2742 if (info->DBCSOffsets)
2743 return mbstowcs_dbcs( info, str, srclen, dst, dstlen );
2744 else
2745 return mbstowcs_sbcs( info, str, srclen, dst, dstlen );
2749 static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2750 const char *defchar, BOOL *used )
2752 int len, i;
2754 if (flags)
2756 SetLastError( ERROR_INVALID_FLAGS );
2757 return 0;
2759 if (defchar || used)
2761 SetLastError( ERROR_INVALID_PARAMETER );
2762 return 0;
2764 if (!dstlen) return srclen;
2765 len = min( srclen, dstlen );
2766 for (i = 0; i < len; i++)
2768 if (src[i] < 0x20) dst[i] = src[i];
2769 else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000;
2770 else
2772 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2773 return 0;
2776 if (srclen > len)
2778 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2779 return 0;
2781 return len;
2785 static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2786 const char *defchar, BOOL *used )
2788 static const char directly_encodable[] =
2790 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
2791 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
2792 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
2793 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
2794 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
2795 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
2796 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
2797 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
2799 #define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
2801 static const char base64_encoding_table[] =
2802 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2804 const WCHAR *source_end = src + srclen;
2805 int pos = 0;
2807 if (defchar || used)
2809 SetLastError( ERROR_INVALID_PARAMETER );
2810 return 0;
2812 if (flags)
2814 SetLastError( ERROR_INVALID_FLAGS );
2815 return 0;
2818 #define OUTPUT(ch) \
2819 do { \
2820 if (dstlen > 0) \
2822 if (pos >= dstlen) goto overflow; \
2823 dst[pos] = (ch); \
2825 pos++; \
2826 } while (0)
2828 while (src < source_end)
2830 if (*src == '+')
2832 OUTPUT( '+' );
2833 OUTPUT( '-' );
2834 src++;
2836 else if (ENCODABLE(*src))
2838 OUTPUT( *src );
2839 src++;
2841 else
2843 unsigned int offset = 0, byte_pair = 0;
2845 OUTPUT( '+' );
2846 while (src < source_end && !ENCODABLE(*src))
2848 byte_pair = (byte_pair << 16) | *src;
2849 offset += 16;
2850 while (offset >= 6)
2852 offset -= 6;
2853 OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] );
2855 src++;
2857 if (offset)
2859 /* Windows won't create a padded base64 character if there's no room for the - sign
2860 * as well ; this is probably a bug in Windows */
2861 if (dstlen > 0 && pos + 1 >= dstlen) goto overflow;
2862 byte_pair <<= (6 - offset);
2863 OUTPUT( base64_encoding_table[byte_pair & 0x3f] );
2865 /* Windows always explicitly terminates the base64 sequence
2866 even though RFC 2152 (page 3, rule 2) does not require this */
2867 OUTPUT( '-' );
2870 return pos;
2872 overflow:
2873 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2874 return 0;
2875 #undef OUTPUT
2876 #undef ENCODABLE
2880 static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2881 const char *defchar, BOOL *used )
2883 DWORD reslen;
2884 NTSTATUS status;
2886 if (used) *used = FALSE;
2887 if (!dstlen) dst = NULL;
2888 status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
2889 if (status == STATUS_SOME_NOT_MAPPED)
2891 if (flags & WC_ERR_INVALID_CHARS)
2893 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2894 return 0;
2896 if (used) *used = TRUE;
2898 else if (!set_ntstatus( status )) reslen = 0;
2899 return reslen;
2903 static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2904 char *dst, unsigned int dstlen )
2906 const char *table = info->WideCharTable;
2907 int ret = srclen;
2909 if (!dstlen) return srclen;
2911 if (dstlen < srclen)
2913 /* buffer too small: fill it up to dstlen and return error */
2914 srclen = dstlen;
2915 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2916 ret = 0;
2919 while (srclen >= 16)
2921 dst[0] = table[src[0]];
2922 dst[1] = table[src[1]];
2923 dst[2] = table[src[2]];
2924 dst[3] = table[src[3]];
2925 dst[4] = table[src[4]];
2926 dst[5] = table[src[5]];
2927 dst[6] = table[src[6]];
2928 dst[7] = table[src[7]];
2929 dst[8] = table[src[8]];
2930 dst[9] = table[src[9]];
2931 dst[10] = table[src[10]];
2932 dst[11] = table[src[11]];
2933 dst[12] = table[src[12]];
2934 dst[13] = table[src[13]];
2935 dst[14] = table[src[14]];
2936 dst[15] = table[src[15]];
2937 src += 16;
2938 dst += 16;
2939 srclen -= 16;
2942 /* now handle remaining characters */
2943 src += srclen;
2944 dst += srclen;
2945 switch(srclen)
2947 case 15: dst[-15] = table[src[-15]];
2948 case 14: dst[-14] = table[src[-14]];
2949 case 13: dst[-13] = table[src[-13]];
2950 case 12: dst[-12] = table[src[-12]];
2951 case 11: dst[-11] = table[src[-11]];
2952 case 10: dst[-10] = table[src[-10]];
2953 case 9: dst[-9] = table[src[-9]];
2954 case 8: dst[-8] = table[src[-8]];
2955 case 7: dst[-7] = table[src[-7]];
2956 case 6: dst[-6] = table[src[-6]];
2957 case 5: dst[-5] = table[src[-5]];
2958 case 4: dst[-4] = table[src[-4]];
2959 case 3: dst[-3] = table[src[-3]];
2960 case 2: dst[-2] = table[src[-2]];
2961 case 1: dst[-1] = table[src[-1]];
2962 case 0: break;
2964 return ret;
2968 static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2969 char *dst, unsigned int dstlen )
2971 const USHORT *table = info->WideCharTable;
2972 int i;
2974 if (!dstlen)
2976 for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++;
2977 return i;
2980 for (i = dstlen; srclen && i; i--, srclen--, src++)
2982 if (table[*src] & 0xff00)
2984 if (i == 1) break; /* do not output a partial char */
2985 i--;
2986 *dst++ = table[*src] >> 8;
2988 *dst++ = (char)table[*src];
2990 if (srclen)
2992 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2993 return 0;
2995 return dstlen - i;
2999 static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
3001 const unsigned char *table = info->WideCharTable;
3003 if (wch >= 0x10000) return 0;
3004 if ((flags & WC_NO_BEST_FIT_CHARS) || table[wch] == info->DefaultChar)
3005 return (info->MultiByteTable[table[wch]] == wch);
3006 return 1;
3010 static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
3012 const unsigned short *table = info->WideCharTable;
3013 unsigned short ch;
3015 if (wch >= 0x10000) return 0;
3016 ch = table[wch];
3017 if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
3019 if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch;
3020 return info->MultiByteTable[ch] == wch;
3022 return 1;
3026 static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
3027 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
3029 const char *table = info->WideCharTable;
3030 const char def = defchar ? *defchar : (char)info->DefaultChar;
3031 int i;
3032 BOOL tmp;
3033 WCHAR wch;
3034 unsigned int composed;
3036 if (!used) used = &tmp; /* avoid checking on every char */
3037 *used = FALSE;
3039 if (!dstlen)
3041 for (i = 0; srclen; i++, src++, srclen--)
3043 wch = *src;
3044 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
3046 /* now check if we can use the composed char */
3047 if (is_valid_sbcs_mapping( info, flags, composed ))
3049 /* we have a good mapping, use it */
3050 src++;
3051 srclen--;
3052 continue;
3054 /* no mapping for the composed char, check the other flags */
3055 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
3057 *used = TRUE;
3058 src++; /* skip the non-spacing char */
3059 srclen--;
3060 continue;
3062 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
3064 src++;
3065 srclen--;
3067 /* WC_SEPCHARS is the default */
3069 if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch );
3071 return i;
3074 for (i = dstlen; srclen && i; dst++, i--, src++, srclen--)
3076 wch = *src;
3077 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
3079 /* now check if we can use the composed char */
3080 if (is_valid_sbcs_mapping( info, flags, composed ))
3082 /* we have a good mapping, use it */
3083 *dst = table[composed];
3084 src++;
3085 srclen--;
3086 continue;
3088 /* no mapping for the composed char, check the other flags */
3089 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
3091 *dst = def;
3092 *used = TRUE;
3093 src++; /* skip the non-spacing char */
3094 srclen--;
3095 continue;
3097 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
3099 src++;
3100 srclen--;
3102 /* WC_SEPCHARS is the default */
3105 *dst = table[wch];
3106 if (!is_valid_sbcs_mapping( info, flags, wch ))
3108 *dst = def;
3109 *used = TRUE;
3112 if (srclen)
3114 SetLastError( ERROR_INSUFFICIENT_BUFFER );
3115 return 0;
3117 return dstlen - i;
3121 static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
3122 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
3124 const USHORT *table = info->WideCharTable;
3125 WCHAR wch, defchar_value;
3126 unsigned int composed;
3127 unsigned short res;
3128 BOOL tmp;
3129 int i;
3131 if (!defchar[1]) defchar_value = (unsigned char)defchar[0];
3132 else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1];
3134 if (!used) used = &tmp; /* avoid checking on every char */
3135 *used = FALSE;
3137 if (!dstlen)
3139 if (!defchar && !used && !(flags & WC_COMPOSITECHECK))
3141 for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++;
3142 return i;
3144 for (i = 0; srclen; srclen--, src++, i++)
3146 wch = *src;
3147 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
3149 /* now check if we can use the composed char */
3150 if (is_valid_dbcs_mapping( info, flags, composed ))
3152 /* we have a good mapping for the composed char, use it */
3153 res = table[composed];
3154 if (res & 0xff00) i++;
3155 src++;
3156 srclen--;
3157 continue;
3159 /* no mapping for the composed char, check the other flags */
3160 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
3162 if (defchar_value & 0xff00) i++;
3163 *used = TRUE;
3164 src++; /* skip the non-spacing char */
3165 srclen--;
3166 continue;
3168 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
3170 src++;
3171 srclen--;
3173 /* WC_SEPCHARS is the default */
3176 res = table[wch];
3177 if (!is_valid_dbcs_mapping( info, flags, wch ))
3179 res = defchar_value;
3180 *used = TRUE;
3182 if (res & 0xff00) i++;
3184 return i;
3188 for (i = dstlen; srclen && i; i--, srclen--, src++)
3190 wch = *src;
3191 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
3193 /* now check if we can use the composed char */
3194 if (is_valid_dbcs_mapping( info, flags, composed ))
3196 /* we have a good mapping for the composed char, use it */
3197 res = table[composed];
3198 src++;
3199 srclen--;
3200 goto output_char;
3202 /* no mapping for the composed char, check the other flags */
3203 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
3205 res = defchar_value;
3206 *used = TRUE;
3207 src++; /* skip the non-spacing char */
3208 srclen--;
3209 goto output_char;
3211 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
3213 src++;
3214 srclen--;
3216 /* WC_SEPCHARS is the default */
3219 res = table[wch];
3220 if (!is_valid_dbcs_mapping( info, flags, wch ))
3222 res = defchar_value;
3223 *used = TRUE;
3226 output_char:
3227 if (res & 0xff00)
3229 if (i == 1) break; /* do not output a partial char */
3230 i--;
3231 *dst++ = res >> 8;
3233 *dst++ = (char)res;
3235 if (srclen)
3237 SetLastError( ERROR_INSUFFICIENT_BUFFER );
3238 return 0;
3240 return dstlen - i;
3244 static int wcstombs_codepage( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, int srclen,
3245 char *dst, int dstlen, const char *defchar, BOOL *used )
3247 if (flags || defchar || used)
3249 if (!defchar) defchar = (const char *)&info->DefaultChar;
3250 if (info->DBCSOffsets)
3251 return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
3252 else
3253 return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
3255 if (info->DBCSOffsets)
3256 return wcstombs_dbcs( info, src, srclen, dst, dstlen );
3257 else
3258 return wcstombs_sbcs( info, src, srclen, dst, dstlen );
3262 struct sortkey
3264 BYTE *buf;
3265 BYTE *new_buf; /* allocated buf if static buf is not large enough */
3266 UINT size; /* buffer size */
3267 UINT max; /* max possible size */
3268 UINT len; /* current key length */
3271 static void append_sortkey( struct sortkey *key, BYTE val )
3273 if (key->len >= key->max) return;
3274 if (key->len >= key->size)
3276 key->new_buf = RtlAllocateHeap( GetProcessHeap(), 0, key->max );
3277 if (key->new_buf) memcpy( key->new_buf, key->buf, key->len );
3278 else key->max = 0;
3279 key->buf = key->new_buf;
3280 key->size = key->max;
3282 key->buf[key->len++] = val;
3285 static void reverse_sortkey( struct sortkey *key )
3287 int i;
3289 for (i = 0; i < key->len / 2; i++)
3291 BYTE tmp = key->buf[key->len - i - 1];
3292 key->buf[key->len - i - 1] = key->buf[i];
3293 key->buf[i] = tmp;
3297 static int compare_sortkeys( const struct sortkey *key1, const struct sortkey *key2, BOOL shorter_wins )
3299 int ret = memcmp( key1->buf, key2->buf, min( key1->len, key2->len ));
3300 if (!ret) ret = shorter_wins ? key2->len - key1->len : key1->len - key2->len;
3301 return ret;
3304 static void append_normal_weights( const struct sortguid *sortid, struct sortkey *key_primary,
3305 struct sortkey *key_diacritic, struct sortkey *key_case,
3306 union char_weights weights, DWORD flags )
3308 append_sortkey( key_primary, weights.script );
3309 append_sortkey( key_primary, weights.primary );
3311 if ((weights.script >= SCRIPT_PUA_FIRST && weights.script <= SCRIPT_PUA_LAST) ||
3312 ((sortid->flags & FLAG_HAS_3_BYTE_WEIGHTS) &&
3313 (weights.script >= SCRIPT_CJK_FIRST && weights.script <= SCRIPT_CJK_LAST)))
3315 append_sortkey( key_primary, weights.diacritic );
3316 append_sortkey( key_case, weights._case );
3317 return;
3319 if (weights.script <= SCRIPT_ARABIC && weights.script != SCRIPT_HEBREW)
3321 if (flags & LINGUISTIC_IGNOREDIACRITIC) weights.diacritic = 2;
3322 if (flags & LINGUISTIC_IGNORECASE) weights._case = 2;
3324 append_sortkey( key_diacritic, weights.diacritic );
3325 append_sortkey( key_case, weights._case );
3328 static void append_nonspace_weights( struct sortkey *key, union char_weights weights, DWORD flags )
3330 if (flags & LINGUISTIC_IGNOREDIACRITIC) weights.diacritic = 2;
3331 if (key->len) key->buf[key->len - 1] += weights.diacritic;
3332 else append_sortkey( key, weights.diacritic );
3335 static void append_expansion_weights( const struct sortguid *sortid, struct sortkey *key_primary,
3336 struct sortkey *key_diacritic, struct sortkey *key_case,
3337 union char_weights weights, DWORD flags, BOOL is_compare )
3339 /* sortkey and comparison behave differently here */
3340 if (is_compare)
3342 if (weights.script == SCRIPT_UNSORTABLE) return;
3343 if (weights.script == SCRIPT_NONSPACE_MARK)
3345 append_nonspace_weights( key_diacritic, weights, flags );
3346 return;
3349 append_normal_weights( sortid, key_primary, key_diacritic, key_case, weights, flags );
3352 static const UINT *find_compression( const WCHAR *src, const WCHAR *table, int count, int len )
3354 int elem_size = compression_size( len ), min = 0, max = count - 1;
3356 while (min <= max)
3358 int pos = (min + max) / 2;
3359 int res = wcsncmp( src, table + pos * elem_size, len );
3360 if (!res) return (UINT *)(table + (pos + 1) * elem_size) - 1;
3361 if (res > 0) min = pos + 1;
3362 else max = pos - 1;
3364 return NULL;
3367 /* find a compression for a char sequence */
3368 /* return the number of extra chars to skip */
3369 static int get_compression_weights( UINT compression, const WCHAR *compr_tables[8],
3370 const WCHAR *src, int srclen, union char_weights *weights )
3372 const struct sort_compression *compr = sort.compressions + compression;
3373 const UINT *ret;
3374 BYTE size = weights->_case & CASE_COMPR_6;
3375 int i, maxlen = 1;
3377 if (compression >= sort.compr_count) return 0;
3378 if (size == CASE_COMPR_6) maxlen = 8;
3379 else if (size == CASE_COMPR_4) maxlen = 5;
3380 else if (size == CASE_COMPR_2) maxlen = 3;
3381 maxlen = min( maxlen, srclen );
3382 for (i = 0; i < maxlen; i++) if (src[i] < compr->minchar || src[i] > compr->maxchar) break;
3383 maxlen = i;
3384 if (!compr_tables[0])
3386 compr_tables[0] = sort.compr_data + compr->offset;
3387 for (i = 1; i < 8; i++)
3388 compr_tables[i] = compr_tables[i - 1] + compr->len[i - 1] * compression_size( i + 1 );
3390 for (i = maxlen - 2; i >= 0; i--)
3392 if (!(ret = find_compression( src, compr_tables[i], compr->len[i], i + 2 ))) continue;
3393 weights->val = *ret;
3394 return i + 1;
3396 return 0;
3399 /* get the zero digit for the digit character range that contains 'ch' */
3400 static WCHAR get_digit_zero_char( WCHAR ch )
3402 static const WCHAR zeroes[] =
3404 0x0030, 0x0660, 0x06f0, 0x0966, 0x09e6, 0x0a66, 0x0ae6, 0x0b66, 0x0be6, 0x0c66,
3405 0x0ce6, 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x1090, 0x17e0, 0x1810, 0x1946,
3406 0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900, 0xaa50, 0xff10
3408 int min = 0, max = ARRAY_SIZE( zeroes ) - 1;
3410 while (min <= max)
3412 int pos = (min + max) / 2;
3413 if (zeroes[pos] <= ch && zeroes[pos] + 9 >= ch) return zeroes[pos];
3414 if (zeroes[pos] < ch) min = pos + 1;
3415 else max = pos - 1;
3417 return 0;
3420 /* append weights for digits when using SORT_DIGITSASNUMBERS */
3421 /* return the number of extra chars to skip */
3422 static int append_digit_weights( struct sortkey *key, const WCHAR *src, UINT srclen )
3424 UINT i, zero, len, lzero;
3425 BYTE val, values[19];
3427 if (!(zero = get_digit_zero_char( *src ))) return -1;
3429 values[0] = *src - zero;
3430 for (len = 1; len < ARRAY_SIZE(values) && len < srclen; len++)
3432 if (src[len] < zero || src[len] > zero + 9) break;
3433 values[len] = src[len] - zero;
3435 for (lzero = 0; lzero < len; lzero++) if (values[lzero]) break;
3437 append_sortkey( key, SCRIPT_DIGIT );
3438 append_sortkey( key, 2 );
3439 append_sortkey( key, 2 + len - lzero );
3440 for (i = lzero, val = 2; i < len; i++)
3442 if ((len - i) % 2) append_sortkey( key, (val << 4) + values[i] + 2 );
3443 else val = values[i] + 2;
3445 append_sortkey( key, 0xfe - lzero );
3446 return len - 1;
3449 /* append the extra weights for kana prolonged sound / repeat marks */
3450 static int append_extra_kana_weights( struct sortkey keys[4], const WCHAR *src, int pos, UINT except,
3451 BYTE case_mask, union char_weights *weights )
3453 BYTE extra1 = 3, case_weight = weights->_case;
3455 if (weights->primary <= 1)
3457 while (pos > 0)
3459 union char_weights prev = get_char_weights( src[--pos], except );
3460 if (prev.script == SCRIPT_UNSORTABLE || prev.script == SCRIPT_NONSPACE_MARK) continue;
3461 if (prev.script == SCRIPT_EXPANSION) return 0;
3462 if (prev.script != SCRIPT_EASTASIA_SPECIAL)
3464 *weights = prev;
3465 return 1;
3467 if (prev.primary <= 1) continue;
3469 case_weight = prev._case & case_mask;
3470 if (weights->primary == 1) /* prolonged sound mark */
3472 prev.primary &= 0x87;
3473 case_weight &= ~CASE_FULLWIDTH;
3474 case_weight |= weights->_case & CASE_FULLWIDTH;
3476 extra1 = 4 + weights->primary;
3477 weights->primary = prev.primary;
3478 goto done;
3480 return 0;
3482 done:
3483 append_sortkey( &keys[0], 0xc4 | (case_weight & CASE_FULLSIZE) );
3484 append_sortkey( &keys[1], extra1 );
3485 append_sortkey( &keys[2], 0xc4 | (case_weight & CASE_KATAKANA) );
3486 append_sortkey( &keys[3], 0xc4 | (case_weight & CASE_FULLWIDTH) );
3487 weights->script = SCRIPT_KANA;
3488 return 1;
3492 #define HANGUL_SBASE 0xac00
3493 #define HANGUL_LCOUNT 19
3494 #define HANGUL_VCOUNT 21
3495 #define HANGUL_TCOUNT 28
3497 static int append_hangul_weights( struct sortkey *key, const WCHAR *src, int srclen, UINT except )
3499 int leading_idx = 0x115f - 0x1100; /* leading filler */
3500 int vowel_idx = 0x1160 - 0x1100; /* vowel filler */
3501 int trailing_idx = -1;
3502 BYTE leading_off, vowel_off, trailing_off;
3503 union char_weights weights;
3504 WCHAR composed;
3505 BYTE filler_mask = 0;
3506 int pos = 0;
3508 /* leading */
3509 if (src[pos] >= 0x1100 && src[pos] <= 0x115f) leading_idx = src[pos++] - 0x1100;
3510 else if (src[pos] >= 0xa960 && src[pos] <= 0xa97c) leading_idx = src[pos++] - (0xa960 - 0x100);
3512 /* vowel */
3513 if (srclen > pos)
3515 if (src[pos] >= 0x1160 && src[pos] <= 0x11a7) vowel_idx = src[pos++] - 0x1100;
3516 else if (src[pos] >= 0xd7b0 && src[pos] <= 0xd7c6) vowel_idx = src[pos++] - (0xd7b0 - 0x11d);
3519 /* trailing */
3520 if (srclen > pos)
3522 if (src[pos] >= 0x11a8 && src[pos] <= 0x11ff) trailing_idx = src[pos++] - 0x1100;
3523 else if (src[pos] >= 0xd7cb && src[pos] <= 0xd7fb) trailing_idx = src[pos++] - (0xd7cb - 0x134);
3526 if (!sort.jamo[leading_idx].is_old && !sort.jamo[vowel_idx].is_old &&
3527 (trailing_idx == -1 || !sort.jamo[trailing_idx].is_old))
3529 /* not old Hangul, only use leading char; vowel and trailing will be handled in the next pass */
3530 pos = 1;
3531 vowel_idx = 0x1160 - 0x1100;
3532 trailing_idx = -1;
3535 leading_off = max( sort.jamo[leading_idx].leading, sort.jamo[vowel_idx].leading );
3536 vowel_off = max( sort.jamo[leading_idx].vowel, sort.jamo[vowel_idx].vowel );
3537 trailing_off = max( sort.jamo[leading_idx].trailing, sort.jamo[vowel_idx].trailing );
3538 if (trailing_idx != -1) trailing_off = max( trailing_off, sort.jamo[trailing_idx].trailing );
3539 composed = HANGUL_SBASE + (leading_off * HANGUL_VCOUNT + vowel_off) * HANGUL_TCOUNT + trailing_off;
3541 if (leading_idx == 0x115f - 0x1100 || vowel_idx == 0x1160 - 0x1100)
3543 filler_mask = 0x80;
3544 composed--;
3546 if (composed < HANGUL_SBASE) composed = 0x3260;
3548 weights = get_char_weights( composed, except );
3549 append_sortkey( key, weights.script );
3550 append_sortkey( key, weights.primary );
3551 append_sortkey( key, 0xff );
3552 append_sortkey( key, sort.jamo[leading_idx].weight | filler_mask );
3553 append_sortkey( key, 0xff );
3554 append_sortkey( key, sort.jamo[vowel_idx].weight );
3555 append_sortkey( key, 0xff );
3556 append_sortkey( key, trailing_idx != -1 ? sort.jamo[trailing_idx].weight : 2 );
3557 return pos - 1;
3560 /* put one of the elements of a sortkey into the dst buffer */
3561 static int put_sortkey( BYTE *dst, int dstlen, int pos, const struct sortkey *key, BYTE terminator )
3563 if (dstlen > pos + key->len)
3565 memcpy( dst + pos, key->buf, key->len );
3566 dst[pos + key->len] = terminator;
3568 return pos + key->len + 1;
3572 struct sortkey_state
3574 struct sortkey key_primary;
3575 struct sortkey key_diacritic;
3576 struct sortkey key_case;
3577 struct sortkey key_special;
3578 struct sortkey key_extra[4];
3579 UINT primary_pos;
3580 BYTE buffer[3 * 128];
3583 static void init_sortkey_state( struct sortkey_state *s, DWORD flags, UINT srclen,
3584 BYTE *primary_buf, UINT primary_size )
3586 /* buffer for secondary weights */
3587 BYTE *secondary_buf = s->buffer;
3588 UINT secondary_size;
3590 memset( s, 0, offsetof( struct sortkey_state, buffer ));
3592 s->key_primary.buf = primary_buf;
3593 s->key_primary.size = primary_size;
3595 if (!(flags & NORM_IGNORENONSPACE)) /* reserve space for diacritics */
3597 secondary_size = sizeof(s->buffer) / 3;
3598 s->key_diacritic.buf = secondary_buf;
3599 s->key_diacritic.size = secondary_size;
3600 secondary_buf += secondary_size;
3602 else secondary_size = sizeof(s->buffer) / 2;
3604 s->key_case.buf = secondary_buf;
3605 s->key_case.size = secondary_size;
3606 s->key_special.buf = secondary_buf + secondary_size;
3607 s->key_special.size = secondary_size;
3609 s->key_primary.max = srclen * 8;
3610 s->key_case.max = srclen * 3;
3611 s->key_special.max = srclen * 4;
3612 s->key_extra[2].max = s->key_extra[3].max = srclen;
3613 if (!(flags & NORM_IGNORENONSPACE))
3615 s->key_diacritic.max = srclen * 3;
3616 s->key_extra[0].max = s->key_extra[1].max = srclen;
3620 static BOOL remove_unneeded_weights( const struct sortguid *sortid, struct sortkey_state *s )
3622 const BYTE ignore[4] = { 0xc4 | CASE_FULLSIZE, 0x03, 0xc4 | CASE_KATAKANA, 0xc4 | CASE_FULLWIDTH };
3623 int i, j;
3625 if (sortid->flags & FLAG_REVERSEDIACRITICS) reverse_sortkey( &s->key_diacritic );
3627 for (i = s->key_diacritic.len; i > 0; i--) if (s->key_diacritic.buf[i - 1] > 2) break;
3628 s->key_diacritic.len = i;
3630 for (i = s->key_case.len; i > 0; i--) if (s->key_case.buf[i - 1] > 2) break;
3631 s->key_case.len = i;
3633 if (!s->key_extra[2].len) return FALSE;
3635 for (i = 0; i < 4; i++)
3637 for (j = s->key_extra[i].len; j > 0; j--) if (s->key_extra[i].buf[j - 1] != ignore[i]) break;
3638 s->key_extra[i].len = j;
3640 return TRUE;
3643 static void free_sortkey_state( struct sortkey_state *s )
3645 RtlFreeHeap( GetProcessHeap(), 0, s->key_primary.new_buf );
3646 RtlFreeHeap( GetProcessHeap(), 0, s->key_diacritic.new_buf );
3647 RtlFreeHeap( GetProcessHeap(), 0, s->key_case.new_buf );
3648 RtlFreeHeap( GetProcessHeap(), 0, s->key_special.new_buf );
3649 RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[0].new_buf );
3650 RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[1].new_buf );
3651 RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[2].new_buf );
3652 RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[3].new_buf );
3655 static int append_weights( const struct sortguid *sortid, DWORD flags,
3656 const WCHAR *src, int srclen, int pos, BYTE case_mask, UINT except,
3657 const WCHAR *compr_tables[8], struct sortkey_state *s, BOOL is_compare )
3659 union char_weights weights = get_char_weights( src[pos], except );
3660 WCHAR idx = (weights.val >> 16) & ~(CASE_COMPR_6 << 8); /* expansion index */
3661 int ret = 1;
3663 if (weights._case & CASE_COMPR_6)
3664 ret += get_compression_weights( sortid->compr, compr_tables, src + pos, srclen - pos, &weights );
3666 weights._case &= case_mask;
3668 switch (weights.script)
3670 case SCRIPT_UNSORTABLE:
3671 break;
3673 case SCRIPT_NONSPACE_MARK:
3674 append_nonspace_weights( &s->key_diacritic, weights, flags );
3675 break;
3677 case SCRIPT_EXPANSION:
3678 while (weights.script == SCRIPT_EXPANSION)
3680 weights = get_char_weights( sort.expansions[idx].exp[0], except );
3681 weights._case &= case_mask;
3682 append_expansion_weights( sortid, &s->key_primary, &s->key_diacritic,
3683 &s->key_case, weights, flags, is_compare );
3684 weights = get_char_weights( sort.expansions[idx].exp[1], except );
3685 idx = weights.val >> 16;
3686 weights._case &= case_mask;
3688 append_expansion_weights( sortid, &s->key_primary, &s->key_diacritic,
3689 &s->key_case, weights, flags, is_compare );
3690 break;
3692 case SCRIPT_EASTASIA_SPECIAL:
3693 if (!append_extra_kana_weights( s->key_extra, src, pos, except, case_mask, &weights ))
3695 append_sortkey( &s->key_primary, 0xff );
3696 append_sortkey( &s->key_primary, 0xff );
3697 break;
3699 weights._case = 2;
3700 append_normal_weights( sortid, &s->key_primary, &s->key_diacritic, &s->key_case, weights, flags );
3701 break;
3703 case SCRIPT_JAMO_SPECIAL:
3704 ret += append_hangul_weights( &s->key_primary, src + pos, srclen - pos, except );
3705 append_sortkey( &s->key_diacritic, 2 );
3706 append_sortkey( &s->key_case, 2 );
3707 break;
3709 case SCRIPT_EXTENSION_A:
3710 append_sortkey( &s->key_primary, 0xfd );
3711 append_sortkey( &s->key_primary, 0xff );
3712 append_sortkey( &s->key_primary, weights.primary );
3713 append_sortkey( &s->key_primary, weights.diacritic );
3714 append_sortkey( &s->key_diacritic, 2 );
3715 append_sortkey( &s->key_case, 2 );
3716 break;
3718 case SCRIPT_PUNCTUATION:
3719 if (flags & NORM_IGNORESYMBOLS) break;
3720 if (!(flags & SORT_STRINGSORT))
3722 short len = -((s->key_primary.len + s->primary_pos) / 2) - 1;
3723 if (flags & LINGUISTIC_IGNORECASE) weights._case = 2;
3724 if (flags & LINGUISTIC_IGNOREDIACRITIC) weights.diacritic = 2;
3725 append_sortkey( &s->key_special, len >> 8 );
3726 append_sortkey( &s->key_special, len & 0xff );
3727 append_sortkey( &s->key_special, weights.primary );
3728 append_sortkey( &s->key_special, weights._case | (weights.diacritic << 3) );
3729 break;
3731 /* fall through */
3732 case SCRIPT_SYMBOL_1:
3733 case SCRIPT_SYMBOL_2:
3734 case SCRIPT_SYMBOL_3:
3735 case SCRIPT_SYMBOL_4:
3736 case SCRIPT_SYMBOL_5:
3737 case SCRIPT_SYMBOL_6:
3738 if (flags & NORM_IGNORESYMBOLS) break;
3739 append_sortkey( &s->key_primary, weights.script );
3740 append_sortkey( &s->key_primary, weights.primary );
3741 append_sortkey( &s->key_diacritic, weights.diacritic );
3742 append_sortkey( &s->key_case, weights._case );
3743 break;
3745 case SCRIPT_DIGIT:
3746 if (flags & SORT_DIGITSASNUMBERS)
3748 int len = append_digit_weights( &s->key_primary, src + pos, srclen - pos );
3749 if (len >= 0)
3751 ret += len;
3752 append_sortkey( &s->key_diacritic, weights.diacritic );
3753 append_sortkey( &s->key_case, weights._case );
3754 break;
3757 /* fall through */
3758 default:
3759 append_normal_weights( sortid, &s->key_primary, &s->key_diacritic, &s->key_case, weights, flags );
3760 break;
3763 return ret;
3766 /* implementation of LCMAP_SORTKEY */
3767 static int get_sortkey( const struct sortguid *sortid, DWORD flags,
3768 const WCHAR *src, int srclen, BYTE *dst, int dstlen )
3770 struct sortkey_state s;
3771 BYTE primary_buf[256];
3772 int ret = 0, pos = 0;
3773 BOOL have_extra;
3774 BYTE case_mask = 0x3f;
3775 UINT except = sortid->except;
3776 const WCHAR *compr_tables[8];
3778 compr_tables[0] = NULL;
3779 if (flags & NORM_IGNORECASE) case_mask &= ~(CASE_UPPER | CASE_SUBSCRIPT);
3780 if (flags & NORM_IGNOREWIDTH) case_mask &= ~CASE_FULLWIDTH;
3781 if (flags & NORM_IGNOREKANATYPE) case_mask &= ~CASE_KATAKANA;
3782 if ((flags & NORM_LINGUISTIC_CASING) && except && sortid->ling_except) except = sortid->ling_except;
3784 init_sortkey_state( &s, flags, srclen, primary_buf, sizeof(primary_buf) );
3786 while (pos < srclen)
3787 pos += append_weights( sortid, flags, src, srclen, pos, case_mask, except, compr_tables, &s, FALSE );
3789 have_extra = remove_unneeded_weights( sortid, &s );
3791 ret = put_sortkey( dst, dstlen, ret, &s.key_primary, 0x01 );
3792 ret = put_sortkey( dst, dstlen, ret, &s.key_diacritic, 0x01 );
3793 ret = put_sortkey( dst, dstlen, ret, &s.key_case, 0x01 );
3795 if (have_extra)
3797 ret = put_sortkey( dst, dstlen, ret, &s.key_extra[0], 0xff );
3798 ret = put_sortkey( dst, dstlen, ret, &s.key_extra[1], 0x02 );
3799 ret = put_sortkey( dst, dstlen, ret, &s.key_extra[2], 0xff );
3800 ret = put_sortkey( dst, dstlen, ret, &s.key_extra[3], 0xff );
3802 if (dstlen > ret) dst[ret] = 0x01;
3803 ret++;
3805 ret = put_sortkey( dst, dstlen, ret, &s.key_special, 0 );
3807 free_sortkey_state( &s );
3809 if (dstlen && dstlen < ret)
3811 SetLastError( ERROR_INSUFFICIENT_BUFFER );
3812 return 0;
3814 if (flags & LCMAP_BYTEREV)
3815 map_byterev( (WCHAR *)dst, min( ret, dstlen ) / sizeof(WCHAR), (WCHAR *)dst );
3816 return ret;
3820 /* implementation of CompareStringEx */
3821 static int compare_string( const struct sortguid *sortid, DWORD flags,
3822 const WCHAR *src1, int srclen1, const WCHAR *src2, int srclen2 )
3824 struct sortkey_state s1;
3825 struct sortkey_state s2;
3826 BYTE primary1[32];
3827 BYTE primary2[32];
3828 int i, ret, len, pos1 = 0, pos2 = 0;
3829 BOOL have_extra1, have_extra2;
3830 BYTE case_mask = 0x3f;
3831 UINT except = sortid->except;
3832 const WCHAR *compr_tables[8];
3834 compr_tables[0] = NULL;
3835 if (flags & NORM_IGNORECASE) case_mask &= ~(CASE_UPPER | CASE_SUBSCRIPT);
3836 if (flags & NORM_IGNOREWIDTH) case_mask &= ~CASE_FULLWIDTH;
3837 if (flags & NORM_IGNOREKANATYPE) case_mask &= ~CASE_KATAKANA;
3838 if ((flags & NORM_LINGUISTIC_CASING) && except && sortid->ling_except) except = sortid->ling_except;
3840 init_sortkey_state( &s1, flags, srclen1, primary1, sizeof(primary1) );
3841 init_sortkey_state( &s2, flags, srclen2, primary2, sizeof(primary2) );
3843 while (pos1 < srclen1 || pos2 < srclen2)
3845 while (pos1 < srclen1 && !s1.key_primary.len)
3846 pos1 += append_weights( sortid, flags, src1, srclen1, pos1,
3847 case_mask, except, compr_tables, &s1, TRUE );
3849 while (pos2 < srclen2 && !s2.key_primary.len)
3850 pos2 += append_weights( sortid, flags, src2, srclen2, pos2,
3851 case_mask, except, compr_tables, &s2, TRUE );
3853 if (!(len = min( s1.key_primary.len, s2.key_primary.len ))) break;
3854 if ((ret = memcmp( primary1, primary2, len ))) goto done;
3855 memmove( primary1, primary1 + len, s1.key_primary.len - len );
3856 memmove( primary2, primary2 + len, s2.key_primary.len - len );
3857 s1.key_primary.len -= len;
3858 s2.key_primary.len -= len;
3859 s1.primary_pos += len;
3860 s2.primary_pos += len;
3863 if ((ret = s1.key_primary.len - s2.key_primary.len)) goto done;
3865 have_extra1 = remove_unneeded_weights( sortid, &s1 );
3866 have_extra2 = remove_unneeded_weights( sortid, &s2 );
3868 if ((ret = compare_sortkeys( &s1.key_diacritic, &s2.key_diacritic, FALSE ))) goto done;
3869 if ((ret = compare_sortkeys( &s1.key_case, &s2.key_case, FALSE ))) goto done;
3871 if (have_extra1 && have_extra2)
3873 for (i = 0; i < 4; i++)
3874 if ((ret = compare_sortkeys( &s1.key_extra[i], &s2.key_extra[i], i != 1 ))) goto done;
3876 else if ((ret = have_extra1 - have_extra2)) goto done;
3878 ret = compare_sortkeys( &s1.key_special, &s2.key_special, FALSE );
3880 done:
3881 free_sortkey_state( &s1 );
3882 free_sortkey_state( &s2 );
3883 return ret;
3887 /* implementation of FindNLSStringEx */
3888 static int find_substring( const struct sortguid *sortid, DWORD flags, const WCHAR *src, int srclen,
3889 const WCHAR *value, int valuelen, int *reslen )
3891 struct sortkey_state s;
3892 struct sortkey_state val;
3893 BYTE primary[32];
3894 BYTE primary_val[256];
3895 int i, start, len, found = -1, foundlen = 0, pos = 0;
3896 BOOL have_extra, have_extra_val;
3897 BYTE case_mask = 0x3f;
3898 UINT except = sortid->except;
3899 const WCHAR *compr_tables[8];
3901 compr_tables[0] = NULL;
3902 if (flags & NORM_IGNORECASE) case_mask &= ~(CASE_UPPER | CASE_SUBSCRIPT);
3903 if (flags & NORM_IGNOREWIDTH) case_mask &= ~CASE_FULLWIDTH;
3904 if (flags & NORM_IGNOREKANATYPE) case_mask &= ~CASE_KATAKANA;
3905 if ((flags & NORM_LINGUISTIC_CASING) && except && sortid->ling_except) except = sortid->ling_except;
3907 init_sortkey_state( &s, flags, srclen, primary, sizeof(primary) );
3909 /* build the value sortkey just once */
3910 init_sortkey_state( &val, flags, valuelen, primary_val, sizeof(primary_val) );
3911 while (pos < valuelen)
3912 pos += append_weights( sortid, flags, value, valuelen, pos,
3913 case_mask, except, compr_tables, &val, TRUE );
3914 have_extra_val = remove_unneeded_weights( sortid, &val );
3916 for (start = 0; start < srclen; start++)
3918 pos = start;
3919 for (len = start + 1; len <= srclen; len++)
3921 while (pos < len && s.primary_pos <= val.key_primary.len)
3923 while (pos < len && !s.key_primary.len)
3924 pos += append_weights( sortid, flags, src, srclen, pos,
3925 case_mask, except, compr_tables, &s, TRUE );
3927 if (s.primary_pos + s.key_primary.len > val.key_primary.len ||
3928 memcmp( primary, val.key_primary.buf + s.primary_pos, s.key_primary.len ))
3930 len = srclen + 1;
3931 goto next;
3933 s.primary_pos += s.key_primary.len;
3934 s.key_primary.len = 0;
3936 if (s.primary_pos < val.key_primary.len) continue;
3938 have_extra = remove_unneeded_weights( sortid, &s );
3939 if (compare_sortkeys( &s.key_diacritic, &val.key_diacritic, FALSE )) goto next;
3940 if (compare_sortkeys( &s.key_case, &val.key_case, FALSE )) goto next;
3942 if (have_extra && have_extra_val)
3944 for (i = 0; i < 4; i++)
3945 if (compare_sortkeys( &s.key_extra[i], &val.key_extra[i], i != 1 )) goto next;
3947 else if (have_extra || have_extra_val) goto next;
3949 if (compare_sortkeys( &s.key_special, &val.key_special, FALSE )) goto next;
3951 found = start;
3952 foundlen = pos - start;
3953 len = srclen; /* no need to continue checking longer strings */
3955 next:
3956 /* reset state */
3957 s.key_primary.len = s.key_diacritic.len = s.key_case.len = s.key_special.len = 0;
3958 s.key_extra[0].len = s.key_extra[1].len = s.key_extra[2].len = s.key_extra[3].len = 0;
3959 s.primary_pos = 0;
3960 pos = start;
3962 if (flags & FIND_STARTSWITH) break;
3963 if (flags & FIND_FROMSTART && found != -1) break;
3966 if (found != -1)
3968 if ((flags & FIND_ENDSWITH) && found + foundlen != srclen) found = -1;
3969 else if (reslen) *reslen = foundlen;
3971 free_sortkey_state( &s );
3972 free_sortkey_state( &val );
3973 return found;
3977 /* map buffer to full-width katakana */
3978 static int map_to_fullwidth( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
3980 int pos, len;
3982 for (pos = 0; srclen; pos++, src += len, srclen -= len)
3984 unsigned int wch = casemap( charmaps[CHARMAP_FULLWIDTH], *src );
3986 len = 1;
3987 if (srclen > 1)
3989 if (table_has_high_planes( charmaps[CHARMAP_FULLWIDTH] ) && IS_SURROGATE_PAIR( src[0], src[1] ))
3991 len = 2;
3992 wch = casemap_high( charmaps[CHARMAP_FULLWIDTH], src[0], src[1] );
3993 if (wch >= 0x10000)
3995 put_utf16( dst, pos, dstlen, wch );
3996 pos++;
3997 continue;
4000 else if (src[1] == 0xff9e) /* dakuten (voiced sound) */
4002 len = 2;
4003 if ((*src >= 0xff76 && *src <= 0xff84) ||
4004 (*src >= 0xff8a && *src <= 0xff8e) ||
4005 *src == 0x30fd)
4006 wch++;
4007 else if (*src == 0xff73)
4008 wch = 0x30f4; /* KATAKANA LETTER VU */
4009 else if (*src == 0xff9c)
4010 wch = 0x30f7; /* KATAKANA LETTER VA */
4011 else if (*src == 0x30f0)
4012 wch = 0x30f8; /* KATAKANA LETTER VI */
4013 else if (*src == 0x30f1)
4014 wch = 0x30f9; /* KATAKANA LETTER VE */
4015 else if (*src == 0xff66)
4016 wch = 0x30fa; /* KATAKANA LETTER VO */
4017 else
4018 len = 1;
4020 else if (src[1] == 0xff9f) /* handakuten (semi-voiced sound) */
4022 if (*src >= 0xff8a && *src <= 0xff8e)
4024 wch += 2;
4025 len = 2;
4030 if (pos < dstlen) dst[pos] = table ? casemap( table, wch ) : wch;
4032 return pos;
4036 static inline int nonspace_ignored( WCHAR ch )
4038 if (get_char_type( CT_CTYPE2, ch ) != C2_OTHERNEUTRAL) return FALSE;
4039 return (get_char_type( CT_CTYPE3, ch ) & (C3_NONSPACING | C3_DIACRITIC));
4042 /* remove ignored chars for NORM_IGNORENONSPACE/NORM_IGNORESYMBOLS */
4043 static int map_remove_ignored( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
4045 int pos;
4047 for (pos = 0; srclen; src++, srclen--)
4049 if (flags & NORM_IGNORESYMBOLS)
4051 if (get_char_type( CT_CTYPE1, *src ) & C1_PUNCT) continue;
4052 if (get_char_type( CT_CTYPE3, *src ) & C3_SYMBOL) continue;
4054 if (flags & NORM_IGNORENONSPACE)
4056 WCHAR buffer[8];
4057 const WCHAR *decomp;
4058 unsigned int i, j, len;
4060 if ((decomp = get_decomposition( *src, &len )) && len > 1)
4062 for (i = j = 0; i < len; i++)
4063 if (!nonspace_ignored( decomp[i] )) buffer[j++] = decomp[i];
4065 if (i > j) /* something was removed */
4067 if (pos + j <= dstlen) memcpy( dst + pos, buffer, j * sizeof(WCHAR) );
4068 pos += j;
4069 continue;
4072 else if (nonspace_ignored( *src )) continue;
4074 if (pos < dstlen) dst[pos] = *src;
4075 pos++;
4077 return pos;
4081 /* map full-width characters to single or double half-width characters. */
4082 static int map_to_halfwidth( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
4084 static const BYTE katakana_map[] =
4086 0x01, 0x00, 0x01, 0x00, /* U+30a8- */
4087 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b0- */
4088 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b8- */
4089 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, /* U+30c0- */
4090 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30c8- */
4091 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, /* U+30d0- */
4092 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x00, /* U+30d8- */
4093 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e0- */
4094 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e8- */
4095 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
4096 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x01 /* U+30f8- */
4098 int pos;
4100 for (pos = 0; srclen; src++, srclen--)
4102 WCHAR ch = table ? casemap( table, *src ) : *src;
4103 USHORT shift = ch - 0x30ac;
4104 BYTE k;
4106 if (shift < ARRAY_SIZE(katakana_map) && (k = katakana_map[shift]))
4108 if (pos < dstlen - 1)
4110 dst[pos] = casemap( charmaps[CHARMAP_HALFWIDTH], ch - k );
4111 dst[pos + 1] = (k == 2) ? 0xff9f : 0xff9e;
4113 pos += 2;
4115 else
4117 if (pos < dstlen) dst[pos] = casemap( charmaps[CHARMAP_HALFWIDTH], ch );
4118 pos++;
4121 return pos;
4125 static int lcmap_string( const struct sortguid *sortid, DWORD flags,
4126 const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
4128 const USHORT *case_table = NULL;
4129 int ret;
4131 if (flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE))
4133 if ((flags & LCMAP_TITLECASE) == LCMAP_TITLECASE) /* FIXME */
4135 SetLastError( ERROR_INVALID_FLAGS );
4136 return 0;
4138 case_table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
4139 case_table = case_table + 2 + (flags & LCMAP_LOWERCASE ? case_table[1] : 0);
4142 switch (flags & ~(LCMAP_BYTEREV | LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING))
4144 case LCMAP_HIRAGANA:
4145 ret = casemap_string( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen );
4146 break;
4147 case LCMAP_KATAKANA:
4148 ret = casemap_string( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen );
4149 break;
4150 case LCMAP_HALFWIDTH:
4151 ret = map_to_halfwidth( NULL, src, srclen, dst, dstlen );
4152 break;
4153 case LCMAP_HIRAGANA | LCMAP_HALFWIDTH:
4154 ret = map_to_halfwidth( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen );
4155 break;
4156 case LCMAP_KATAKANA | LCMAP_HALFWIDTH:
4157 ret = map_to_halfwidth( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen );
4158 break;
4159 case LCMAP_FULLWIDTH:
4160 ret = map_to_fullwidth( NULL, src, srclen, dst, dstlen );
4161 break;
4162 case LCMAP_HIRAGANA | LCMAP_FULLWIDTH:
4163 ret = map_to_fullwidth( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen );
4164 break;
4165 case LCMAP_KATAKANA | LCMAP_FULLWIDTH:
4166 ret = map_to_fullwidth( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen );
4167 break;
4168 case LCMAP_SIMPLIFIED_CHINESE:
4169 ret = casemap_string( charmaps[CHARMAP_SIMPLIFIED], src, srclen, dst, dstlen );
4170 break;
4171 case LCMAP_TRADITIONAL_CHINESE:
4172 ret = casemap_string( charmaps[CHARMAP_TRADITIONAL], src, srclen, dst, dstlen );
4173 break;
4174 case NORM_IGNORENONSPACE:
4175 case NORM_IGNORESYMBOLS:
4176 case NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS:
4177 if (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | LCMAP_BYTEREV))
4179 SetLastError( ERROR_INVALID_FLAGS );
4180 return 0;
4182 ret = map_remove_ignored( flags, src, srclen, dst, dstlen );
4183 break;
4184 case 0:
4185 if (case_table)
4187 ret = casemap_string( case_table, src, srclen, dst, dstlen );
4188 case_table = NULL;
4189 break;
4191 if (flags & LCMAP_BYTEREV)
4193 ret = min( srclen, dstlen );
4194 memcpy( dst, src, ret * sizeof(WCHAR) );
4195 break;
4197 /* fall through */
4198 default:
4199 SetLastError( ERROR_INVALID_FLAGS );
4200 return 0;
4203 if (dstlen && case_table) ret = casemap_string( case_table, dst, ret, dst, dstlen );
4204 if (flags & LCMAP_BYTEREV) map_byterev( dst, min( dstlen, ret ), dst );
4206 if (dstlen && dstlen < ret)
4208 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4209 return 0;
4211 return ret;
4215 static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare )
4217 static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
4218 int first, last, limit, dayinsecs;
4220 if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */
4221 if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */
4223 /* if year is 0 then date is in day-of-week format, otherwise
4224 * it's absolute date.
4226 if (!compare->wYear)
4228 /* wDay is interpreted as number of the week in the month
4229 * 5 means: the last week in the month */
4230 /* calculate the day of the first DayOfWeek in the month */
4231 first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1;
4232 /* check needed for the 5th weekday of the month */
4233 last = month_lengths[tf->Month - 1] +
4234 (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400))));
4235 limit = first + 7 * (compare->wDay - 1);
4236 if (limit > last) limit -= 7;
4238 else limit = compare->wDay;
4240 limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60;
4241 dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second;
4242 return dayinsecs - limit;
4246 static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local )
4248 int year;
4249 BOOL before_standard_date, after_daylight_date;
4250 LARGE_INTEGER t2;
4251 TIME_FIELDS tf;
4253 if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN;
4255 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
4256 if (info->StandardDate.wMonth == 0 ||
4257 (info->StandardDate.wYear == 0 &&
4258 (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 ||
4259 info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5)))
4261 SetLastError( ERROR_INVALID_PARAMETER );
4262 return TIME_ZONE_ID_INVALID;
4265 if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000;
4266 RtlTimeToTimeFields( &time, &tf );
4267 year = tf.Year;
4268 if (!is_local)
4270 t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000;
4271 RtlTimeToTimeFields( &t2, &tf );
4273 if (tf.Year == year)
4274 before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0;
4275 else
4276 before_standard_date = tf.Year < year;
4278 if (!is_local)
4280 t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000;
4281 RtlTimeToTimeFields( &t2, &tf );
4283 if (tf.Year == year)
4284 after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0;
4285 else
4286 after_daylight_date = tf.Year > year;
4288 if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */
4290 if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
4292 else /* Down south */
4294 if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
4296 return TIME_ZONE_ID_STANDARD;
4300 /* Note: the Internal_ functions are not documented. The number of parameters
4301 * should be correct, but their exact meaning may not.
4304 /******************************************************************************
4305 * Internal_EnumCalendarInfo (kernelbase.@)
4307 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc,
4308 const NLS_LOCALE_DATA *locale, CALID id,
4309 CALTYPE type, BOOL unicode, BOOL ex,
4310 BOOL exex, LPARAM lparam )
4312 const USHORT *calendars;
4313 USHORT cal = id;
4314 WCHAR buffer[256];
4315 INT ret, i, count = 1;
4317 if (!proc || !locale)
4319 SetLastError( ERROR_INVALID_PARAMETER );
4320 return FALSE;
4323 if (id == ENUM_ALL_CALENDARS)
4325 count = locale_strings[locale->scalendartype];
4326 calendars = locale_strings + locale->scalendartype + 1;
4328 else if (id <= CAL_UMALQURA)
4330 calendars = &cal;
4331 count = 1;
4333 else
4335 SetLastError( ERROR_INVALID_PARAMETER );
4336 return FALSE;
4339 for (i = 0; i < count; i++)
4341 id = calendars[i];
4342 if (type & CAL_RETURN_NUMBER)
4343 ret = get_calendar_info( locale, id, type, NULL, 0, (LPDWORD)buffer );
4344 else if (unicode)
4345 ret = get_calendar_info( locale, id, type, buffer, ARRAY_SIZE(buffer), NULL );
4346 else
4348 WCHAR bufW[256];
4349 ret = get_calendar_info( locale, id, type, bufW, ARRAY_SIZE(bufW), NULL );
4350 if (ret) WideCharToMultiByte( get_locale_codepage( locale, type ), 0,
4351 bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
4354 if (ret)
4356 if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
4357 else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
4358 else ret = proc( buffer );
4360 if (!ret) break;
4362 return TRUE;
4366 static BOOL call_enum_date_func( DATEFMT_ENUMPROCW proc, const NLS_LOCALE_DATA *locale, DWORD flags,
4367 DWORD str, WCHAR *buffer, CALID id, BOOL unicode,
4368 BOOL ex, BOOL exex, LPARAM lparam )
4370 char buffA[256];
4372 if (str) memcpy( buffer, locale_strings + str + 1, (locale_strings[str] + 1) * sizeof(WCHAR) );
4373 if (exex) return ((DATEFMT_ENUMPROCEXEX)proc)( buffer, id, lparam );
4374 if (ex) return ((DATEFMT_ENUMPROCEXW)proc)( buffer, id );
4375 if (unicode) return proc( buffer );
4376 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
4377 buffA, ARRAY_SIZE(buffA), NULL, NULL );
4378 return proc( (WCHAR *)buffA );
4382 /**************************************************************************
4383 * Internal_EnumDateFormats (kernelbase.@)
4385 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc,
4386 const NLS_LOCALE_DATA *locale, DWORD flags,
4387 BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
4389 WCHAR buffer[256];
4390 INT i, j, ret;
4391 DWORD pos;
4392 const struct calendar *cal;
4393 const USHORT *calendars;
4394 const DWORD *array;
4396 if (!proc || !locale)
4398 SetLastError( ERROR_INVALID_PARAMETER );
4399 return FALSE;
4402 calendars = locale_strings + locale->scalendartype;
4404 switch (flags & ~LOCALE_USE_CP_ACP)
4406 case 0:
4407 case DATE_SHORTDATE:
4408 if (!get_locale_info( locale, 0, LOCALE_SSHORTDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4409 pos = locale->sshortdate;
4410 break;
4411 case DATE_LONGDATE:
4412 if (!get_locale_info( locale, 0, LOCALE_SLONGDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4413 pos = locale->slongdate;
4414 break;
4415 case DATE_YEARMONTH:
4416 if (!get_locale_info( locale, 0, LOCALE_SYEARMONTH, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4417 pos = locale->syearmonth;
4418 break;
4419 default:
4420 SetLastError( ERROR_INVALID_PARAMETER );
4421 return FALSE;
4424 /* first the user override data */
4426 ret = call_enum_date_func( proc, locale, flags, 0, buffer, 1, unicode, ex, exex, lparam );
4428 /* then the remaining locale data */
4430 array = (const DWORD *)(locale_strings + pos + 1);
4431 for (i = 1; ret && i < locale_strings[pos]; i++)
4432 ret = call_enum_date_func( proc, locale, flags, array[i], buffer, 1, unicode, ex, exex, lparam );
4434 /* then the extra calendars */
4436 for (i = 0; ret && i < calendars[0]; i++)
4438 if (calendars[i + 1] == 1) continue;
4439 if (!(cal = get_calendar_data( locale, calendars[i + 1] ))) continue;
4440 switch (flags & ~LOCALE_USE_CP_ACP)
4442 case 0:
4443 case DATE_SHORTDATE:
4444 pos = cal->sshortdate;
4445 break;
4446 case DATE_LONGDATE:
4447 pos = cal->slongdate;
4448 break;
4449 case DATE_YEARMONTH:
4450 pos = cal->syearmonth;
4451 break;
4453 array = (const DWORD *)(locale_strings + pos + 1);
4454 for (j = 0; ret && j < locale_strings[pos]; j++)
4455 ret = call_enum_date_func( proc, locale, flags, array[j], buffer,
4456 calendars[i + 1], unicode, ex, exex, lparam );
4458 return TRUE;
4462 /******************************************************************************
4463 * Internal_EnumLanguageGroupLocales (kernelbase.@)
4465 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
4466 DWORD flags, LONG_PTR param, BOOL unicode )
4468 WCHAR name[10], value[10];
4469 DWORD name_len, value_len, type, index = 0, alt = 0;
4470 HKEY key, altkey;
4471 LCID lcid;
4473 if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
4475 SetLastError( ERROR_INVALID_PARAMETER );
4476 return FALSE;
4479 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
4480 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
4482 for (;;)
4484 name_len = ARRAY_SIZE(name);
4485 value_len = sizeof(value);
4486 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
4487 &type, (BYTE *)value, &value_len ))
4489 if (alt++) break;
4490 index = 0;
4491 continue;
4493 if (type != REG_SZ) continue;
4494 if (id != wcstoul( value, NULL, 16 )) continue;
4495 lcid = wcstoul( name, NULL, 16 );
4496 if (!unicode)
4498 char nameA[10];
4499 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
4500 if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
4502 else if (!proc( id, lcid, name, param )) break;
4504 RegCloseKey( altkey );
4505 RegCloseKey( key );
4506 return TRUE;
4510 /***********************************************************************
4511 * Internal_EnumSystemCodePages (kernelbase.@)
4513 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
4514 BOOL unicode )
4516 WCHAR name[10];
4517 DWORD name_len, type, index = 0;
4518 HKEY key;
4520 if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
4522 for (;;)
4524 name_len = ARRAY_SIZE(name);
4525 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
4526 if (type != REG_SZ) continue;
4527 if (!wcstoul( name, NULL, 10 )) continue;
4528 if (!unicode)
4530 char nameA[10];
4531 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
4532 if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
4534 else if (!proc( name )) break;
4536 RegCloseKey( key );
4537 return TRUE;
4541 /******************************************************************************
4542 * Internal_EnumSystemLanguageGroups (kernelbase.@)
4544 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
4545 DWORD flags, LONG_PTR param, BOOL unicode )
4547 WCHAR name[10], value[10], descr[80];
4548 DWORD name_len, value_len, type, index = 0;
4549 HKEY key;
4550 LGRPID id;
4552 if (!proc)
4554 SetLastError( ERROR_INVALID_PARAMETER );
4555 return FALSE;
4558 switch (flags)
4560 case 0:
4561 flags = LGRPID_INSTALLED;
4562 break;
4563 case LGRPID_INSTALLED:
4564 case LGRPID_SUPPORTED:
4565 break;
4566 default:
4567 SetLastError( ERROR_INVALID_FLAGS );
4568 return FALSE;
4571 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
4573 for (;;)
4575 name_len = ARRAY_SIZE(name);
4576 value_len = sizeof(value);
4577 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
4578 if (type != REG_SZ) continue;
4580 id = wcstoul( name, NULL, 16 );
4582 if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
4583 if (!LoadStringW( kernelbase_handle, id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
4584 TRACE( "%p: %lu %s %s %lx %Ix\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
4585 if (!unicode)
4587 char nameA[10], descrA[80];
4588 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
4589 WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
4590 if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
4592 else if (!proc( id, name, descr, flags, param )) break;
4594 RegCloseKey( key );
4595 return TRUE;
4599 /**************************************************************************
4600 * Internal_EnumTimeFormats (kernelbase.@)
4602 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc,
4603 const NLS_LOCALE_DATA *locale, DWORD flags,
4604 BOOL unicode, BOOL ex, LPARAM lparam )
4606 WCHAR buffer[256];
4607 INT ret = TRUE;
4608 const DWORD *array;
4609 DWORD pos, i;
4611 if (!proc || !locale)
4613 SetLastError( ERROR_INVALID_PARAMETER );
4614 return FALSE;
4616 switch (flags & ~LOCALE_USE_CP_ACP)
4618 case 0:
4619 if (!get_locale_info( locale, 0, LOCALE_STIMEFORMAT, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4620 pos = locale->stimeformat;
4621 break;
4622 case TIME_NOSECONDS:
4623 if (!get_locale_info( locale, 0, LOCALE_SSHORTTIME, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4624 pos = locale->sshorttime;
4625 break;
4626 default:
4627 FIXME( "Unknown time format %lx\n", flags );
4628 SetLastError( ERROR_INVALID_PARAMETER );
4629 return FALSE;
4632 array = (const DWORD *)(locale_strings + pos + 1);
4633 for (i = 0; ret && i < locale_strings[pos]; i++)
4635 if (i) memcpy( buffer, locale_strings + array[i] + 1,
4636 (locale_strings[array[i]] + 1) * sizeof(WCHAR) );
4638 if (ex) ret = ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
4639 else if (unicode) ret = proc( buffer );
4640 else
4642 char buffA[256];
4643 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
4644 buffA, ARRAY_SIZE(buffA), NULL, NULL );
4645 ret = proc( (WCHAR *)buffA );
4648 return TRUE;
4652 /******************************************************************************
4653 * Internal_EnumUILanguages (kernelbase.@)
4655 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
4656 LONG_PTR param, BOOL unicode )
4658 WCHAR nameW[LOCALE_NAME_MAX_LENGTH];
4659 char nameA[LOCALE_NAME_MAX_LENGTH];
4660 DWORD i;
4662 if (!proc)
4664 SetLastError( ERROR_INVALID_PARAMETER );
4665 return FALSE;
4667 if (flags & ~(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))
4669 SetLastError( ERROR_INVALID_FLAGS );
4670 return FALSE;
4673 for (i = 0; i < locale_table->nb_lcnames; i++)
4675 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4676 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4677 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4678 if (SORTIDFROMLCID( lcnames_index[i].id )) continue; /* skip alternate sorts */
4679 if (flags & MUI_LANGUAGE_NAME)
4681 const WCHAR *str = locale_strings + lcnames_index[i].name;
4683 if (unicode)
4685 memcpy( nameW, str + 1, (*str + 1) * sizeof(WCHAR) );
4686 if (!proc( nameW, param )) break;
4688 else
4690 WideCharToMultiByte( CP_ACP, 0, str + 1, -1, nameA, sizeof(nameA), NULL, NULL );
4691 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
4694 else
4696 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4697 if (unicode)
4699 swprintf( nameW, ARRAY_SIZE(nameW), L"%04lx", lcnames_index[i].id );
4700 if (!proc( nameW, param )) break;
4702 else
4704 sprintf( nameA, "%04x", lcnames_index[i].id );
4705 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
4709 return TRUE;
4713 /******************************************************************************
4714 * CompareStringEx (kernelbase.@)
4716 INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1,
4717 const WCHAR *str2, int len2, NLSVERSIONINFO *version,
4718 void *reserved, LPARAM handle )
4720 const struct sortguid *sortid;
4721 const DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS |
4722 SORT_STRINGSORT | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH |
4723 NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE |
4724 LINGUISTIC_IGNOREDIACRITIC | SORT_DIGITSASNUMBERS |
4725 0x10000000 | LOCALE_USE_CP_ACP;
4726 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
4727 int ret;
4729 if (version) FIXME( "unexpected version parameter\n" );
4730 if (reserved) FIXME( "unexpected reserved value\n" );
4731 if (handle) FIXME( "unexpected handle\n" );
4733 if (flags & ~supported_flags)
4735 SetLastError( ERROR_INVALID_FLAGS );
4736 return 0;
4739 if (!(sortid = get_language_sort( locale ))) return 0;
4741 if (!str1 || !str2)
4743 SetLastError( ERROR_INVALID_PARAMETER );
4744 return 0;
4747 if (len1 < 0) len1 = lstrlenW(str1);
4748 if (len2 < 0) len2 = lstrlenW(str2);
4750 ret = compare_string( sortid, flags, str1, len1, str2, len2 );
4751 if (ret < 0) return CSTR_LESS_THAN;
4752 if (ret > 0) return CSTR_GREATER_THAN;
4753 return CSTR_EQUAL;
4757 /******************************************************************************
4758 * CompareStringA (kernelbase.@)
4760 INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1,
4761 const char *str2, int len2 )
4763 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
4764 WCHAR *buf2W = buf1W + 130;
4765 LPWSTR str1W, str2W;
4766 INT len1W = 0, len2W = 0, ret;
4767 UINT locale_cp = CP_ACP;
4769 if (!str1 || !str2)
4771 SetLastError( ERROR_INVALID_PARAMETER );
4772 return 0;
4775 if (flags & SORT_DIGITSASNUMBERS)
4777 SetLastError( ERROR_INVALID_FLAGS );
4778 return 0;
4781 if (len1 < 0) len1 = strlen(str1);
4782 if (len2 < 0) len2 = strlen(str2);
4784 locale_cp = get_lcid_codepage( lcid, flags );
4785 if (len1)
4787 if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 );
4788 if (len1W) str1W = buf1W;
4789 else
4791 len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 );
4792 str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) );
4793 if (!str1W)
4795 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4796 return 0;
4798 MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W );
4801 else
4803 len1W = 0;
4804 str1W = buf1W;
4807 if (len2)
4809 if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 );
4810 if (len2W) str2W = buf2W;
4811 else
4813 len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 );
4814 str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) );
4815 if (!str2W)
4817 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
4818 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4819 return 0;
4821 MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W );
4824 else
4826 len2W = 0;
4827 str2W = buf2W;
4830 ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W );
4832 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
4833 if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W );
4834 return ret;
4838 /******************************************************************************
4839 * CompareStringW (kernelbase.@)
4841 INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1,
4842 const WCHAR *str2, int len2 )
4844 const WCHAR *locale = LOCALE_NAME_USER_DEFAULT;
4845 const NLS_LOCALE_LCID_INDEX *entry;
4847 switch (lcid)
4849 case LOCALE_NEUTRAL:
4850 case LOCALE_USER_DEFAULT:
4851 case LOCALE_SYSTEM_DEFAULT:
4852 case LOCALE_CUSTOM_DEFAULT:
4853 case LOCALE_CUSTOM_UNSPECIFIED:
4854 case LOCALE_CUSTOM_UI_DEFAULT:
4855 break;
4856 default:
4857 if (lcid == user_lcid || lcid == system_lcid) break;
4858 if (!(entry = find_lcid_entry( lcid )))
4860 WARN( "unknown locale %04lx\n", lcid );
4861 SetLastError( ERROR_INVALID_PARAMETER );
4862 return 0;
4864 locale = locale_strings + entry->name + 1;
4865 break;
4868 return CompareStringEx( locale, flags, str1, len1, str2, len2, NULL, NULL, 0 );
4872 /******************************************************************************
4873 * CompareStringOrdinal (kernelbase.@)
4875 INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
4876 const WCHAR *str2, INT len2, BOOL ignore_case )
4878 int ret;
4880 if (!str1 || !str2)
4882 SetLastError( ERROR_INVALID_PARAMETER );
4883 return 0;
4885 if (len1 < 0) len1 = lstrlenW( str1 );
4886 if (len2 < 0) len2 = lstrlenW( str2 );
4888 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4889 if (ret < 0) return CSTR_LESS_THAN;
4890 if (ret > 0) return CSTR_GREATER_THAN;
4891 return CSTR_EQUAL;
4895 /******************************************************************************
4896 * ConvertDefaultLocale (kernelbase.@)
4898 LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
4900 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
4901 if (locale) lcid = locale->ilanguage;
4902 return lcid;
4906 /******************************************************************************
4907 * EnumCalendarInfoW (kernelbase.@)
4909 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
4910 CALID id, CALTYPE type )
4912 return Internal_EnumCalendarInfo( proc, NlsValidateLocale( &lcid, 0 ),
4913 id, type, TRUE, FALSE, FALSE, 0 );
4917 /******************************************************************************
4918 * EnumCalendarInfoExW (kernelbase.@)
4920 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
4921 CALID id, CALTYPE type )
4923 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4924 id, type, TRUE, TRUE, FALSE, 0 );
4927 /******************************************************************************
4928 * EnumCalendarInfoExEx (kernelbase.@)
4930 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
4931 LPCWSTR reserved, CALTYPE type, LPARAM lparam )
4933 LCID lcid;
4934 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4935 id, type, TRUE, TRUE, TRUE, lparam );
4939 /**************************************************************************
4940 * EnumDateFormatsW (kernelbase.@)
4942 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4944 return Internal_EnumDateFormats( proc, NlsValidateLocale( &lcid, 0 ),
4945 flags, TRUE, FALSE, FALSE, 0 );
4949 /**************************************************************************
4950 * EnumDateFormatsExW (kernelbase.@)
4952 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
4954 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4955 flags, TRUE, TRUE, FALSE, 0 );
4959 /**************************************************************************
4960 * EnumDateFormatsExEx (kernelbase.@)
4962 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
4963 DWORD flags, LPARAM lparam )
4965 LCID lcid;
4966 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4967 flags, TRUE, TRUE, TRUE, lparam );
4972 /******************************************************************************
4973 * EnumDynamicTimeZoneInformation (kernelbase.@)
4975 DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index,
4976 DYNAMIC_TIME_ZONE_INFORMATION *info )
4978 DYNAMIC_TIME_ZONE_INFORMATION tz;
4979 LSTATUS ret;
4980 DWORD size;
4982 if (!info) return ERROR_INVALID_PARAMETER;
4984 size = ARRAY_SIZE(tz.TimeZoneKeyName);
4985 ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL );
4986 if (ret) return ret;
4988 tz.DynamicDaylightTimeDisabled = TRUE;
4989 if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError();
4991 lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName );
4992 info->DynamicDaylightTimeDisabled = FALSE;
4993 return 0;
4997 /******************************************************************************
4998 * EnumLanguageGroupLocalesW (kernelbase.@)
5000 BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
5001 DWORD flags, LONG_PTR param )
5003 return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
5007 /******************************************************************************
5008 * EnumUILanguagesW (kernelbase.@)
5010 BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
5012 return Internal_EnumUILanguages( proc, flags, param, TRUE );
5016 /***********************************************************************
5017 * EnumSystemCodePagesW (kernelbase.@)
5019 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
5021 return Internal_EnumSystemCodePages( proc, flags, TRUE );
5025 /******************************************************************************
5026 * EnumSystemGeoID (kernelbase.@)
5028 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc )
5030 INT i;
5032 TRACE( "(%ld, %ld, %p)\n", class, parent, proc );
5034 if (!proc)
5036 SetLastError( ERROR_INVALID_PARAMETER );
5037 return FALSE;
5039 if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL)
5041 SetLastError( ERROR_INVALID_FLAGS );
5042 return FALSE;
5045 for (i = 0; i < geo_ids_count; i++)
5047 if (class != GEOCLASS_ALL && geo_ids[i].class != class) continue;
5048 if (parent && geo_ids[i].parent != parent) continue;
5049 if (!proc( geo_ids[i].id )) break;
5051 return TRUE;
5055 /******************************************************************************
5056 * EnumSystemLanguageGroupsW (kernelbase.@)
5058 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
5059 DWORD flags, LONG_PTR param )
5061 return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
5065 /******************************************************************************
5066 * EnumSystemLocalesA (kernelbase.@)
5068 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
5070 char name[10];
5071 DWORD i;
5073 for (i = 0; i < locale_table->nb_lcnames; i++)
5075 if (!lcnames_index[i].name) continue; /* skip invariant locale */
5076 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
5077 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
5078 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
5079 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
5080 continue; /* skip alternate sorts */
5081 sprintf( name, "%08x", lcnames_index[i].id );
5082 if (!proc( name )) break;
5084 return TRUE;
5088 /******************************************************************************
5089 * EnumSystemLocalesW (kernelbase.@)
5091 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
5093 WCHAR name[10];
5094 DWORD i;
5096 for (i = 0; i < locale_table->nb_lcnames; i++)
5098 if (!lcnames_index[i].name) continue; /* skip invariant locale */
5099 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
5100 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
5101 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
5102 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
5103 continue; /* skip alternate sorts */
5104 swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id );
5105 if (!proc( name )) break;
5107 return TRUE;
5111 /******************************************************************************
5112 * EnumSystemLocalesEx (kernelbase.@)
5114 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
5115 LPARAM param, void *reserved )
5117 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
5118 DWORD i, flags;
5120 if (reserved)
5122 SetLastError( ERROR_INVALID_PARAMETER );
5123 return FALSE;
5126 for (i = 0; i < locale_table->nb_lcnames; i++)
5128 const NLS_LOCALE_DATA *locale = get_locale_data( lcnames_index[i].idx );
5129 const WCHAR *str = locale_strings + lcnames_index[i].name;
5131 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
5132 memcpy( buffer, str + 1, (*str + 1) * sizeof(WCHAR) );
5133 if (SORTIDFROMLCID( lcnames_index[i].id ) || wcschr( str + 1, '_' ))
5134 flags = LOCALE_ALTERNATE_SORTS;
5135 else
5136 flags = LOCALE_WINDOWS | (locale->inotneutral ? LOCALE_SPECIFICDATA : LOCALE_NEUTRALDATA);
5137 if (wanted_flags && !(flags & wanted_flags)) continue;
5138 if (!proc( buffer, flags, param )) break;
5140 return TRUE;
5144 /**************************************************************************
5145 * EnumTimeFormatsW (kernelbase.@)
5147 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
5149 return Internal_EnumTimeFormats( proc, NlsValidateLocale( &lcid, 0 ), flags, TRUE, FALSE, 0 );
5153 /**************************************************************************
5154 * EnumTimeFormatsEx (kernelbase.@)
5156 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
5157 DWORD flags, LPARAM lparam )
5159 LCID lcid;
5160 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
5161 flags, TRUE, TRUE, lparam );
5165 /**************************************************************************
5166 * FindNLSString (kernelbase.@)
5168 INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src,
5169 int srclen, const WCHAR *value, int valuelen, int *found )
5171 const WCHAR *locale = LOCALE_NAME_USER_DEFAULT;
5172 const NLS_LOCALE_LCID_INDEX *entry;
5174 switch (lcid)
5176 case LOCALE_NEUTRAL:
5177 case LOCALE_USER_DEFAULT:
5178 case LOCALE_SYSTEM_DEFAULT:
5179 case LOCALE_CUSTOM_DEFAULT:
5180 case LOCALE_CUSTOM_UNSPECIFIED:
5181 case LOCALE_CUSTOM_UI_DEFAULT:
5182 break;
5183 default:
5184 if (lcid == user_lcid || lcid == system_lcid) break;
5185 if (!(entry = find_lcid_entry( lcid )))
5187 WARN( "unknown locale %04lx\n", lcid );
5188 SetLastError( ERROR_INVALID_PARAMETER );
5189 return 0;
5191 locale = locale_strings + entry->name + 1;
5192 break;
5195 return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 );
5199 /**************************************************************************
5200 * FindNLSStringEx (kernelbase.@)
5202 INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src,
5203 int srclen, const WCHAR *value, int valuelen, int *found,
5204 NLSVERSIONINFO *version, void *reserved, LPARAM handle )
5206 const struct sortguid *sortid;
5208 TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale), flags,
5209 wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found,
5210 version, reserved, handle );
5212 if (version) FIXME( "unexpected version parameter\n" );
5213 if (reserved) FIXME( "unexpected reserved value\n" );
5214 if (handle) FIXME( "unexpected handle\n" );
5216 if (!src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1)
5218 SetLastError( ERROR_INVALID_PARAMETER );
5219 return -1;
5221 if (!(sortid = get_language_sort( locale ))) return -1;
5223 if (srclen == -1) srclen = lstrlenW(src);
5224 if (valuelen == -1) valuelen = lstrlenW(value);
5226 return find_substring( sortid, flags, src, srclen, value, valuelen, found );
5230 /******************************************************************************
5231 * FindStringOrdinal (kernelbase.@)
5233 INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
5234 const WCHAR *val, INT val_size, BOOL ignore_case )
5236 INT offset, inc, count;
5238 TRACE( "%#lx %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
5239 wine_dbgstr_w(val), val_size, ignore_case );
5241 if (!src || !val)
5243 SetLastError( ERROR_INVALID_PARAMETER );
5244 return -1;
5247 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
5249 SetLastError( ERROR_INVALID_FLAGS );
5250 return -1;
5253 if (src_size == -1) src_size = lstrlenW( src );
5254 if (val_size == -1) val_size = lstrlenW( val );
5256 SetLastError( ERROR_SUCCESS );
5257 src_size -= val_size;
5258 if (src_size < 0) return -1;
5260 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
5261 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
5262 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
5263 while (count--)
5265 if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
5266 return offset;
5267 offset += inc;
5269 return -1;
5273 /******************************************************************************
5274 * FoldStringW (kernelbase.@)
5276 INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
5278 NTSTATUS status;
5279 WCHAR *buf = dst;
5280 int len = dstlen;
5282 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
5284 SetLastError( ERROR_INVALID_PARAMETER );
5285 return 0;
5287 if (srclen == -1) srclen = lstrlenW(src) + 1;
5289 if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE)))
5291 len = srclen * 4;
5292 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
5294 SetLastError( ERROR_OUTOFMEMORY );
5295 return 0;
5299 for (;;)
5301 status = fold_string( flags, src, srclen, buf, &len );
5302 if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf );
5303 if (status != STATUS_BUFFER_TOO_SMALL) break;
5304 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
5306 SetLastError( ERROR_OUTOFMEMORY );
5307 return 0;
5310 if (status == STATUS_INVALID_PARAMETER_1)
5312 SetLastError( ERROR_INVALID_FLAGS );
5313 return 0;
5315 if (!set_ntstatus( status )) return 0;
5317 if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
5318 return len;
5322 static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang,
5323 BOOL ansi, WCHAR **buffer )
5325 DWORD len;
5327 if (!(flags & FORMAT_MESSAGE_FROM_STRING))
5329 const MESSAGE_RESOURCE_ENTRY *entry;
5330 NTSTATUS status = STATUS_INVALID_PARAMETER;
5332 if (flags & FORMAT_MESSAGE_FROM_HMODULE)
5334 HMODULE module = (HMODULE)src;
5335 if (!module) module = GetModuleHandleW( 0 );
5336 status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry );
5338 if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
5340 /* Fold win32 hresult to its embedded error code. */
5341 if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32)
5342 id = HRESULT_CODE( id );
5343 status = RtlFindMessage( kernelbase_handle, RT_MESSAGETABLE, lang, id, &entry );
5345 if (!set_ntstatus( status )) return NULL;
5347 src = entry->Text;
5348 ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE);
5351 if (!ansi) return src;
5352 len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
5353 if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
5354 MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len );
5355 return *buffer;
5359 /***********************************************************************
5360 * FormatMessageA (kernelbase.@)
5362 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid,
5363 char *buffer, DWORD size, va_list *args )
5365 DWORD ret = 0;
5366 ULONG len, retsize = 0;
5367 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
5368 const WCHAR *src;
5369 WCHAR *result, *message = NULL;
5370 NTSTATUS status;
5372 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
5374 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
5376 if (!buffer)
5378 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5379 return 0;
5381 *(char **)buffer = NULL;
5383 if (size >= 32768)
5385 SetLastError( ERROR_INVALID_PARAMETER );
5386 return 0;
5389 if (width == 0xff) width = ~0u;
5391 if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0;
5393 if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 )))
5394 status = STATUS_NO_MEMORY;
5395 else
5396 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5397 TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
5398 result, 65536, &retsize );
5400 HeapFree( GetProcessHeap(), 0, message );
5402 if (status == STATUS_BUFFER_OVERFLOW)
5404 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5405 goto done;
5407 if (!set_ntstatus( status )) goto done;
5409 len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL );
5410 if (len <= 1)
5412 SetLastError( ERROR_NO_WORK_DONE );
5413 goto done;
5416 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
5418 char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len ));
5419 if (!buf)
5421 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5422 goto done;
5424 *(char **)buffer = buf;
5425 WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL );
5427 else if (len > size)
5429 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5430 goto done;
5432 else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL );
5434 ret = len - 1;
5436 done:
5437 HeapFree( GetProcessHeap(), 0, result );
5438 return ret;
5442 /***********************************************************************
5443 * FormatMessageW (kernelbase.@)
5445 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid,
5446 WCHAR *buffer, DWORD size, va_list *args )
5448 ULONG retsize = 0;
5449 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
5450 const WCHAR *src;
5451 WCHAR *message = NULL;
5452 NTSTATUS status;
5454 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
5456 if (!buffer)
5458 SetLastError( ERROR_INVALID_PARAMETER );
5459 return 0;
5462 if (width == 0xff) width = ~0u;
5464 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL;
5466 if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0;
5468 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
5470 WCHAR *result;
5471 va_list args_copy;
5472 ULONG alloc = max( size * sizeof(WCHAR), 65536 );
5474 for (;;)
5476 if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc )))
5478 status = STATUS_NO_MEMORY;
5479 break;
5481 if (args && !(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY))
5483 va_copy( args_copy, *args );
5484 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5485 FALSE, FALSE, &args_copy, result, alloc, &retsize );
5486 va_end( args_copy );
5488 else
5489 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5490 FALSE, TRUE, args, result, alloc, &retsize );
5492 if (!status)
5494 if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );
5495 else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
5496 result, max( retsize, size * sizeof(WCHAR) ));
5497 break;
5499 HeapFree( GetProcessHeap(), 0, result );
5500 if (status != STATUS_BUFFER_OVERFLOW) break;
5501 alloc *= 2;
5504 else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5505 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
5506 buffer, size * sizeof(WCHAR), &retsize );
5508 HeapFree( GetProcessHeap(), 0, message );
5510 if (status == STATUS_BUFFER_OVERFLOW)
5512 if (size) buffer[size - 1] = 0;
5513 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5514 return 0;
5516 if (!set_ntstatus( status )) return 0;
5517 if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE );
5518 return retsize / sizeof(WCHAR) - 1;
5522 /******************************************************************************
5523 * GetACP (kernelbase.@)
5525 UINT WINAPI GetACP(void)
5527 return ansi_cpinfo.CodePage;
5531 /***********************************************************************
5532 * GetCPInfo (kernelbase.@)
5534 BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
5536 const CPTABLEINFO *table;
5538 if (!cpinfo || !(table = get_codepage_table( codepage )))
5540 SetLastError( ERROR_INVALID_PARAMETER );
5541 return FALSE;
5543 cpinfo->MaxCharSize = table->MaximumCharacterSize;
5544 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
5545 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
5546 return TRUE;
5550 /***********************************************************************
5551 * GetCPInfoExW (kernelbase.@)
5553 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
5555 const CPTABLEINFO *table;
5556 int min, max, pos;
5558 if (!cpinfo || !(table = get_codepage_table( codepage )))
5560 SetLastError( ERROR_INVALID_PARAMETER );
5561 return FALSE;
5563 cpinfo->MaxCharSize = table->MaximumCharacterSize;
5564 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
5565 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
5566 cpinfo->CodePage = table->CodePage;
5567 cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
5569 min = 0;
5570 max = ARRAY_SIZE(codepage_names) - 1;
5571 cpinfo->CodePageName[0] = 0;
5572 while (min <= max)
5574 pos = (min + max) / 2;
5575 if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
5576 else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
5577 else
5579 wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
5580 break;
5583 return TRUE;
5587 /***********************************************************************
5588 * GetCalendarInfoW (kernelbase.@)
5590 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
5591 WCHAR *buffer, INT len, DWORD *value )
5593 const NLS_LOCALE_DATA *locale;
5595 TRACE( "%04lx %lu 0x%lx %p %d %p\n", lcid, calendar, type, buffer, len, value );
5597 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5599 SetLastError( ERROR_INVALID_PARAMETER );
5600 return 0;
5602 return get_calendar_info( locale, calendar, type, buffer, len, value );
5606 /***********************************************************************
5607 * GetCalendarInfoEx (kernelbase.@)
5609 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *name, CALID calendar, const WCHAR *reserved,
5610 CALTYPE type, WCHAR *buffer, INT len, DWORD *value )
5612 LCID lcid;
5613 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5615 TRACE( "%s %lu 0x%lx %p %d %p\n", debugstr_w(name), calendar, type, buffer, len, value );
5617 if (!locale)
5619 SetLastError( ERROR_INVALID_PARAMETER );
5620 return 0;
5622 return get_calendar_info( locale, calendar, type, buffer, len, value );
5626 static CRITICAL_SECTION tzname_section;
5627 static CRITICAL_SECTION_DEBUG tzname_section_debug =
5629 0, 0, &tzname_section,
5630 { &tzname_section_debug.ProcessLocksList, &tzname_section_debug.ProcessLocksList },
5631 0, 0, { (DWORD_PTR)(__FILE__ ": tzname_section") }
5633 static CRITICAL_SECTION tzname_section = { &tzname_section_debug, -1, 0, 0, 0, 0 };
5634 static struct {
5635 LCID lcid;
5636 WCHAR key_name[128];
5637 WCHAR standard_name[32];
5638 WCHAR daylight_name[32];
5639 } cached_tzname;
5641 /***********************************************************************
5642 * GetDynamicTimeZoneInformation (kernelbase.@)
5644 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info )
5646 HKEY key;
5647 LARGE_INTEGER now;
5649 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info )))
5650 return TIME_ZONE_ID_INVALID;
5652 RtlEnterCriticalSection( &tzname_section );
5653 if (cached_tzname.lcid == GetThreadLocale() &&
5654 !wcscmp( info->TimeZoneKeyName, cached_tzname.key_name ))
5656 wcscpy( info->StandardName, cached_tzname.standard_name );
5657 wcscpy( info->DaylightName, cached_tzname.daylight_name );
5658 RtlLeaveCriticalSection( &tzname_section );
5660 else
5662 RtlLeaveCriticalSection( &tzname_section );
5663 if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))
5665 RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
5666 sizeof(info->StandardName), NULL, 0, system_dir );
5667 RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
5668 sizeof(info->DaylightName), NULL, 0, system_dir );
5669 RegCloseKey( key );
5671 else return TIME_ZONE_ID_INVALID;
5673 RtlEnterCriticalSection( &tzname_section );
5674 cached_tzname.lcid = GetThreadLocale();
5675 wcscpy( cached_tzname.key_name, info->TimeZoneKeyName );
5676 wcscpy( cached_tzname.standard_name, info->StandardName );
5677 wcscpy( cached_tzname.daylight_name, info->DaylightName );
5678 RtlLeaveCriticalSection( &tzname_section );
5681 NtQuerySystemTime( &now );
5682 return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE );
5686 /******************************************************************************
5687 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
5689 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info,
5690 DWORD *first, DWORD *last )
5692 HKEY key, dst_key = 0;
5693 DWORD type, count, ret = ERROR_FILE_NOT_FOUND;
5695 if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret;
5697 if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done;
5698 count = sizeof(DWORD);
5699 if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done;
5700 if (type != REG_DWORD) goto done;
5701 count = sizeof(DWORD);
5702 if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done;
5703 if (type != REG_DWORD) goto done;
5704 ret = 0;
5706 done:
5707 RegCloseKey( dst_key );
5708 RegCloseKey( key );
5709 return ret;
5713 /******************************************************************************
5714 * GetFileMUIInfo (kernelbase.@)
5716 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIInfo( DWORD flags, const WCHAR *path,
5717 FILEMUIINFO *info, DWORD *size )
5719 FIXME( "stub: %lu, %s, %p, %p\n", flags, debugstr_w(path), info, size );
5720 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
5721 return FALSE;
5725 /******************************************************************************
5726 * GetFileMUIPath (kernelbase.@)
5728 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags, const WCHAR *filepath,
5729 WCHAR *language, ULONG *languagelen,
5730 WCHAR *muipath, ULONG *muipathlen,
5731 ULONGLONG *enumerator )
5733 FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5734 debugstr_w(language), languagelen, muipath, muipathlen, enumerator );
5735 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
5736 return FALSE;
5740 /******************************************************************************
5741 * GetGeoInfoW (kernelbase.@)
5743 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang )
5745 const struct geo_id *ptr = find_geo_id_entry( id );
5747 TRACE( "%ld %ld %p %d %d\n", id, type, data, count, lang );
5749 if (!ptr)
5751 SetLastError( ERROR_INVALID_PARAMETER );
5752 return 0;
5754 return get_geo_info( ptr, type, data, count, lang );
5758 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoEx( WCHAR *location, GEOTYPE type, WCHAR *data, int data_count )
5760 const struct geo_id *ptr = find_geo_name_entry( location );
5762 TRACE( "%s %lx %p %d\n", wine_dbgstr_w(location), type, data, data_count );
5764 if (!ptr)
5766 SetLastError( ERROR_INVALID_PARAMETER );
5767 return 0;
5770 if (type == GEO_LCID || type == GEO_NATION || type == GEO_RFC1766)
5772 SetLastError( ERROR_INVALID_FLAGS );
5773 return 0;
5776 return get_geo_info( ptr, type, data, data_count, 0 );
5780 /******************************************************************************
5781 * GetLocaleInfoA (kernelbase.@)
5783 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
5785 const NLS_LOCALE_DATA *locale;
5786 WCHAR *bufferW;
5787 INT lenW, ret;
5789 TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid, lctype, buffer, len );
5791 if (len < 0 || (len && !buffer))
5793 SetLastError( ERROR_INVALID_PARAMETER );
5794 return 0;
5796 if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
5798 SetLastError( ERROR_INVALID_FLAGS );
5799 return 0;
5801 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5803 SetLastError( ERROR_INVALID_PARAMETER );
5804 return 0;
5806 if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
5808 ret = get_locale_info( locale, lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) );
5809 return ret * sizeof(WCHAR);
5812 if (!(lenW = get_locale_info( locale, lcid, lctype, NULL, 0 ))) return 0;
5814 if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
5816 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5817 return 0;
5819 ret = get_locale_info( locale, lcid, lctype, bufferW, lenW );
5820 if (ret) ret = WideCharToMultiByte( get_locale_codepage( locale, lctype ), 0,
5821 bufferW, ret, buffer, len, NULL, NULL );
5822 RtlFreeHeap( GetProcessHeap(), 0, bufferW );
5823 return ret;
5827 /******************************************************************************
5828 * GetLocaleInfoW (kernelbase.@)
5830 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
5832 const NLS_LOCALE_DATA *locale;
5834 if (len < 0 || (len && !buffer))
5836 SetLastError( ERROR_INVALID_PARAMETER );
5837 return 0;
5840 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
5842 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5844 SetLastError( ERROR_INVALID_PARAMETER );
5845 return 0;
5847 return get_locale_info( locale, lcid, lctype, buffer, len );
5851 /******************************************************************************
5852 * GetLocaleInfoEx (kernelbase.@)
5854 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *name, LCTYPE info, WCHAR *buffer, INT len )
5856 LCID lcid;
5857 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5859 TRACE( "%s 0x%lx %p %d\n", debugstr_w(name), info, buffer, len );
5861 if (!locale)
5863 SetLastError( ERROR_INVALID_PARAMETER );
5864 return 0;
5866 return get_locale_info( locale, lcid, info, buffer, len );
5870 /******************************************************************************
5871 * GetNLSVersion (kernelbase.@)
5873 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info )
5875 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
5877 if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId ))
5879 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5880 return FALSE;
5882 if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES ))
5884 SetLastError( ERROR_INVALID_PARAMETER );
5885 return FALSE;
5887 return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info );
5891 /******************************************************************************
5892 * GetNLSVersionEx (kernelbase.@)
5894 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale,
5895 NLSVERSIONINFOEX *info )
5897 const struct sortguid *sortid;
5899 if (func != COMPARE_STRING)
5901 SetLastError( ERROR_INVALID_FLAGS );
5902 return FALSE;
5904 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5905 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5907 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5908 return FALSE;
5911 if (!(sortid = get_language_sort( locale ))) return FALSE;
5913 info->dwNLSVersion = info->dwDefinedVersion = sort.version;
5914 if (info->dwNLSVersionInfoSize >= sizeof(*info))
5916 info->dwEffectiveId = LocaleNameToLCID( locale, 0 );
5917 info->guidCustomVersion = sortid->id;
5919 return TRUE;
5923 /******************************************************************************
5924 * GetOEMCP (kernelbase.@)
5926 UINT WINAPI GetOEMCP(void)
5928 return oem_cpinfo.CodePage;
5932 /***********************************************************************
5933 * GetProcessPreferredUILanguages (kernelbase.@)
5935 BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPreferredUILanguages( DWORD flags, ULONG *count,
5936 WCHAR *buffer, ULONG *size )
5938 return set_ntstatus( RtlGetProcessPreferredUILanguages( flags, count, buffer, size ));
5942 /***********************************************************************
5943 * GetStringTypeA (kernelbase.@)
5945 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
5946 WORD *chartype )
5948 UINT cp;
5949 INT countW;
5950 LPWSTR srcW;
5951 BOOL ret = FALSE;
5953 if (count == -1) count = strlen(src) + 1;
5955 cp = get_lcid_codepage( locale, 0 );
5956 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
5957 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5959 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
5961 * NOTE: the target buffer has 1 word for each CHARACTER in the source
5962 * string, with multibyte characters there maybe be more bytes in count
5963 * than character space in the buffer!
5965 ret = GetStringTypeW(type, srcW, countW, chartype);
5966 HeapFree(GetProcessHeap(), 0, srcW);
5968 return ret;
5972 /***********************************************************************
5973 * GetStringTypeW (kernelbase.@)
5975 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
5977 if (!src)
5979 SetLastError( ERROR_INVALID_PARAMETER );
5980 return FALSE;
5982 if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3)
5984 SetLastError( ERROR_INVALID_PARAMETER );
5985 return FALSE;
5988 if (count == -1) count = lstrlenW(src) + 1;
5990 while (count--) *chartype++ = get_char_type( type, *src++ );
5992 return TRUE;
5996 /***********************************************************************
5997 * GetStringTypeExW (kernelbase.@)
5999 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
6000 WORD *chartype )
6002 /* locale is ignored for Unicode */
6003 return GetStringTypeW( type, src, count, chartype );
6007 /***********************************************************************
6008 * GetSystemDefaultLCID (kernelbase.@)
6010 LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
6012 return system_lcid;
6016 /***********************************************************************
6017 * GetSystemDefaultLangID (kernelbase.@)
6019 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
6021 return LANGIDFROMLCID( GetSystemDefaultLCID() );
6025 /***********************************************************************
6026 * GetSystemDefaultLocaleName (kernelbase.@)
6028 INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT count )
6030 return get_locale_info( system_locale, system_lcid, LOCALE_SNAME, name, count );
6034 /***********************************************************************
6035 * GetSystemDefaultUILanguage (kernelbase.@)
6037 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
6039 LANGID lang;
6040 NtQueryInstallUILanguage( &lang );
6041 return lang;
6045 /***********************************************************************
6046 * GetSystemPreferredUILanguages (kernelbase.@)
6048 BOOL WINAPI DECLSPEC_HOTPATCH GetSystemPreferredUILanguages( DWORD flags, ULONG *count,
6049 WCHAR *buffer, ULONG *size )
6051 return set_ntstatus( RtlGetSystemPreferredUILanguages( flags, 0, count, buffer, size ));
6055 /***********************************************************************
6056 * GetThreadPreferredUILanguages (kernelbase.@)
6058 BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPreferredUILanguages( DWORD flags, ULONG *count,
6059 WCHAR *buffer, ULONG *size )
6061 return set_ntstatus( RtlGetThreadPreferredUILanguages( flags, count, buffer, size ));
6065 /***********************************************************************
6066 * GetTimeZoneInformation (kernelbase.@)
6068 DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info )
6070 DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
6071 DWORD ret = GetDynamicTimeZoneInformation( &tzinfo );
6073 memcpy( info, &tzinfo, sizeof(*info) );
6074 return ret;
6078 /***********************************************************************
6079 * GetTimeZoneInformationForYear (kernelbase.@)
6081 BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year,
6082 DYNAMIC_TIME_ZONE_INFORMATION *dynamic,
6083 TIME_ZONE_INFORMATION *info )
6085 DYNAMIC_TIME_ZONE_INFORMATION local_info;
6086 HKEY key = 0, dst_key;
6087 DWORD count;
6088 LRESULT ret;
6089 struct
6091 LONG bias;
6092 LONG std_bias;
6093 LONG dlt_bias;
6094 SYSTEMTIME std_date;
6095 SYSTEMTIME dlt_date;
6096 } data;
6098 TRACE( "(%u,%p)\n", year, info );
6100 if (!dynamic)
6102 if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE;
6103 dynamic = &local_info;
6106 if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done;
6107 if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
6108 sizeof(info->StandardName), NULL, 0, system_dir ))
6110 count = sizeof(info->StandardName);
6111 if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count )))
6112 goto done;
6114 if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
6115 sizeof(info->DaylightName), NULL, 0, system_dir ))
6117 count = sizeof(info->DaylightName);
6118 if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count )))
6119 goto done;
6122 ret = ERROR_FILE_NOT_FOUND;
6123 if (!dynamic->DynamicDaylightTimeDisabled &&
6124 !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key ))
6126 WCHAR yearW[16];
6127 swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year );
6128 count = sizeof(data);
6129 ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count );
6130 RegCloseKey( dst_key );
6132 if (ret)
6134 count = sizeof(data);
6135 ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count );
6138 if (!ret)
6140 info->Bias = data.bias;
6141 info->StandardBias = data.std_bias;
6142 info->DaylightBias = data.dlt_bias;
6143 info->StandardDate = data.std_date;
6144 info->DaylightDate = data.dlt_date;
6147 done:
6148 RegCloseKey( key );
6149 if (ret) SetLastError( ret );
6150 return !ret;
6154 /***********************************************************************
6155 * GetUserDefaultLCID (kernelbase.@)
6157 LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
6159 return user_lcid;
6163 /***********************************************************************
6164 * GetUserDefaultLangID (kernelbase.@)
6166 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
6168 return LANGIDFROMLCID( GetUserDefaultLCID() );
6172 /***********************************************************************
6173 * GetUserDefaultLocaleName (kernelbase.@)
6175 INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
6177 return get_locale_info( user_locale, user_lcid, LOCALE_SNAME, name, len );
6181 /***********************************************************************
6182 * GetUserDefaultUILanguage (kernelbase.@)
6184 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
6186 return LANGIDFROMLCID( GetUserDefaultLCID() );
6190 /******************************************************************************
6191 * GetUserGeoID (kernelbase.@)
6193 GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass )
6195 GEOID ret = 39070;
6196 const WCHAR *name;
6197 WCHAR bufferW[40];
6198 HKEY hkey;
6200 switch (geoclass)
6202 case GEOCLASS_NATION:
6203 name = L"Nation";
6204 break;
6205 case GEOCLASS_REGION:
6206 name = L"Region";
6207 break;
6208 default:
6209 WARN("Unknown geoclass %ld\n", geoclass);
6210 return GEOID_NOT_AVAILABLE;
6212 if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey ))
6214 DWORD count = sizeof(bufferW);
6215 if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count ))
6216 ret = wcstol( bufferW, NULL, 10 );
6217 RegCloseKey( hkey );
6219 return ret;
6223 /******************************************************************************
6224 * GetUserPreferredUILanguages (kernelbase.@)
6226 BOOL WINAPI DECLSPEC_HOTPATCH GetUserPreferredUILanguages( DWORD flags, ULONG *count,
6227 WCHAR *buffer, ULONG *size )
6229 return set_ntstatus( RtlGetUserPreferredUILanguages( flags, 0, count, buffer, size ));
6233 /******************************************************************************
6234 * IdnToAscii (kernelbase.@)
6236 INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen,
6237 WCHAR *dst, INT dstlen )
6239 NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
6240 if (!set_ntstatus( status )) return 0;
6241 return dstlen;
6245 /******************************************************************************
6246 * IdnToNameprepUnicode (kernelbase.@)
6248 INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
6249 WCHAR *dst, INT dstlen )
6251 NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen );
6252 if (!set_ntstatus( status )) return 0;
6253 return dstlen;
6257 /******************************************************************************
6258 * IdnToUnicode (kernelbase.@)
6260 INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen,
6261 WCHAR *dst, INT dstlen )
6263 NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen );
6264 if (!set_ntstatus( status )) return 0;
6265 return dstlen;
6269 /******************************************************************************
6270 * IsCharAlphaA (kernelbase.@)
6272 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
6274 WCHAR wc;
6275 DWORD reslen;
6276 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6277 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
6281 /******************************************************************************
6282 * IsCharAlphaW (kernelbase.@)
6284 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
6286 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
6290 /******************************************************************************
6291 * IsCharAlphaNumericA (kernelbase.@)
6293 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
6295 WCHAR wc;
6296 DWORD reslen;
6297 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6298 return reslen && (get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
6302 /******************************************************************************
6303 * IsCharAlphaNumericW (kernelbase.@)
6305 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
6307 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
6311 /******************************************************************************
6312 * IsCharBlankW (kernelbase.@)
6314 BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
6316 return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK);
6320 /******************************************************************************
6321 * IsCharCntrlW (kernelbase.@)
6323 BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
6325 return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL);
6329 /******************************************************************************
6330 * IsCharDigitW (kernelbase.@)
6332 BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
6334 return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT);
6338 /******************************************************************************
6339 * IsCharLowerA (kernelbase.@)
6341 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
6343 WCHAR wc;
6344 DWORD reslen;
6345 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6346 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
6350 /******************************************************************************
6351 * IsCharLowerW (kernelbase.@)
6353 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
6355 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
6359 /******************************************************************************
6360 * IsCharPunctW (kernelbase.@)
6362 BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
6364 return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT);
6368 /******************************************************************************
6369 * IsCharSpaceA (kernelbase.@)
6371 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
6373 WCHAR wc;
6374 DWORD reslen;
6375 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6376 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
6380 /******************************************************************************
6381 * IsCharSpaceW (kernelbase.@)
6383 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
6385 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
6389 /******************************************************************************
6390 * IsCharUpperA (kernelbase.@)
6392 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
6394 WCHAR wc;
6395 DWORD reslen;
6396 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6397 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
6401 /******************************************************************************
6402 * IsCharUpperW (kernelbase.@)
6404 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
6406 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
6410 /******************************************************************************
6411 * IsCharXDigitW (kernelbase.@)
6413 BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
6415 return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT);
6419 /******************************************************************************
6420 * IsDBCSLeadByte (kernelbase.@)
6422 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
6424 return ansi_cpinfo.DBCSCodePage && ansi_cpinfo.DBCSOffsets[testchar];
6428 /******************************************************************************
6429 * IsDBCSLeadByteEx (kernelbase.@)
6431 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
6433 const CPTABLEINFO *table = get_codepage_table( codepage );
6434 return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
6438 /******************************************************************************
6439 * IsNormalizedString (kernelbase.@)
6441 BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
6443 BOOLEAN res;
6444 if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
6445 return res;
6449 /******************************************************************************
6450 * IsValidCodePage (kernelbase.@)
6452 BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
6454 switch (codepage)
6456 case CP_ACP:
6457 case CP_OEMCP:
6458 case CP_MACCP:
6459 case CP_THREAD_ACP:
6460 return FALSE;
6461 default:
6462 return get_codepage_table( codepage ) != NULL;
6467 /******************************************************************************
6468 * IsValidLanguageGroup (kernelbase.@)
6470 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
6472 WCHAR name[10], value[10];
6473 DWORD type, value_len = sizeof(value);
6474 BOOL ret = FALSE;
6475 HKEY key;
6477 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
6479 swprintf( name, ARRAY_SIZE(name), L"%x", id );
6480 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
6481 ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
6483 RegCloseKey( key );
6484 return ret;
6488 /******************************************************************************
6489 * IsValidLocale (kernelbase.@)
6491 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
6493 switch (lcid)
6495 case LOCALE_NEUTRAL:
6496 case LOCALE_USER_DEFAULT:
6497 case LOCALE_SYSTEM_DEFAULT:
6498 return FALSE;
6499 default:
6500 return !!NlsValidateLocale( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES );
6505 /******************************************************************************
6506 * IsValidLocaleName (kernelbase.@)
6508 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
6510 if (locale == LOCALE_NAME_USER_DEFAULT) return FALSE;
6511 return !!find_lcname_entry( locale );
6515 /******************************************************************************
6516 * IsNLSDefinedString (kernelbase.@)
6518 BOOL WINAPI DECLSPEC_HOTPATCH IsNLSDefinedString( NLS_FUNCTION func, DWORD flags, NLSVERSIONINFO *info,
6519 const WCHAR *str, int len )
6521 int i;
6523 if (func != COMPARE_STRING)
6525 SetLastError( ERROR_INVALID_FLAGS );
6526 return FALSE;
6528 if (info)
6530 if (info->dwNLSVersionInfoSize != sizeof(*info) &&
6531 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
6533 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6534 return FALSE;
6538 if (len < 0) len = lstrlenW( str ) + 1;
6540 for (i = 0; i < len; i++)
6542 if (is_private_use_area_char( str[i] )) return FALSE;
6543 if (IS_LOW_SURROGATE( str[i] )) return FALSE;
6544 if (IS_HIGH_SURROGATE( str[i] ))
6546 if (++i == len) return FALSE;
6547 if (!IS_LOW_SURROGATE( str[i] )) return FALSE;
6548 continue;
6550 if (!(get_char_type( CT_CTYPE1, str[i] ) & C1_DEFINED)) return FALSE;
6552 return TRUE;
6556 /******************************************************************************
6557 * IsValidNLSVersion (kernelbase.@)
6559 DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale,
6560 NLSVERSIONINFOEX *info )
6562 static const GUID GUID_NULL;
6563 NLSVERSIONINFOEX infoex;
6564 DWORD ret;
6566 if (func != COMPARE_STRING)
6568 SetLastError( ERROR_INVALID_PARAMETER );
6569 return FALSE;
6571 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
6572 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
6574 SetLastError( ERROR_INVALID_PARAMETER );
6575 return FALSE;
6577 infoex.dwNLSVersionInfoSize = sizeof(infoex);
6578 if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE;
6580 ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff);
6581 if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL ))
6582 ret = find_sortguid( &info->guidCustomVersion ) != NULL;
6584 if (!ret) SetLastError( ERROR_SUCCESS );
6585 return ret;
6589 /***********************************************************************
6590 * LCIDToLocaleName (kernelbase.@)
6592 INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
6594 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, flags );
6596 if (!locale)
6598 SetLastError( ERROR_INVALID_PARAMETER );
6599 return 0;
6601 return get_locale_info( locale, lcid, LOCALE_SNAME, name, count );
6605 /***********************************************************************
6606 * LCMapStringEx (kernelbase.@)
6608 INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
6609 WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
6610 void *reserved, LPARAM handle )
6612 const struct sortguid *sortid = NULL;
6614 if (version) FIXME( "unsupported version structure %p\n", version );
6615 if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
6616 if (handle)
6618 static int once;
6619 if (!once++) FIXME( "unsupported lparam %Ix\n", handle );
6622 if (!src || !srclen || dstlen < 0)
6624 SetLastError( ERROR_INVALID_PARAMETER );
6625 return 0;
6628 if (srclen < 0) srclen = lstrlenW(src) + 1;
6630 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
6631 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
6633 flags &= ~LOCALE_USE_CP_ACP;
6635 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
6637 SetLastError( ERROR_INVALID_FLAGS );
6638 return 0;
6640 if (flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_SORTKEY))
6642 if (!(sortid = get_language_sort( locale ))) return 0;
6644 if (flags & LCMAP_HASH)
6646 FIXME( "LCMAP_HASH %s not supported\n", debugstr_wn( src, srclen ));
6647 return 0;
6649 if (flags & LCMAP_SORTHANDLE)
6651 FIXME( "LCMAP_SORTHANDLE not supported\n" );
6652 return 0;
6654 if (flags & LCMAP_SORTKEY) return get_sortkey( sortid, flags, src, srclen, (BYTE *)dst, dstlen );
6656 return lcmap_string( sortid, flags, src, srclen, dst, dstlen );
6660 /***********************************************************************
6661 * LCMapStringA (kernelbase.@)
6663 INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
6664 char *dst, int dstlen )
6666 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
6667 LPWSTR srcW, dstW;
6668 INT ret = 0, srclenW, dstlenW;
6669 UINT locale_cp = CP_ACP;
6671 if (!src || !srclen || dstlen < 0)
6673 SetLastError( ERROR_INVALID_PARAMETER );
6674 return 0;
6677 locale_cp = get_lcid_codepage( lcid, flags );
6679 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
6680 if (srclenW) srcW = bufW;
6681 else
6683 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
6684 srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
6685 if (!srcW)
6687 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6688 return 0;
6690 MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
6693 if (flags & LCMAP_SORTKEY)
6695 if (src == dst)
6697 SetLastError( ERROR_INVALID_FLAGS );
6698 goto done;
6700 ret = LCMapStringW( lcid, flags, srcW, srclenW, (WCHAR *)dst, dstlen );
6701 goto done;
6704 if (flags & SORT_STRINGSORT)
6706 SetLastError( ERROR_INVALID_FLAGS );
6707 goto done;
6710 dstlenW = LCMapStringW( lcid, flags, srcW, srclenW, NULL, 0 );
6711 if (!dstlenW) goto done;
6713 dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
6714 if (!dstW)
6716 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6717 goto done;
6719 LCMapStringW( lcid, flags, srcW, srclenW, dstW, dstlenW );
6720 ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
6721 HeapFree( GetProcessHeap(), 0, dstW );
6723 done:
6724 if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
6725 return ret;
6729 /***********************************************************************
6730 * LCMapStringW (kernelbase.@)
6732 INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
6733 WCHAR *dst, int dstlen )
6735 const WCHAR *locale = LOCALE_NAME_USER_DEFAULT;
6736 const NLS_LOCALE_LCID_INDEX *entry;
6738 switch (lcid)
6740 case LOCALE_NEUTRAL:
6741 case LOCALE_USER_DEFAULT:
6742 case LOCALE_SYSTEM_DEFAULT:
6743 case LOCALE_CUSTOM_DEFAULT:
6744 case LOCALE_CUSTOM_UNSPECIFIED:
6745 case LOCALE_CUSTOM_UI_DEFAULT:
6746 break;
6747 default:
6748 if (lcid == user_lcid || lcid == system_lcid) break;
6749 if (!(entry = find_lcid_entry( lcid )))
6751 WARN( "unknown locale %04lx\n", lcid );
6752 SetLastError( ERROR_INVALID_PARAMETER );
6753 return 0;
6755 locale = locale_strings + entry->name + 1;
6756 break;
6759 return LCMapStringEx( locale, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
6763 /***********************************************************************
6764 * LocaleNameToLCID (kernelbase.@)
6766 LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
6768 LCID lcid;
6769 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6771 if (!locale)
6773 SetLastError( ERROR_INVALID_PARAMETER );
6774 return 0;
6776 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
6777 lcid = locale->idefaultlanguage;
6778 return lcid;
6782 /******************************************************************************
6783 * MultiByteToWideChar (kernelbase.@)
6785 INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
6786 WCHAR *dst, INT dstlen )
6788 const CPTABLEINFO *info;
6789 int ret;
6791 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6793 SetLastError( ERROR_INVALID_PARAMETER );
6794 return 0;
6796 if (srclen < 0) srclen = strlen(src) + 1;
6798 switch (codepage)
6800 case CP_SYMBOL:
6801 ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
6802 break;
6803 case CP_UTF7:
6804 ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
6805 break;
6806 case CP_UNIXCP:
6807 codepage = unix_cp;
6808 /* fall through */
6809 default:
6810 if (!(info = get_codepage_table( codepage )))
6812 SetLastError( ERROR_INVALID_PARAMETER );
6813 return 0;
6815 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
6817 SetLastError( ERROR_INVALID_FLAGS );
6818 return 0;
6820 if (info->CodePage == CP_UTF8)
6821 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6822 else
6823 ret = mbstowcs_codepage( info, flags, src, srclen, dst, dstlen );
6824 break;
6826 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
6827 return ret;
6831 /******************************************************************************
6832 * NormalizeString (kernelbase.@)
6834 INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
6835 WCHAR *dst, INT dst_len)
6837 NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len );
6839 switch (status)
6841 case STATUS_OBJECT_NAME_NOT_FOUND:
6842 status = STATUS_INVALID_PARAMETER;
6843 break;
6844 case STATUS_BUFFER_TOO_SMALL:
6845 case STATUS_NO_UNICODE_TRANSLATION:
6846 dst_len = -dst_len;
6847 break;
6849 SetLastError( RtlNtStatusToDosError( status ));
6850 return dst_len;
6854 /******************************************************************************
6855 * ResolveLocaleName (kernelbase.@)
6857 INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
6859 LCID lcid;
6860 UINT pos, datalen;
6861 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6863 if (!locale)
6865 static const WCHAR valid[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
6866 WCHAR *p, tmp[LOCALE_NAME_MAX_LENGTH];
6868 if (wcsspn( name, valid ) < wcslen( name ))
6870 SetLastError( ERROR_INVALID_PARAMETER );
6871 return 0;
6873 lstrcpynW( tmp, name, LOCALE_NAME_MAX_LENGTH );
6874 while (!locale)
6876 for (p = tmp + wcslen(tmp) - 1; p >= tmp; p--) if (*p == '-' || *p == '_') break;
6877 if (p <= tmp) break;
6878 *p = 0;
6879 locale = get_locale_by_name( tmp, &lcid );
6883 pos = locale ? (locale->inotneutral ? locale->sname : locale->ssortlocale) : 0;
6884 datalen = locale_strings[pos] + 1;
6886 if (!len) return datalen;
6887 lstrcpynW( buffer, locale_strings + pos + 1, len );
6888 if (datalen > len)
6890 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6891 return 0;
6893 return datalen;
6897 /******************************************************************************
6898 * SetLocaleInfoW (kernelbase.@)
6900 BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
6902 WCHAR *str, tmp[80];
6904 if (!data)
6906 SetLastError( ERROR_INVALID_PARAMETER );
6907 return FALSE;
6910 switch (LOWORD(lctype))
6912 case LOCALE_ICALENDARTYPE: return set_registry_entry( &entry_icalendartype, data );
6913 case LOCALE_ICURRDIGITS: return set_registry_entry( &entry_icurrdigits, data );
6914 case LOCALE_ICURRENCY: return set_registry_entry( &entry_icurrency, data );
6915 case LOCALE_IDIGITS: return set_registry_entry( &entry_idigits, data );
6916 case LOCALE_IDIGITSUBSTITUTION: return set_registry_entry( &entry_idigitsubstitution, data );
6917 case LOCALE_IFIRSTDAYOFWEEK: return set_registry_entry( &entry_ifirstdayofweek, data );
6918 case LOCALE_IFIRSTWEEKOFYEAR: return set_registry_entry( &entry_ifirstweekofyear, data );
6919 case LOCALE_ILZERO: return set_registry_entry( &entry_ilzero, data );
6920 case LOCALE_IMEASURE: return set_registry_entry( &entry_imeasure, data );
6921 case LOCALE_INEGCURR: return set_registry_entry( &entry_inegcurr, data );
6922 case LOCALE_INEGNUMBER: return set_registry_entry( &entry_inegnumber, data );
6923 case LOCALE_IPAPERSIZE: return set_registry_entry( &entry_ipapersize, data );
6924 case LOCALE_S1159: return set_registry_entry( &entry_s1159, data );
6925 case LOCALE_S2359: return set_registry_entry( &entry_s2359, data );
6926 case LOCALE_SCURRENCY: return set_registry_entry( &entry_scurrency, data );
6927 case LOCALE_SDECIMAL: return set_registry_entry( &entry_sdecimal, data );
6928 case LOCALE_SGROUPING: return set_registry_entry( &entry_sgrouping, data );
6929 case LOCALE_SLIST: return set_registry_entry( &entry_slist, data );
6930 case LOCALE_SLONGDATE: return set_registry_entry( &entry_slongdate, data );
6931 case LOCALE_SMONDECIMALSEP: return set_registry_entry( &entry_smondecimalsep, data );
6932 case LOCALE_SMONGROUPING: return set_registry_entry( &entry_smongrouping, data );
6933 case LOCALE_SMONTHOUSANDSEP: return set_registry_entry( &entry_smonthousandsep, data );
6934 case LOCALE_SNATIVEDIGITS: return set_registry_entry( &entry_snativedigits, data );
6935 case LOCALE_SNEGATIVESIGN: return set_registry_entry( &entry_snegativesign, data );
6936 case LOCALE_SPOSITIVESIGN: return set_registry_entry( &entry_spositivesign, data );
6937 case LOCALE_SSHORTTIME: return set_registry_entry( &entry_sshorttime, data );
6938 case LOCALE_STHOUSAND: return set_registry_entry( &entry_sthousand, data );
6939 case LOCALE_SYEARMONTH: return set_registry_entry( &entry_syearmonth, data );
6941 case LOCALE_SDATE:
6942 if (!get_locale_info( user_locale, user_lcid, LOCALE_SSHORTDATE, tmp, ARRAY_SIZE(tmp) )) break;
6943 data = locale_replace_separator( tmp, data );
6944 /* fall through */
6945 case LOCALE_SSHORTDATE:
6946 if (!set_registry_entry( &entry_sshortdate, data )) return FALSE;
6947 update_registry_value( LOCALE_IDATE, NULL, L"iDate" );
6948 update_registry_value( LOCALE_SDATE, NULL, L"sDate" );
6949 return TRUE;
6951 case LOCALE_STIME:
6952 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6953 data = locale_replace_separator( tmp, data );
6954 /* fall through */
6955 case LOCALE_STIMEFORMAT:
6956 if (!set_registry_entry( &entry_stimeformat, data )) return FALSE;
6957 update_registry_value( LOCALE_ITIME, NULL, L"iTime" );
6958 update_registry_value( LOCALE_ITIMEMARKPOSN, NULL, L"iTimePrefix" );
6959 update_registry_value( LOCALE_ITLZERO, NULL, L"iTLZero" );
6960 update_registry_value( LOCALE_STIME, NULL, L"sTime" );
6961 return TRUE;
6963 case LOCALE_ITIME:
6964 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6965 if (!(str = find_format( tmp, L"Hh" ))) break;
6966 while (*str == 'h' || *str == 'H') *str++ = (*data == '0' ? 'h' : 'H');
6967 if (!set_registry_entry( &entry_stimeformat, tmp )) break;
6968 update_registry_value( LOCALE_ITIME, NULL, L"iTime" );
6969 return TRUE;
6971 case LOCALE_SINTLSYMBOL:
6972 if (!set_registry_entry( &entry_sintlsymbol, data )) return FALSE;
6973 /* if restoring the original value, restore the original LOCALE_SCURRENCY as well */
6974 if (!wcsicmp( data, locale_strings + user_locale->sintlsymbol + 1 ))
6975 data = locale_strings + user_locale->scurrency + 1;
6976 set_registry_entry( &entry_scurrency, data );
6977 return TRUE;
6979 SetLastError( ERROR_INVALID_FLAGS );
6980 return FALSE;
6984 /***********************************************************************
6985 * SetCalendarInfoW (kernelbase.@)
6987 INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
6989 FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
6990 return 0;
6994 /***********************************************************************
6995 * SetProcessPreferredUILanguages (kernelbase.@)
6997 BOOL WINAPI DECLSPEC_HOTPATCH SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6999 return set_ntstatus( RtlSetProcessPreferredUILanguages( flags, buffer, count ));
7003 /***********************************************************************
7004 * SetThreadPreferredUILanguages (kernelbase.@)
7006 BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
7008 return set_ntstatus( RtlSetThreadPreferredUILanguages( flags, buffer, count ));
7012 /***********************************************************************
7013 * SetTimeZoneInformation (kernelbase.@)
7015 BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info )
7017 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info ));
7021 /******************************************************************************
7022 * SetUserGeoID (kernelbase.@)
7024 BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id )
7026 const struct geo_id *geo = find_geo_id_entry( id );
7027 WCHAR bufferW[10];
7028 HKEY hkey;
7030 if (!geo)
7032 SetLastError( ERROR_INVALID_PARAMETER );
7033 return FALSE;
7035 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
7037 const WCHAR *name = geo->class == GEOCLASS_NATION ? L"Nation" : L"Region";
7038 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geo->id );
7039 RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
7041 if (geo->class == GEOCLASS_NATION || wcscmp( geo->iso2, L"XX" ))
7042 lstrcpyW( bufferW, geo->iso2 );
7043 else
7044 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", geo->uncode );
7045 RegSetValueExW( hkey, L"Name", 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
7046 RegCloseKey( hkey );
7048 return TRUE;
7052 /***********************************************************************
7053 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
7055 BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info,
7056 const SYSTEMTIME *system,
7057 SYSTEMTIME *local )
7059 TIME_ZONE_INFORMATION tzinfo;
7060 LARGE_INTEGER ft;
7062 if (!info)
7064 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
7065 info = &tzinfo;
7068 if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE;
7069 switch (get_timezone_id( info, ft, FALSE ))
7071 case TIME_ZONE_ID_UNKNOWN:
7072 ft.QuadPart -= info->Bias * (LONGLONG)600000000;
7073 break;
7074 case TIME_ZONE_ID_STANDARD:
7075 ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000;
7076 break;
7077 case TIME_ZONE_ID_DAYLIGHT:
7078 ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
7079 break;
7080 default:
7081 return FALSE;
7083 return FileTimeToSystemTime( (FILETIME *)&ft, local );
7087 /***********************************************************************
7088 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
7090 BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info,
7091 const SYSTEMTIME *local,
7092 SYSTEMTIME *system )
7094 TIME_ZONE_INFORMATION tzinfo;
7095 LARGE_INTEGER ft;
7097 if (!info)
7099 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
7100 info = &tzinfo;
7103 if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE;
7104 switch (get_timezone_id( info, ft, TRUE ))
7106 case TIME_ZONE_ID_UNKNOWN:
7107 ft.QuadPart += info->Bias * (LONGLONG)600000000;
7108 break;
7109 case TIME_ZONE_ID_STANDARD:
7110 ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000;
7111 break;
7112 case TIME_ZONE_ID_DAYLIGHT:
7113 ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
7114 break;
7115 default:
7116 return FALSE;
7118 return FileTimeToSystemTime( (FILETIME *)&ft, system );
7122 /***********************************************************************
7123 * VerLanguageNameA (kernelbase.@)
7125 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
7127 return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
7131 /***********************************************************************
7132 * VerLanguageNameW (kernelbase.@)
7134 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
7136 return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
7140 /***********************************************************************
7141 * WideCharToMultiByte (kernelbase.@)
7143 INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
7144 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
7146 const CPTABLEINFO *info;
7147 int ret;
7149 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
7151 SetLastError( ERROR_INVALID_PARAMETER );
7152 return 0;
7155 if (srclen < 0) srclen = lstrlenW(src) + 1;
7157 switch (codepage)
7159 case CP_SYMBOL:
7160 ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
7161 break;
7162 case CP_UTF7:
7163 ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
7164 break;
7165 case CP_UNIXCP:
7166 codepage = unix_cp;
7167 /* fall through */
7168 default:
7169 if (!(info = get_codepage_table( codepage )))
7171 SetLastError( ERROR_INVALID_PARAMETER );
7172 return 0;
7174 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
7175 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
7177 SetLastError( ERROR_INVALID_FLAGS );
7178 return 0;
7180 if (info->CodePage == CP_UTF8)
7181 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
7182 else
7183 ret = wcstombs_codepage( info, flags, src, srclen, dst, dstlen, defchar, used );
7184 break;
7186 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
7187 return ret;
7191 /***********************************************************************
7192 * GetUserDefaultGeoName (kernelbase.@)
7194 INT WINAPI GetUserDefaultGeoName(LPWSTR geo_name, int count)
7196 WCHAR buffer[32];
7197 LSTATUS status;
7198 DWORD size;
7199 HKEY key;
7201 TRACE( "geo_name %p, count %d.\n", geo_name, count );
7203 if (count && !geo_name)
7205 SetLastError( ERROR_INVALID_PARAMETER );
7206 return 0;
7208 if (!(status = RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &key )))
7210 size = sizeof(buffer);
7211 status = RegQueryValueExW( key, L"Name", NULL, NULL, (BYTE *)buffer, &size );
7212 RegCloseKey( key );
7214 if (status)
7216 const struct geo_id *geo = find_geo_id_entry( GetUserGeoID( GEOCLASS_NATION ));
7217 if (geo && geo->id != 39070)
7218 lstrcpyW( buffer, geo->iso2 );
7219 else
7220 lstrcpyW( buffer, L"001" );
7222 size = lstrlenW( buffer ) + 1;
7223 if (count < size)
7225 if (!count)
7226 return size;
7227 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7228 return 0;
7230 lstrcpyW( geo_name, buffer );
7231 return size;
7235 /***********************************************************************
7236 * SetUserDefaultGeoName (kernelbase.@)
7238 BOOL WINAPI SetUserGeoName(PWSTR geo_name)
7240 const struct geo_id *geo;
7242 TRACE( "geo_name %s.\n", debugstr_w( geo_name ));
7244 if (!geo_name || !(geo = find_geo_name_entry( geo_name )))
7246 SetLastError( ERROR_INVALID_PARAMETER );
7247 return FALSE;
7249 return SetUserGeoID( geo->id );
7253 static void grouping_to_string( UINT grouping, WCHAR *buffer )
7255 UINT last_digit = grouping % 10;
7256 WCHAR tmp[10], *p = tmp;
7258 /* The string is confusingly different when it comes to repetitions (trailing zeros). For a string,
7259 * a 0 signals that the format needs to be repeated, which is the opposite of the grouping integer. */
7260 if (last_digit == 0)
7262 grouping /= 10;
7264 /* Special case: two or more trailing zeros result in zero-sided groupings, with no repeats */
7265 if (grouping % 10 == 0)
7266 last_digit = ~0;
7269 while (grouping)
7271 *p++ = '0' + grouping % 10;
7272 grouping /= 10;
7274 while (p > tmp)
7276 *buffer++ = *(--p);
7277 if (p > tmp) *buffer++ = ';';
7279 if (last_digit != 0)
7281 *buffer++ = ';';
7282 *buffer++ = '0';
7283 if (last_digit == ~0)
7285 /* Add another trailing zero due to the weird way trailing zeros work in grouping string */
7286 *buffer++ = ';';
7287 *buffer++ = '0';
7290 *buffer = 0;
7294 static WCHAR *prepend_str( WCHAR *end, const WCHAR *str )
7296 UINT len = wcslen( str );
7297 return memcpy( end - len, str, len * sizeof(WCHAR) );
7301 /* format a positive number with decimal part; helper for get_number_format */
7302 static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decimal_sep,
7303 const WCHAR *thousand_sep, const WCHAR *grouping, UINT digits, BOOL lzero )
7305 BOOL round = FALSE, repeat = FALSE;
7306 UINT i, len = 0, prev = ~0;
7307 const WCHAR *frac = NULL;
7309 *(--end) = 0;
7311 for (i = 0; value[i]; i++)
7313 if (value[i] >= '0' && value[i] <= '9') continue;
7314 if (value[i] != '.') return NULL;
7315 if (frac) return NULL;
7316 frac = value + i + 1;
7319 /* format fractional part */
7321 len = frac ? wcslen( frac ) : 0;
7323 if (len > digits)
7325 round = frac[digits] >= '5';
7326 len = digits;
7328 while (digits > len)
7330 (*--end) = '0';
7331 digits--;
7333 while (len)
7335 WCHAR ch = frac[--len];
7336 if (round)
7338 if (ch != '9')
7340 ch++;
7341 round = FALSE;
7343 else ch = '0';
7345 *(--end) = ch;
7347 if (*end) end = prepend_str( end, decimal_sep );
7349 /* format integer part */
7351 len = frac ? frac - value - 1 : wcslen( value );
7353 while (len && *value == '0')
7355 value++;
7356 len--;
7358 if (len) lzero = FALSE;
7360 /* leading 0s are ignored */
7361 while (grouping[0] == '0' && grouping[1] == ';')
7362 grouping += 2;
7364 while (len)
7366 UINT limit = prev;
7368 if (!repeat)
7370 limit = *grouping - '0';
7371 if (grouping[1] == ';')
7373 grouping += 2;
7374 if (limit)
7375 prev = limit;
7376 else
7378 /* Trailing 0;0 is a special case */
7379 prev = ~0;
7380 if (grouping[0] == '0' && grouping[1] != ';')
7382 repeat = TRUE;
7383 limit = prev;
7387 else
7389 repeat = TRUE;
7390 if (!limit)
7391 limit = prev;
7392 else
7393 prev = ~0;
7397 while (len && limit--)
7399 WCHAR ch = value[--len];
7400 if (round)
7402 if (ch != '9')
7404 ch++;
7405 round = FALSE;
7407 else ch = '0';
7409 *(--end) = ch;
7411 if (len) end = prepend_str( end, thousand_sep );
7413 if (round) *(--end) = '1';
7414 else if (lzero) *(--end) = '0';
7415 return end;
7419 static int get_number_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value,
7420 const NUMBERFMTW *format, WCHAR *buffer, int len )
7422 WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_neg[5], grouping[24], output[256];
7423 const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand;
7424 DWORD digits, lzero, order;
7425 int ret = 0;
7426 BOOL negative = (*value == '-');
7428 flags &= LOCALE_NOUSEROVERRIDE;
7430 if (!format)
7432 get_locale_info( locale, 0, LOCALE_SGROUPING | flags, grouping, ARRAY_SIZE(grouping) );
7433 get_locale_info( locale, 0, LOCALE_SDECIMAL | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) );
7434 get_locale_info( locale, 0, LOCALE_STHOUSAND | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) );
7435 get_locale_info( locale, 0, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER | flags,
7436 (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) );
7437 get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags,
7438 (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) );
7439 get_locale_info( locale, 0, LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER | flags,
7440 (WCHAR *)&order, sizeof(DWORD)/sizeof(WCHAR) );
7442 else
7444 if (flags)
7446 SetLastError( ERROR_INVALID_FLAGS );
7447 return 0;
7449 decimal_sep = format->lpDecimalSep;
7450 thousand_sep = format->lpThousandSep;
7451 grouping_to_string( format->Grouping, grouping );
7452 digits = format->NumDigits;
7453 lzero = format->LeadingZero;
7454 order = format->NegativeOrder;
7455 if (!decimal_sep || !thousand_sep)
7457 SetLastError( ERROR_INVALID_PARAMETER );
7458 return 0;
7462 if (negative)
7464 value++;
7465 get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) );
7468 if (!(num = format_number( output + ARRAY_SIZE(output) - 6, value,
7469 decimal_sep, thousand_sep, grouping, digits, lzero )))
7471 SetLastError( ERROR_INVALID_PARAMETER );
7472 return 0;
7475 if (negative)
7477 switch (order)
7479 case 0: /* (1.1) */
7480 num = prepend_str( num, L"(" );
7481 wcscat( num, L")" );
7482 break;
7483 case 2: /* - 1.1 */
7484 num = prepend_str( num, L" " );
7485 /* fall through */
7486 case 1: /* -1.1 */
7487 num = prepend_str( num, fmt_neg );
7488 break;
7489 case 4: /* 1.1 - */
7490 wcscat( num, L" " );
7491 /* fall through */
7492 case 3: /* 1.1- */
7493 wcscat( num, fmt_neg );
7494 break;
7495 default:
7496 SetLastError( ERROR_INVALID_PARAMETER );
7497 return 0;
7501 ret = wcslen( num ) + 1;
7502 if (!len) return ret;
7503 lstrcpynW( buffer, num, len );
7504 if (ret > len)
7506 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7507 return 0;
7509 return ret;
7513 static int get_currency_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value,
7514 const CURRENCYFMTW *format, WCHAR *buffer, int len )
7516 WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_symbol[13], fmt_neg[5], grouping[20], output[256];
7517 const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand, *symbol = fmt_symbol;
7518 DWORD digits, lzero, pos_order, neg_order;
7519 int ret = 0;
7520 BOOL negative = (*value == '-');
7522 flags &= LOCALE_NOUSEROVERRIDE;
7524 if (!format)
7526 get_locale_info( locale, 0, LOCALE_SCURRENCY | flags, fmt_symbol, ARRAY_SIZE(fmt_symbol) );
7527 get_locale_info( locale, 0, LOCALE_SMONGROUPING | flags, grouping, ARRAY_SIZE(grouping) );
7528 get_locale_info( locale, 0, LOCALE_SMONDECIMALSEP | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) );
7529 get_locale_info( locale, 0, LOCALE_SMONTHOUSANDSEP | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) );
7530 get_locale_info( locale, 0, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER | flags,
7531 (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) );
7532 get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags,
7533 (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) );
7534 get_locale_info( locale, 0, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | flags,
7535 (WCHAR *)&pos_order, sizeof(DWORD)/sizeof(WCHAR) );
7536 get_locale_info( locale, 0, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | flags,
7537 (WCHAR *)&neg_order, sizeof(DWORD)/sizeof(WCHAR) );
7539 else
7541 if (flags)
7543 SetLastError( ERROR_INVALID_FLAGS );
7544 return 0;
7546 decimal_sep = format->lpDecimalSep;
7547 thousand_sep = format->lpThousandSep;
7548 symbol = format->lpCurrencySymbol;
7549 grouping_to_string( format->Grouping, grouping );
7550 digits = format->NumDigits;
7551 lzero = format->LeadingZero;
7552 pos_order = format->PositiveOrder;
7553 neg_order = format->NegativeOrder;
7554 if (!decimal_sep || !thousand_sep || !symbol)
7556 SetLastError( ERROR_INVALID_PARAMETER );
7557 return 0;
7561 if (negative)
7563 value++;
7564 get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) );
7567 if (!(num = format_number( output + ARRAY_SIZE(output) - 20, value,
7568 decimal_sep, thousand_sep, grouping, digits, lzero )))
7570 SetLastError( ERROR_INVALID_PARAMETER );
7571 return 0;
7574 if (negative)
7576 switch (neg_order)
7578 case 14: /* ($ 1.1) */
7579 num = prepend_str( num, L" " );
7580 /* fall through */
7581 case 0: /* ($1.1) */
7582 num = prepend_str( num, symbol );
7583 num = prepend_str( num, L"(" );
7584 wcscat( num, L")" );
7585 break;
7586 case 9: /* -$ 1.1 */
7587 num = prepend_str( num, L" " );
7588 /* fall through */
7589 case 1: /* -$1.1 */
7590 num = prepend_str( num, symbol );
7591 num = prepend_str( num, fmt_neg );
7592 break;
7593 case 2: /* $-1.1 */
7594 num = prepend_str( num, fmt_neg );
7595 num = prepend_str( num, symbol );
7596 break;
7597 case 11: /* $ 1.1- */
7598 num = prepend_str( num, L" " );
7599 /* fall through */
7600 case 3: /* $1.1- */
7601 num = prepend_str( num, symbol );
7602 wcscat( num, fmt_neg );
7603 break;
7604 case 15: /* (1.1 $) */
7605 wcscat( num, L" " );
7606 /* fall through */
7607 case 4: /* (1.1$) */
7608 wcscat( num, symbol );
7609 num = prepend_str( num, L"(" );
7610 wcscat( num, L")" );
7611 break;
7612 case 8: /* -1.1 $ */
7613 wcscat( num, L" " );
7614 /* fall through */
7615 case 5: /* -1.1$ */
7616 num = prepend_str( num, fmt_neg );
7617 wcscat( num, symbol );
7618 break;
7619 case 6: /* 1.1-$ */
7620 wcscat( num, fmt_neg );
7621 wcscat( num, symbol );
7622 break;
7623 case 10: /* 1.1 $- */
7624 wcscat( num, L" " );
7625 /* fall through */
7626 case 7: /* 1.1$- */
7627 wcscat( num, symbol );
7628 wcscat( num, fmt_neg );
7629 break;
7630 case 12: /* $ -1.1 */
7631 num = prepend_str( num, fmt_neg );
7632 num = prepend_str( num, L" " );
7633 num = prepend_str( num, symbol );
7634 break;
7635 case 13: /* 1.1- $ */
7636 wcscat( num, fmt_neg );
7637 wcscat( num, L" " );
7638 wcscat( num, symbol );
7639 break;
7640 default:
7641 SetLastError( ERROR_INVALID_PARAMETER );
7642 return 0;
7645 else
7647 switch (pos_order)
7649 case 2: /* $ 1.1 */
7650 num = prepend_str( num, L" " );
7651 /* fall through */
7652 case 0: /* $1.1 */
7653 num = prepend_str( num, symbol );
7654 break;
7655 case 3: /* 1.1 $ */
7656 wcscat( num, L" " );
7657 /* fall through */
7658 case 1: /* 1.1$ */
7659 wcscat( num, symbol );
7660 break;
7661 default:
7662 SetLastError( ERROR_INVALID_PARAMETER );
7663 return 0;
7667 ret = wcslen( num ) + 1;
7668 if (!len) return ret;
7669 lstrcpynW( buffer, num, len );
7670 if (ret > len)
7672 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7673 return 0;
7675 return ret;
7679 /* get the length of a date/time formatting pattern */
7680 static int get_pattern_len( const WCHAR *pattern, const WCHAR *accept )
7682 int i;
7684 if (*pattern == '\'')
7686 for (i = 1; pattern[i]; i++)
7688 if (pattern[i] != '\'') continue;
7689 if (pattern[++i] != '\'') return i;
7691 return i;
7693 if (!wcschr( accept, *pattern )) return 1;
7694 for (i = 1; pattern[i]; i++) if (pattern[i] != pattern[0]) break;
7695 return i;
7699 static int get_date_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime,
7700 const WCHAR *format, WCHAR *buffer, int len )
7702 DWORD override = flags & LOCALE_NOUSEROVERRIDE;
7703 DWORD genitive = 0;
7704 WCHAR *p, fmt[80], output[256];
7705 SYSTEMTIME time;
7706 int ret, val, count, i;
7708 if (!format)
7710 if (flags & DATE_USE_ALT_CALENDAR) FIXME( "alt calendar not supported\n" );
7711 switch (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY))
7713 case 0:
7714 case DATE_SHORTDATE:
7715 get_locale_info( locale, 0, LOCALE_SSHORTDATE | override, fmt, ARRAY_SIZE(fmt) );
7716 break;
7717 case DATE_LONGDATE:
7718 get_locale_info( locale, 0, LOCALE_SLONGDATE | override, fmt, ARRAY_SIZE(fmt) );
7719 break;
7720 case DATE_YEARMONTH:
7721 get_locale_info( locale, 0, LOCALE_SYEARMONTH | override, fmt, ARRAY_SIZE(fmt) );
7722 break;
7723 case DATE_MONTHDAY:
7724 get_locale_info( locale, 0, LOCALE_SMONTHDAY | override, fmt, ARRAY_SIZE(fmt) );
7725 break;
7726 default:
7727 SetLastError( ERROR_INVALID_FLAGS );
7728 return 0;
7730 format = fmt;
7732 else if (override || (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY)))
7734 SetLastError( ERROR_INVALID_FLAGS );
7735 return 0;
7738 if (systime)
7740 FILETIME ft;
7742 time = *systime;
7743 time.wHour = time.wMinute = time.wSecond = time.wMilliseconds = 0;
7744 if (!SystemTimeToFileTime( &time, &ft ) || !FileTimeToSystemTime( &ft, &time )) return 0;
7746 else GetLocalTime( &time );
7748 for (p = output; *format; format += count)
7750 count = get_pattern_len( format, L"yMd" );
7752 switch (*format)
7754 case '\'':
7755 for (i = 1; i < count; i++)
7757 if (format[i] == '\'') i++;
7758 if (i < count) *p++ = format[i];
7760 break;
7762 case 'y':
7763 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%02u",
7764 (count <= 2) ? time.wYear % 100 : time.wYear );
7765 break;
7767 case 'M':
7768 if (count <= 2)
7770 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wMonth );
7771 break;
7773 val = (count == 3 ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1) + time.wMonth - 1;
7774 if (!genitive)
7776 for (i = count; format[i]; i += get_pattern_len( format + i, L"yMd" ))
7778 if (format[i] != 'd') continue;
7779 if (format[i + 1] != 'd' || format[i + 2] != 'd')
7780 genitive = LOCALE_RETURN_GENITIVE_NAMES;
7781 break;
7784 p += get_locale_info( locale, 0, val | override | genitive,
7785 p, output + ARRAY_SIZE(output) - p ) - 1;
7786 break;
7788 case 'd':
7789 if (count <= 2)
7791 genitive = LOCALE_RETURN_GENITIVE_NAMES;
7792 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wDay );
7793 break;
7795 genitive = 0;
7796 val = (count == 3 ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1) + (time.wDayOfWeek + 6) % 7;
7797 p += get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p ) - 1;
7798 break;
7800 case 'g':
7801 p += locale_return_string( count >= 2 ? locale->serastring : locale->sabbreverastring,
7802 override, p, output + ARRAY_SIZE(output) - p ) - 1;
7803 break;
7805 default:
7806 *p++ = *format;
7807 break;
7810 *p++ = 0;
7811 ret = p - output;
7813 if (!len) return ret;
7814 lstrcpynW( buffer, output, len );
7815 if (ret > len)
7817 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7818 return 0;
7820 return ret;
7824 static int get_time_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime,
7825 const WCHAR *format, WCHAR *buffer, int len )
7827 DWORD override = flags & LOCALE_NOUSEROVERRIDE;
7828 WCHAR *p, *last, fmt[80], output[256];
7829 SYSTEMTIME time;
7830 int i, ret, val, count;
7831 BOOL skip = FALSE;
7833 if (!format)
7835 get_locale_info( locale, 0, LOCALE_STIMEFORMAT | override, fmt, ARRAY_SIZE(fmt) );
7836 format = fmt;
7838 else if (override)
7840 SetLastError( ERROR_INVALID_FLAGS );
7841 return 0;
7844 if (systime)
7846 time = *systime;
7847 if (time.wMilliseconds > 999 || time.wSecond > 59 || time.wMinute > 59 || time.wHour > 23)
7849 SetLastError( ERROR_INVALID_PARAMETER );
7850 return 0;
7853 else GetLocalTime( &time );
7855 for (p = last = output; *format; format += count)
7857 count = get_pattern_len( format, L"Hhmst" );
7859 switch (*format)
7861 case '\'':
7862 for (i = 1; i < count; i++)
7864 if (format[i] == '\'') i++;
7865 if (!skip && i < count) *p++ = format[i];
7867 continue;
7869 case 'H':
7870 val = time.wHour;
7871 break;
7873 case 'h':
7874 val = time.wHour;
7875 if (!(flags & TIME_FORCE24HOURFORMAT))
7877 val %= 12;
7878 if (!val) val = 12;
7880 break;
7882 case 'm':
7883 if (flags & TIME_NOMINUTESORSECONDS)
7885 p = last;
7886 skip = TRUE;
7887 continue;
7889 val = time.wMinute;
7890 break;
7892 case 's':
7893 if (flags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS))
7895 p = last;
7896 skip = TRUE;
7897 continue;
7899 val = time.wSecond;
7900 break;
7902 case 't':
7903 if (flags & TIME_NOTIMEMARKER)
7905 p = last;
7906 skip = TRUE;
7907 continue;
7909 val = time.wHour < 12 ? LOCALE_S1159 : LOCALE_S2359;
7910 ret = get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p );
7911 p += (count > 1) ? ret - 1 : 1;
7912 skip = FALSE;
7913 continue;
7915 default:
7916 if (!skip || *format == ' ') *p++ = *format;
7917 continue;
7920 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", min( 2, count ), val );
7921 last = p;
7922 skip = FALSE;
7924 *p++ = 0;
7925 ret = p - output;
7927 if (!len) return ret;
7928 lstrcpynW( buffer, output, len );
7929 if (ret > len)
7931 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7932 return 0;
7934 return ret;
7938 /**************************************************************************
7939 * GetNumberFormatW (kernelbase.@)
7941 int WINAPI GetNumberFormatW( LCID lcid, DWORD flags, const WCHAR *value,
7942 const NUMBERFMTW *format, WCHAR *buffer, int len )
7944 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7946 if (len < 0 || (len && !buffer) || !value || !locale)
7948 SetLastError( ERROR_INVALID_PARAMETER );
7949 return 0;
7952 TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len );
7953 return get_number_format( locale, flags, value, format, buffer, len );
7957 /**************************************************************************
7958 * GetNumberFormatEx (kernelbase.@)
7960 int WINAPI GetNumberFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value,
7961 const NUMBERFMTW *format, WCHAR *buffer, int len )
7963 LCID lcid;
7964 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
7966 if (len < 0 || (len && !buffer) || !value || !locale)
7968 SetLastError( ERROR_INVALID_PARAMETER );
7969 return 0;
7972 TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len );
7973 return get_number_format( locale, flags, value, format, buffer, len );
7977 /***********************************************************************
7978 * GetCurrencyFormatW (kernelbase.@)
7980 int WINAPI GetCurrencyFormatW( LCID lcid, DWORD flags, const WCHAR *value,
7981 const CURRENCYFMTW *format, WCHAR *buffer, int len )
7983 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7985 if (len < 0 || (len && !buffer) || !value || !locale)
7987 SetLastError( ERROR_INVALID_PARAMETER );
7988 return 0;
7991 TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len );
7992 return get_currency_format( locale, flags, value, format, buffer, len );
7996 /***********************************************************************
7997 * GetCurrencyFormatEx (kernelbase.@)
7999 int WINAPI GetCurrencyFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value,
8000 const CURRENCYFMTW *format, WCHAR *buffer, int len )
8002 LCID lcid;
8003 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
8005 if (len < 0 || (len && !buffer) || !value || !locale)
8007 SetLastError( ERROR_INVALID_PARAMETER );
8008 return 0;
8011 TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len );
8012 return get_currency_format( locale, flags, value, format, buffer, len );
8016 /******************************************************************************
8017 * GetDateFormatA (KERNEL32.@)
8019 int WINAPI GetDateFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time,
8020 const char *format, char *buffer, int len )
8022 UINT cp = get_lcid_codepage( lcid, flags );
8023 WCHAR formatW[128], output[128];
8024 int ret;
8026 TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len );
8028 if (len < 0 || (len && !buffer))
8030 SetLastError( ERROR_INVALID_PARAMETER );
8031 return 0;
8033 if (format)
8035 MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) );
8036 ret = GetDateFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) );
8038 else ret = GetDateFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) );
8040 if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 );
8041 return ret;
8045 /***********************************************************************
8046 * GetDateFormatW (kernelbase.@)
8048 int WINAPI GetDateFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime,
8049 const WCHAR *format, WCHAR *buffer, int len )
8051 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
8053 if (len < 0 || (len && !buffer) || !locale)
8055 SetLastError( ERROR_INVALID_PARAMETER );
8056 return 0;
8059 TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len );
8060 return get_date_format( locale, flags, systime, format, buffer, len );
8064 /***********************************************************************
8065 * GetDateFormatEx (kernelbase.@)
8067 int WINAPI GetDateFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime,
8068 const WCHAR *format, WCHAR *buffer, int len, const WCHAR *calendar )
8070 LCID lcid;
8071 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
8073 if (len < 0 || (len && !buffer) || !locale || calendar)
8075 SetLastError( ERROR_INVALID_PARAMETER );
8076 return 0;
8079 TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len );
8080 return get_date_format( locale, flags, systime, format, buffer, len );
8084 /******************************************************************************
8085 * GetTimeFormatA (kernelbase.@)
8087 int WINAPI GetTimeFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time,
8088 const char *format, char *buffer, int len )
8090 UINT cp = get_lcid_codepage( lcid, flags );
8091 WCHAR formatW[128], output[128];
8092 int ret;
8094 TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len );
8096 if (len < 0 || (len && !buffer))
8098 SetLastError( ERROR_INVALID_PARAMETER );
8099 return 0;
8101 if (format)
8103 MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) );
8104 ret = GetTimeFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) );
8106 else ret = GetTimeFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) );
8108 if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 );
8109 return ret;
8113 /***********************************************************************
8114 * GetTimeFormatW (kernelbase.@)
8116 int WINAPI GetTimeFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime,
8117 const WCHAR *format, WCHAR *buffer, int len )
8119 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
8121 if (len < 0 || (len && !buffer) || !locale)
8123 SetLastError( ERROR_INVALID_PARAMETER );
8124 return 0;
8127 TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len );
8128 return get_time_format( locale, flags, systime, format, buffer, len );
8132 /***********************************************************************
8133 * GetTimeFormatEx (kernelbase.@)
8135 int WINAPI GetTimeFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime,
8136 const WCHAR *format, WCHAR *buffer, int len )
8138 LCID lcid;
8139 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
8141 if (len < 0 || (len && !buffer) || !locale)
8143 SetLastError( ERROR_INVALID_PARAMETER );
8144 return 0;
8147 TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len );
8148 return get_time_format( locale, flags, systime, format, buffer, len );