kernelbase: Let GetModuleBaseName succeed on 64bit modules in wow64.
[wine.git] / dlls / kernelbase / locale.c
blob13ec1ebeb3a4f5fcfea92584ba3ad4d0dba91af7
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 for (len = start + 1; len <= srclen; len++)
3920 pos = start;
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) goto next;
3928 if (memcmp( primary, val.key_primary.buf + s.primary_pos, s.key_primary.len )) goto next;
3929 s.primary_pos += s.key_primary.len;
3930 s.key_primary.len = 0;
3932 if (s.primary_pos < val.key_primary.len) goto next;
3934 have_extra = remove_unneeded_weights( sortid, &s );
3935 if (compare_sortkeys( &s.key_diacritic, &val.key_diacritic, FALSE )) goto next;
3936 if (compare_sortkeys( &s.key_case, &val.key_case, FALSE )) goto next;
3938 if (have_extra && have_extra_val)
3940 for (i = 0; i < 4; i++)
3941 if (compare_sortkeys( &s.key_extra[i], &val.key_extra[i], i != 1 )) goto next;
3943 else if (have_extra || have_extra_val) goto next;
3945 if (compare_sortkeys( &s.key_special, &val.key_special, FALSE )) goto next;
3947 found = start;
3948 foundlen = pos - start;
3949 len = srclen; /* no need to continue checking longer strings */
3951 next:
3952 /* reset state */
3953 s.key_primary.len = s.key_diacritic.len = s.key_case.len = s.key_special.len = 0;
3954 s.key_extra[0].len = s.key_extra[1].len = s.key_extra[2].len = s.key_extra[3].len = 0;
3955 s.primary_pos = 0;
3957 if (flags & FIND_STARTSWITH) break;
3958 if (flags & FIND_FROMSTART && found != -1) break;
3961 if (found != -1)
3963 if ((flags & FIND_ENDSWITH) && found + foundlen != srclen) found = -1;
3964 else if (reslen) *reslen = foundlen;
3966 free_sortkey_state( &s );
3967 free_sortkey_state( &val );
3968 return found;
3972 /* map buffer to full-width katakana */
3973 static int map_to_fullwidth( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
3975 int pos, len;
3977 for (pos = 0; srclen; pos++, src += len, srclen -= len)
3979 unsigned int wch = casemap( charmaps[CHARMAP_FULLWIDTH], *src );
3981 len = 1;
3982 if (srclen > 1)
3984 if (table_has_high_planes( charmaps[CHARMAP_FULLWIDTH] ) && IS_SURROGATE_PAIR( src[0], src[1] ))
3986 len = 2;
3987 wch = casemap_high( charmaps[CHARMAP_FULLWIDTH], src[0], src[1] );
3988 if (wch >= 0x10000)
3990 put_utf16( dst, pos, dstlen, wch );
3991 pos++;
3992 continue;
3995 else if (src[1] == 0xff9e) /* dakuten (voiced sound) */
3997 len = 2;
3998 if ((*src >= 0xff76 && *src <= 0xff84) ||
3999 (*src >= 0xff8a && *src <= 0xff8e) ||
4000 *src == 0x30fd)
4001 wch++;
4002 else if (*src == 0xff73)
4003 wch = 0x30f4; /* KATAKANA LETTER VU */
4004 else if (*src == 0xff9c)
4005 wch = 0x30f7; /* KATAKANA LETTER VA */
4006 else if (*src == 0x30f0)
4007 wch = 0x30f8; /* KATAKANA LETTER VI */
4008 else if (*src == 0x30f1)
4009 wch = 0x30f9; /* KATAKANA LETTER VE */
4010 else if (*src == 0xff66)
4011 wch = 0x30fa; /* KATAKANA LETTER VO */
4012 else
4013 len = 1;
4015 else if (src[1] == 0xff9f) /* handakuten (semi-voiced sound) */
4017 if (*src >= 0xff8a && *src <= 0xff8e)
4019 wch += 2;
4020 len = 2;
4025 if (pos < dstlen) dst[pos] = table ? casemap( table, wch ) : wch;
4027 return pos;
4031 static inline int nonspace_ignored( WCHAR ch )
4033 if (get_char_type( CT_CTYPE2, ch ) != C2_OTHERNEUTRAL) return FALSE;
4034 return (get_char_type( CT_CTYPE3, ch ) & (C3_NONSPACING | C3_DIACRITIC));
4037 /* remove ignored chars for NORM_IGNORENONSPACE/NORM_IGNORESYMBOLS */
4038 static int map_remove_ignored( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
4040 int pos;
4042 for (pos = 0; srclen; src++, srclen--)
4044 if (flags & NORM_IGNORESYMBOLS)
4046 if (get_char_type( CT_CTYPE1, *src ) & C1_PUNCT) continue;
4047 if (get_char_type( CT_CTYPE3, *src ) & C3_SYMBOL) continue;
4049 if (flags & NORM_IGNORENONSPACE)
4051 WCHAR buffer[8];
4052 const WCHAR *decomp;
4053 unsigned int i, j, len;
4055 if ((decomp = get_decomposition( *src, &len )) && len > 1)
4057 for (i = j = 0; i < len; i++)
4058 if (!nonspace_ignored( decomp[i] )) buffer[j++] = decomp[i];
4060 if (i > j) /* something was removed */
4062 if (pos + j <= dstlen) memcpy( dst + pos, buffer, j * sizeof(WCHAR) );
4063 pos += j;
4064 continue;
4067 else if (nonspace_ignored( *src )) continue;
4069 if (pos < dstlen) dst[pos] = *src;
4070 pos++;
4072 return pos;
4076 /* map full-width characters to single or double half-width characters. */
4077 static int map_to_halfwidth( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
4079 static const BYTE katakana_map[] =
4081 0x01, 0x00, 0x01, 0x00, /* U+30a8- */
4082 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b0- */
4083 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b8- */
4084 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, /* U+30c0- */
4085 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30c8- */
4086 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, /* U+30d0- */
4087 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x00, /* U+30d8- */
4088 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e0- */
4089 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e8- */
4090 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
4091 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x01 /* U+30f8- */
4093 int pos;
4095 for (pos = 0; srclen; src++, srclen--)
4097 WCHAR ch = table ? casemap( table, *src ) : *src;
4098 USHORT shift = ch - 0x30ac;
4099 BYTE k;
4101 if (shift < ARRAY_SIZE(katakana_map) && (k = katakana_map[shift]))
4103 if (pos < dstlen - 1)
4105 dst[pos] = casemap( charmaps[CHARMAP_HALFWIDTH], ch - k );
4106 dst[pos + 1] = (k == 2) ? 0xff9f : 0xff9e;
4108 pos += 2;
4110 else
4112 if (pos < dstlen) dst[pos] = casemap( charmaps[CHARMAP_HALFWIDTH], ch );
4113 pos++;
4116 return pos;
4120 static int lcmap_string( const struct sortguid *sortid, DWORD flags,
4121 const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
4123 const USHORT *case_table = NULL;
4124 int ret;
4126 if (flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE))
4128 if ((flags & LCMAP_TITLECASE) == LCMAP_TITLECASE) /* FIXME */
4130 SetLastError( ERROR_INVALID_FLAGS );
4131 return 0;
4133 case_table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
4134 case_table = case_table + 2 + (flags & LCMAP_LOWERCASE ? case_table[1] : 0);
4137 switch (flags & ~(LCMAP_BYTEREV | LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING))
4139 case LCMAP_HIRAGANA:
4140 ret = casemap_string( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen );
4141 break;
4142 case LCMAP_KATAKANA:
4143 ret = casemap_string( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen );
4144 break;
4145 case LCMAP_HALFWIDTH:
4146 ret = map_to_halfwidth( NULL, src, srclen, dst, dstlen );
4147 break;
4148 case LCMAP_HIRAGANA | LCMAP_HALFWIDTH:
4149 ret = map_to_halfwidth( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen );
4150 break;
4151 case LCMAP_KATAKANA | LCMAP_HALFWIDTH:
4152 ret = map_to_halfwidth( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen );
4153 break;
4154 case LCMAP_FULLWIDTH:
4155 ret = map_to_fullwidth( NULL, src, srclen, dst, dstlen );
4156 break;
4157 case LCMAP_HIRAGANA | LCMAP_FULLWIDTH:
4158 ret = map_to_fullwidth( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen );
4159 break;
4160 case LCMAP_KATAKANA | LCMAP_FULLWIDTH:
4161 ret = map_to_fullwidth( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen );
4162 break;
4163 case LCMAP_SIMPLIFIED_CHINESE:
4164 ret = casemap_string( charmaps[CHARMAP_SIMPLIFIED], src, srclen, dst, dstlen );
4165 break;
4166 case LCMAP_TRADITIONAL_CHINESE:
4167 ret = casemap_string( charmaps[CHARMAP_TRADITIONAL], src, srclen, dst, dstlen );
4168 break;
4169 case NORM_IGNORENONSPACE:
4170 case NORM_IGNORESYMBOLS:
4171 case NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS:
4172 if (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | LCMAP_BYTEREV))
4174 SetLastError( ERROR_INVALID_FLAGS );
4175 return 0;
4177 ret = map_remove_ignored( flags, src, srclen, dst, dstlen );
4178 break;
4179 case 0:
4180 if (case_table)
4182 ret = casemap_string( case_table, src, srclen, dst, dstlen );
4183 case_table = NULL;
4184 break;
4186 if (flags & LCMAP_BYTEREV)
4188 ret = min( srclen, dstlen );
4189 memcpy( dst, src, ret * sizeof(WCHAR) );
4190 break;
4192 /* fall through */
4193 default:
4194 SetLastError( ERROR_INVALID_FLAGS );
4195 return 0;
4198 if (dstlen && case_table) ret = casemap_string( case_table, dst, ret, dst, dstlen );
4199 if (flags & LCMAP_BYTEREV) map_byterev( dst, min( dstlen, ret ), dst );
4201 if (dstlen && dstlen < ret)
4203 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4204 return 0;
4206 return ret;
4210 static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare )
4212 static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
4213 int first, last, limit, dayinsecs;
4215 if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */
4216 if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */
4218 /* if year is 0 then date is in day-of-week format, otherwise
4219 * it's absolute date.
4221 if (!compare->wYear)
4223 /* wDay is interpreted as number of the week in the month
4224 * 5 means: the last week in the month */
4225 /* calculate the day of the first DayOfWeek in the month */
4226 first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1;
4227 /* check needed for the 5th weekday of the month */
4228 last = month_lengths[tf->Month - 1] +
4229 (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400))));
4230 limit = first + 7 * (compare->wDay - 1);
4231 if (limit > last) limit -= 7;
4233 else limit = compare->wDay;
4235 limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60;
4236 dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second;
4237 return dayinsecs - limit;
4241 static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local )
4243 int year;
4244 BOOL before_standard_date, after_daylight_date;
4245 LARGE_INTEGER t2;
4246 TIME_FIELDS tf;
4248 if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN;
4250 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
4251 if (info->StandardDate.wMonth == 0 ||
4252 (info->StandardDate.wYear == 0 &&
4253 (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 ||
4254 info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5)))
4256 SetLastError( ERROR_INVALID_PARAMETER );
4257 return TIME_ZONE_ID_INVALID;
4260 if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000;
4261 RtlTimeToTimeFields( &time, &tf );
4262 year = tf.Year;
4263 if (!is_local)
4265 t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000;
4266 RtlTimeToTimeFields( &t2, &tf );
4268 if (tf.Year == year)
4269 before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0;
4270 else
4271 before_standard_date = tf.Year < year;
4273 if (!is_local)
4275 t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000;
4276 RtlTimeToTimeFields( &t2, &tf );
4278 if (tf.Year == year)
4279 after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0;
4280 else
4281 after_daylight_date = tf.Year > year;
4283 if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */
4285 if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
4287 else /* Down south */
4289 if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
4291 return TIME_ZONE_ID_STANDARD;
4295 /* Note: the Internal_ functions are not documented. The number of parameters
4296 * should be correct, but their exact meaning may not.
4299 /******************************************************************************
4300 * Internal_EnumCalendarInfo (kernelbase.@)
4302 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc,
4303 const NLS_LOCALE_DATA *locale, CALID id,
4304 CALTYPE type, BOOL unicode, BOOL ex,
4305 BOOL exex, LPARAM lparam )
4307 const USHORT *calendars;
4308 USHORT cal = id;
4309 WCHAR buffer[256];
4310 INT ret, i, count = 1;
4312 if (!proc || !locale)
4314 SetLastError( ERROR_INVALID_PARAMETER );
4315 return FALSE;
4318 if (id == ENUM_ALL_CALENDARS)
4320 count = locale_strings[locale->scalendartype];
4321 calendars = locale_strings + locale->scalendartype + 1;
4323 else if (id <= CAL_UMALQURA)
4325 calendars = &cal;
4326 count = 1;
4328 else
4330 SetLastError( ERROR_INVALID_PARAMETER );
4331 return FALSE;
4334 for (i = 0; i < count; i++)
4336 id = calendars[i];
4337 if (type & CAL_RETURN_NUMBER)
4338 ret = get_calendar_info( locale, id, type, NULL, 0, (LPDWORD)buffer );
4339 else if (unicode)
4340 ret = get_calendar_info( locale, id, type, buffer, ARRAY_SIZE(buffer), NULL );
4341 else
4343 WCHAR bufW[256];
4344 ret = get_calendar_info( locale, id, type, bufW, ARRAY_SIZE(bufW), NULL );
4345 if (ret) WideCharToMultiByte( get_locale_codepage( locale, type ), 0,
4346 bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
4349 if (ret)
4351 if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
4352 else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
4353 else ret = proc( buffer );
4355 if (!ret) break;
4357 return TRUE;
4361 static BOOL call_enum_date_func( DATEFMT_ENUMPROCW proc, const NLS_LOCALE_DATA *locale, DWORD flags,
4362 DWORD str, WCHAR *buffer, CALID id, BOOL unicode,
4363 BOOL ex, BOOL exex, LPARAM lparam )
4365 char buffA[256];
4367 if (str) memcpy( buffer, locale_strings + str + 1, (locale_strings[str] + 1) * sizeof(WCHAR) );
4368 if (exex) return ((DATEFMT_ENUMPROCEXEX)proc)( buffer, id, lparam );
4369 if (ex) return ((DATEFMT_ENUMPROCEXW)proc)( buffer, id );
4370 if (unicode) return proc( buffer );
4371 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
4372 buffA, ARRAY_SIZE(buffA), NULL, NULL );
4373 return proc( (WCHAR *)buffA );
4377 /**************************************************************************
4378 * Internal_EnumDateFormats (kernelbase.@)
4380 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc,
4381 const NLS_LOCALE_DATA *locale, DWORD flags,
4382 BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
4384 WCHAR buffer[256];
4385 INT i, j, ret;
4386 DWORD pos;
4387 const struct calendar *cal;
4388 const USHORT *calendars = locale_strings + locale->scalendartype;
4389 const DWORD *array;
4391 if (!proc || !locale)
4393 SetLastError( ERROR_INVALID_PARAMETER );
4394 return FALSE;
4397 switch (flags & ~LOCALE_USE_CP_ACP)
4399 case 0:
4400 case DATE_SHORTDATE:
4401 if (!get_locale_info( locale, 0, LOCALE_SSHORTDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4402 pos = locale->sshortdate;
4403 break;
4404 case DATE_LONGDATE:
4405 if (!get_locale_info( locale, 0, LOCALE_SLONGDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4406 pos = locale->slongdate;
4407 break;
4408 case DATE_YEARMONTH:
4409 if (!get_locale_info( locale, 0, LOCALE_SYEARMONTH, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4410 pos = locale->syearmonth;
4411 break;
4412 default:
4413 SetLastError( ERROR_INVALID_PARAMETER );
4414 return FALSE;
4417 /* first the user override data */
4419 ret = call_enum_date_func( proc, locale, flags, 0, buffer, 1, unicode, ex, exex, lparam );
4421 /* then the remaining locale data */
4423 array = (const DWORD *)(locale_strings + pos + 1);
4424 for (i = 1; ret && i < locale_strings[pos]; i++)
4425 ret = call_enum_date_func( proc, locale, flags, array[i], buffer, 1, unicode, ex, exex, lparam );
4427 /* then the extra calendars */
4429 for (i = 0; ret && i < calendars[0]; i++)
4431 if (calendars[i + 1] == 1) continue;
4432 if (!(cal = get_calendar_data( locale, calendars[i + 1] ))) continue;
4433 switch (flags & ~LOCALE_USE_CP_ACP)
4435 case 0:
4436 case DATE_SHORTDATE:
4437 pos = cal->sshortdate;
4438 break;
4439 case DATE_LONGDATE:
4440 pos = cal->slongdate;
4441 break;
4442 case DATE_YEARMONTH:
4443 pos = cal->syearmonth;
4444 break;
4446 array = (const DWORD *)(locale_strings + pos + 1);
4447 for (j = 0; ret && j < locale_strings[pos]; j++)
4448 ret = call_enum_date_func( proc, locale, flags, array[j], buffer,
4449 calendars[i + 1], unicode, ex, exex, lparam );
4451 return TRUE;
4455 /******************************************************************************
4456 * Internal_EnumLanguageGroupLocales (kernelbase.@)
4458 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
4459 DWORD flags, LONG_PTR param, BOOL unicode )
4461 WCHAR name[10], value[10];
4462 DWORD name_len, value_len, type, index = 0, alt = 0;
4463 HKEY key, altkey;
4464 LCID lcid;
4466 if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
4468 SetLastError( ERROR_INVALID_PARAMETER );
4469 return FALSE;
4472 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
4473 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
4475 for (;;)
4477 name_len = ARRAY_SIZE(name);
4478 value_len = sizeof(value);
4479 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
4480 &type, (BYTE *)value, &value_len ))
4482 if (alt++) break;
4483 index = 0;
4484 continue;
4486 if (type != REG_SZ) continue;
4487 if (id != wcstoul( value, NULL, 16 )) continue;
4488 lcid = wcstoul( name, NULL, 16 );
4489 if (!unicode)
4491 char nameA[10];
4492 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
4493 if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
4495 else if (!proc( id, lcid, name, param )) break;
4497 RegCloseKey( altkey );
4498 RegCloseKey( key );
4499 return TRUE;
4503 /***********************************************************************
4504 * Internal_EnumSystemCodePages (kernelbase.@)
4506 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
4507 BOOL unicode )
4509 WCHAR name[10];
4510 DWORD name_len, type, index = 0;
4511 HKEY key;
4513 if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
4515 for (;;)
4517 name_len = ARRAY_SIZE(name);
4518 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
4519 if (type != REG_SZ) continue;
4520 if (!wcstoul( name, NULL, 10 )) continue;
4521 if (!unicode)
4523 char nameA[10];
4524 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
4525 if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
4527 else if (!proc( name )) break;
4529 RegCloseKey( key );
4530 return TRUE;
4534 /******************************************************************************
4535 * Internal_EnumSystemLanguageGroups (kernelbase.@)
4537 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
4538 DWORD flags, LONG_PTR param, BOOL unicode )
4540 WCHAR name[10], value[10], descr[80];
4541 DWORD name_len, value_len, type, index = 0;
4542 HKEY key;
4543 LGRPID id;
4545 if (!proc)
4547 SetLastError( ERROR_INVALID_PARAMETER );
4548 return FALSE;
4551 switch (flags)
4553 case 0:
4554 flags = LGRPID_INSTALLED;
4555 break;
4556 case LGRPID_INSTALLED:
4557 case LGRPID_SUPPORTED:
4558 break;
4559 default:
4560 SetLastError( ERROR_INVALID_FLAGS );
4561 return FALSE;
4564 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
4566 for (;;)
4568 name_len = ARRAY_SIZE(name);
4569 value_len = sizeof(value);
4570 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
4571 if (type != REG_SZ) continue;
4573 id = wcstoul( name, NULL, 16 );
4575 if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
4576 if (!LoadStringW( kernelbase_handle, id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
4577 TRACE( "%p: %lu %s %s %lx %Ix\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
4578 if (!unicode)
4580 char nameA[10], descrA[80];
4581 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
4582 WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
4583 if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
4585 else if (!proc( id, name, descr, flags, param )) break;
4587 RegCloseKey( key );
4588 return TRUE;
4592 /**************************************************************************
4593 * Internal_EnumTimeFormats (kernelbase.@)
4595 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc,
4596 const NLS_LOCALE_DATA *locale, DWORD flags,
4597 BOOL unicode, BOOL ex, LPARAM lparam )
4599 WCHAR buffer[256];
4600 INT ret = TRUE;
4601 const DWORD *array;
4602 DWORD pos, i;
4604 if (!proc || !locale)
4606 SetLastError( ERROR_INVALID_PARAMETER );
4607 return FALSE;
4609 switch (flags & ~LOCALE_USE_CP_ACP)
4611 case 0:
4612 if (!get_locale_info( locale, 0, LOCALE_STIMEFORMAT, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4613 pos = locale->stimeformat;
4614 break;
4615 case TIME_NOSECONDS:
4616 if (!get_locale_info( locale, 0, LOCALE_SSHORTTIME, buffer, ARRAY_SIZE(buffer) )) return FALSE;
4617 pos = locale->sshorttime;
4618 break;
4619 default:
4620 FIXME( "Unknown time format %lx\n", flags );
4621 SetLastError( ERROR_INVALID_PARAMETER );
4622 return FALSE;
4625 array = (const DWORD *)(locale_strings + pos + 1);
4626 for (i = 0; ret && i < locale_strings[pos]; i++)
4628 if (i) memcpy( buffer, locale_strings + array[i] + 1,
4629 (locale_strings[array[i]] + 1) * sizeof(WCHAR) );
4631 if (ex) ret = ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
4632 else if (unicode) ret = proc( buffer );
4633 else
4635 char buffA[256];
4636 WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1,
4637 buffA, ARRAY_SIZE(buffA), NULL, NULL );
4638 ret = proc( (WCHAR *)buffA );
4641 return TRUE;
4645 /******************************************************************************
4646 * Internal_EnumUILanguages (kernelbase.@)
4648 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
4649 LONG_PTR param, BOOL unicode )
4651 WCHAR nameW[LOCALE_NAME_MAX_LENGTH];
4652 char nameA[LOCALE_NAME_MAX_LENGTH];
4653 DWORD i;
4655 if (!proc)
4657 SetLastError( ERROR_INVALID_PARAMETER );
4658 return FALSE;
4660 if (flags & ~(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))
4662 SetLastError( ERROR_INVALID_FLAGS );
4663 return FALSE;
4666 for (i = 0; i < locale_table->nb_lcnames; i++)
4668 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4669 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4670 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4671 if (SORTIDFROMLCID( lcnames_index[i].id )) continue; /* skip alternate sorts */
4672 if (flags & MUI_LANGUAGE_NAME)
4674 const WCHAR *str = locale_strings + lcnames_index[i].name;
4676 if (unicode)
4678 memcpy( nameW, str + 1, (*str + 1) * sizeof(WCHAR) );
4679 if (!proc( nameW, param )) break;
4681 else
4683 WideCharToMultiByte( CP_ACP, 0, str + 1, -1, nameA, sizeof(nameA), NULL, NULL );
4684 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
4687 else
4689 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4690 if (unicode)
4692 swprintf( nameW, ARRAY_SIZE(nameW), L"%04lx", lcnames_index[i].id );
4693 if (!proc( nameW, param )) break;
4695 else
4697 sprintf( nameA, "%04x", lcnames_index[i].id );
4698 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
4702 return TRUE;
4706 /******************************************************************************
4707 * CompareStringEx (kernelbase.@)
4709 INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1,
4710 const WCHAR *str2, int len2, NLSVERSIONINFO *version,
4711 void *reserved, LPARAM handle )
4713 const struct sortguid *sortid;
4714 const DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS |
4715 SORT_STRINGSORT | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH |
4716 NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE |
4717 LINGUISTIC_IGNOREDIACRITIC | SORT_DIGITSASNUMBERS |
4718 0x10000000 | LOCALE_USE_CP_ACP;
4719 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
4720 int ret;
4722 if (version) FIXME( "unexpected version parameter\n" );
4723 if (reserved) FIXME( "unexpected reserved value\n" );
4724 if (handle) FIXME( "unexpected handle\n" );
4726 if (flags & ~supported_flags)
4728 SetLastError( ERROR_INVALID_FLAGS );
4729 return 0;
4732 if (!(sortid = get_language_sort( locale ))) return 0;
4734 if (!str1 || !str2)
4736 SetLastError( ERROR_INVALID_PARAMETER );
4737 return 0;
4740 if (len1 < 0) len1 = lstrlenW(str1);
4741 if (len2 < 0) len2 = lstrlenW(str2);
4743 ret = compare_string( sortid, flags, str1, len1, str2, len2 );
4744 if (ret < 0) return CSTR_LESS_THAN;
4745 if (ret > 0) return CSTR_GREATER_THAN;
4746 return CSTR_EQUAL;
4750 /******************************************************************************
4751 * CompareStringA (kernelbase.@)
4753 INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1,
4754 const char *str2, int len2 )
4756 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
4757 WCHAR *buf2W = buf1W + 130;
4758 LPWSTR str1W, str2W;
4759 INT len1W = 0, len2W = 0, ret;
4760 UINT locale_cp = CP_ACP;
4762 if (!str1 || !str2)
4764 SetLastError( ERROR_INVALID_PARAMETER );
4765 return 0;
4768 if (flags & SORT_DIGITSASNUMBERS)
4770 SetLastError( ERROR_INVALID_FLAGS );
4771 return 0;
4774 if (len1 < 0) len1 = strlen(str1);
4775 if (len2 < 0) len2 = strlen(str2);
4777 locale_cp = get_lcid_codepage( lcid, flags );
4778 if (len1)
4780 if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 );
4781 if (len1W) str1W = buf1W;
4782 else
4784 len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 );
4785 str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) );
4786 if (!str1W)
4788 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4789 return 0;
4791 MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W );
4794 else
4796 len1W = 0;
4797 str1W = buf1W;
4800 if (len2)
4802 if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 );
4803 if (len2W) str2W = buf2W;
4804 else
4806 len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 );
4807 str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) );
4808 if (!str2W)
4810 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
4811 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4812 return 0;
4814 MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W );
4817 else
4819 len2W = 0;
4820 str2W = buf2W;
4823 ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W );
4825 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
4826 if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W );
4827 return ret;
4831 /******************************************************************************
4832 * CompareStringW (kernelbase.@)
4834 INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1,
4835 const WCHAR *str2, int len2 )
4837 const WCHAR *locale = LOCALE_NAME_USER_DEFAULT;
4838 const NLS_LOCALE_LCID_INDEX *entry;
4840 switch (lcid)
4842 case LOCALE_NEUTRAL:
4843 case LOCALE_USER_DEFAULT:
4844 case LOCALE_SYSTEM_DEFAULT:
4845 case LOCALE_CUSTOM_DEFAULT:
4846 case LOCALE_CUSTOM_UNSPECIFIED:
4847 case LOCALE_CUSTOM_UI_DEFAULT:
4848 break;
4849 default:
4850 if (lcid == user_lcid || lcid == system_lcid) break;
4851 if (!(entry = find_lcid_entry( lcid )))
4853 WARN( "unknown locale %04lx\n", lcid );
4854 SetLastError( ERROR_INVALID_PARAMETER );
4855 return 0;
4857 locale = locale_strings + entry->name + 1;
4858 break;
4861 return CompareStringEx( locale, flags, str1, len1, str2, len2, NULL, NULL, 0 );
4865 /******************************************************************************
4866 * CompareStringOrdinal (kernelbase.@)
4868 INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
4869 const WCHAR *str2, INT len2, BOOL ignore_case )
4871 int ret;
4873 if (!str1 || !str2)
4875 SetLastError( ERROR_INVALID_PARAMETER );
4876 return 0;
4878 if (len1 < 0) len1 = lstrlenW( str1 );
4879 if (len2 < 0) len2 = lstrlenW( str2 );
4881 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4882 if (ret < 0) return CSTR_LESS_THAN;
4883 if (ret > 0) return CSTR_GREATER_THAN;
4884 return CSTR_EQUAL;
4888 /******************************************************************************
4889 * ConvertDefaultLocale (kernelbase.@)
4891 LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
4893 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
4894 if (locale) lcid = locale->ilanguage;
4895 return lcid;
4899 /******************************************************************************
4900 * EnumCalendarInfoW (kernelbase.@)
4902 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
4903 CALID id, CALTYPE type )
4905 return Internal_EnumCalendarInfo( proc, NlsValidateLocale( &lcid, 0 ),
4906 id, type, TRUE, FALSE, FALSE, 0 );
4910 /******************************************************************************
4911 * EnumCalendarInfoExW (kernelbase.@)
4913 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
4914 CALID id, CALTYPE type )
4916 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4917 id, type, TRUE, TRUE, FALSE, 0 );
4920 /******************************************************************************
4921 * EnumCalendarInfoExEx (kernelbase.@)
4923 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
4924 LPCWSTR reserved, CALTYPE type, LPARAM lparam )
4926 LCID lcid;
4927 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4928 id, type, TRUE, TRUE, TRUE, lparam );
4932 /**************************************************************************
4933 * EnumDateFormatsW (kernelbase.@)
4935 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4937 return Internal_EnumDateFormats( proc, NlsValidateLocale( &lcid, 0 ),
4938 flags, TRUE, FALSE, FALSE, 0 );
4942 /**************************************************************************
4943 * EnumDateFormatsExW (kernelbase.@)
4945 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
4947 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ),
4948 flags, TRUE, TRUE, FALSE, 0 );
4952 /**************************************************************************
4953 * EnumDateFormatsExEx (kernelbase.@)
4955 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
4956 DWORD flags, LPARAM lparam )
4958 LCID lcid;
4959 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
4960 flags, TRUE, TRUE, TRUE, lparam );
4965 /******************************************************************************
4966 * EnumDynamicTimeZoneInformation (kernelbase.@)
4968 DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index,
4969 DYNAMIC_TIME_ZONE_INFORMATION *info )
4971 DYNAMIC_TIME_ZONE_INFORMATION tz;
4972 LSTATUS ret;
4973 DWORD size;
4975 if (!info) return ERROR_INVALID_PARAMETER;
4977 size = ARRAY_SIZE(tz.TimeZoneKeyName);
4978 ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL );
4979 if (ret) return ret;
4981 tz.DynamicDaylightTimeDisabled = TRUE;
4982 if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError();
4984 lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName );
4985 info->DynamicDaylightTimeDisabled = FALSE;
4986 return 0;
4990 /******************************************************************************
4991 * EnumLanguageGroupLocalesW (kernelbase.@)
4993 BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
4994 DWORD flags, LONG_PTR param )
4996 return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
5000 /******************************************************************************
5001 * EnumUILanguagesW (kernelbase.@)
5003 BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
5005 return Internal_EnumUILanguages( proc, flags, param, TRUE );
5009 /***********************************************************************
5010 * EnumSystemCodePagesW (kernelbase.@)
5012 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
5014 return Internal_EnumSystemCodePages( proc, flags, TRUE );
5018 /******************************************************************************
5019 * EnumSystemGeoID (kernelbase.@)
5021 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc )
5023 INT i;
5025 TRACE( "(%ld, %ld, %p)\n", class, parent, proc );
5027 if (!proc)
5029 SetLastError( ERROR_INVALID_PARAMETER );
5030 return FALSE;
5032 if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL)
5034 SetLastError( ERROR_INVALID_FLAGS );
5035 return FALSE;
5038 for (i = 0; i < geo_ids_count; i++)
5040 if (class != GEOCLASS_ALL && geo_ids[i].class != class) continue;
5041 if (parent && geo_ids[i].parent != parent) continue;
5042 if (!proc( geo_ids[i].id )) break;
5044 return TRUE;
5048 /******************************************************************************
5049 * EnumSystemLanguageGroupsW (kernelbase.@)
5051 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
5052 DWORD flags, LONG_PTR param )
5054 return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
5058 /******************************************************************************
5059 * EnumSystemLocalesA (kernelbase.@)
5061 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
5063 char name[10];
5064 DWORD i;
5066 for (i = 0; i < locale_table->nb_lcnames; i++)
5068 if (!lcnames_index[i].name) continue; /* skip invariant locale */
5069 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
5070 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
5071 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
5072 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
5073 continue; /* skip alternate sorts */
5074 sprintf( name, "%08x", lcnames_index[i].id );
5075 if (!proc( name )) break;
5077 return TRUE;
5081 /******************************************************************************
5082 * EnumSystemLocalesW (kernelbase.@)
5084 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
5086 WCHAR name[10];
5087 DWORD i;
5089 for (i = 0; i < locale_table->nb_lcnames; i++)
5091 if (!lcnames_index[i].name) continue; /* skip invariant locale */
5092 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
5093 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
5094 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
5095 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
5096 continue; /* skip alternate sorts */
5097 swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id );
5098 if (!proc( name )) break;
5100 return TRUE;
5104 /******************************************************************************
5105 * EnumSystemLocalesEx (kernelbase.@)
5107 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
5108 LPARAM param, void *reserved )
5110 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
5111 DWORD i, flags;
5113 if (reserved)
5115 SetLastError( ERROR_INVALID_PARAMETER );
5116 return FALSE;
5119 for (i = 0; i < locale_table->nb_lcnames; i++)
5121 const NLS_LOCALE_DATA *locale = get_locale_data( lcnames_index[i].idx );
5122 const WCHAR *str = locale_strings + lcnames_index[i].name;
5124 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
5125 memcpy( buffer, str + 1, (*str + 1) * sizeof(WCHAR) );
5126 if (SORTIDFROMLCID( lcnames_index[i].id ) || wcschr( str + 1, '_' ))
5127 flags = LOCALE_ALTERNATE_SORTS;
5128 else
5129 flags = LOCALE_WINDOWS | (locale->inotneutral ? LOCALE_SPECIFICDATA : LOCALE_NEUTRALDATA);
5130 if (wanted_flags && !(flags & wanted_flags)) continue;
5131 if (!proc( buffer, flags, param )) break;
5133 return TRUE;
5137 /**************************************************************************
5138 * EnumTimeFormatsW (kernelbase.@)
5140 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
5142 return Internal_EnumTimeFormats( proc, NlsValidateLocale( &lcid, 0 ), flags, TRUE, FALSE, 0 );
5146 /**************************************************************************
5147 * EnumTimeFormatsEx (kernelbase.@)
5149 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
5150 DWORD flags, LPARAM lparam )
5152 LCID lcid;
5153 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ),
5154 flags, TRUE, TRUE, lparam );
5158 /**************************************************************************
5159 * FindNLSString (kernelbase.@)
5161 INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src,
5162 int srclen, const WCHAR *value, int valuelen, int *found )
5164 const WCHAR *locale = LOCALE_NAME_USER_DEFAULT;
5165 const NLS_LOCALE_LCID_INDEX *entry;
5167 switch (lcid)
5169 case LOCALE_NEUTRAL:
5170 case LOCALE_USER_DEFAULT:
5171 case LOCALE_SYSTEM_DEFAULT:
5172 case LOCALE_CUSTOM_DEFAULT:
5173 case LOCALE_CUSTOM_UNSPECIFIED:
5174 case LOCALE_CUSTOM_UI_DEFAULT:
5175 break;
5176 default:
5177 if (lcid == user_lcid || lcid == system_lcid) break;
5178 if (!(entry = find_lcid_entry( lcid )))
5180 WARN( "unknown locale %04lx\n", lcid );
5181 SetLastError( ERROR_INVALID_PARAMETER );
5182 return 0;
5184 locale = locale_strings + entry->name + 1;
5185 break;
5188 return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 );
5192 /**************************************************************************
5193 * FindNLSStringEx (kernelbase.@)
5195 INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src,
5196 int srclen, const WCHAR *value, int valuelen, int *found,
5197 NLSVERSIONINFO *version, void *reserved, LPARAM handle )
5199 const struct sortguid *sortid;
5201 TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale), flags,
5202 wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found,
5203 version, reserved, handle );
5205 if (version) FIXME( "unexpected version parameter\n" );
5206 if (reserved) FIXME( "unexpected reserved value\n" );
5207 if (handle) FIXME( "unexpected handle\n" );
5209 if (!src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1)
5211 SetLastError( ERROR_INVALID_PARAMETER );
5212 return -1;
5214 if (!(sortid = get_language_sort( locale ))) return -1;
5216 if (srclen == -1) srclen = lstrlenW(src);
5217 if (valuelen == -1) valuelen = lstrlenW(value);
5219 return find_substring( sortid, flags, src, srclen, value, valuelen, found );
5223 /******************************************************************************
5224 * FindStringOrdinal (kernelbase.@)
5226 INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
5227 const WCHAR *val, INT val_size, BOOL ignore_case )
5229 INT offset, inc, count;
5231 TRACE( "%#lx %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
5232 wine_dbgstr_w(val), val_size, ignore_case );
5234 if (!src || !val)
5236 SetLastError( ERROR_INVALID_PARAMETER );
5237 return -1;
5240 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
5242 SetLastError( ERROR_INVALID_FLAGS );
5243 return -1;
5246 if (src_size == -1) src_size = lstrlenW( src );
5247 if (val_size == -1) val_size = lstrlenW( val );
5249 SetLastError( ERROR_SUCCESS );
5250 src_size -= val_size;
5251 if (src_size < 0) return -1;
5253 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
5254 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
5255 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
5256 while (count--)
5258 if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
5259 return offset;
5260 offset += inc;
5262 return -1;
5266 /******************************************************************************
5267 * FoldStringW (kernelbase.@)
5269 INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
5271 NTSTATUS status;
5272 WCHAR *buf = dst;
5273 int len = dstlen;
5275 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
5277 SetLastError( ERROR_INVALID_PARAMETER );
5278 return 0;
5280 if (srclen == -1) srclen = lstrlenW(src) + 1;
5282 if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE)))
5284 len = srclen * 4;
5285 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
5287 SetLastError( ERROR_OUTOFMEMORY );
5288 return 0;
5292 for (;;)
5294 status = fold_string( flags, src, srclen, buf, &len );
5295 if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf );
5296 if (status != STATUS_BUFFER_TOO_SMALL) break;
5297 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
5299 SetLastError( ERROR_OUTOFMEMORY );
5300 return 0;
5303 if (status == STATUS_INVALID_PARAMETER_1)
5305 SetLastError( ERROR_INVALID_FLAGS );
5306 return 0;
5308 if (!set_ntstatus( status )) return 0;
5310 if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
5311 return len;
5315 static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang,
5316 BOOL ansi, WCHAR **buffer )
5318 DWORD len;
5320 if (!(flags & FORMAT_MESSAGE_FROM_STRING))
5322 const MESSAGE_RESOURCE_ENTRY *entry;
5323 NTSTATUS status = STATUS_INVALID_PARAMETER;
5325 if (flags & FORMAT_MESSAGE_FROM_HMODULE)
5327 HMODULE module = (HMODULE)src;
5328 if (!module) module = GetModuleHandleW( 0 );
5329 status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry );
5331 if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
5333 /* Fold win32 hresult to its embedded error code. */
5334 if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32)
5335 id = HRESULT_CODE( id );
5336 status = RtlFindMessage( kernelbase_handle, RT_MESSAGETABLE, lang, id, &entry );
5338 if (!set_ntstatus( status )) return NULL;
5340 src = entry->Text;
5341 ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE);
5344 if (!ansi) return src;
5345 len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
5346 if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
5347 MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len );
5348 return *buffer;
5352 /***********************************************************************
5353 * FormatMessageA (kernelbase.@)
5355 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid,
5356 char *buffer, DWORD size, va_list *args )
5358 DWORD ret = 0;
5359 ULONG len, retsize = 0;
5360 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
5361 const WCHAR *src;
5362 WCHAR *result, *message = NULL;
5363 NTSTATUS status;
5365 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
5367 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
5369 if (!buffer)
5371 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5372 return 0;
5374 *(char **)buffer = NULL;
5376 if (size >= 32768)
5378 SetLastError( ERROR_INVALID_PARAMETER );
5379 return 0;
5382 if (width == 0xff) width = ~0u;
5384 if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0;
5386 if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 )))
5387 status = STATUS_NO_MEMORY;
5388 else
5389 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5390 TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
5391 result, 65536, &retsize );
5393 HeapFree( GetProcessHeap(), 0, message );
5395 if (status == STATUS_BUFFER_OVERFLOW)
5397 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5398 goto done;
5400 if (!set_ntstatus( status )) goto done;
5402 len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL );
5403 if (len <= 1)
5405 SetLastError( ERROR_NO_WORK_DONE );
5406 goto done;
5409 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
5411 char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len ));
5412 if (!buf)
5414 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5415 goto done;
5417 *(char **)buffer = buf;
5418 WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL );
5420 else if (len > size)
5422 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5423 goto done;
5425 else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL );
5427 ret = len - 1;
5429 done:
5430 HeapFree( GetProcessHeap(), 0, result );
5431 return ret;
5435 /***********************************************************************
5436 * FormatMessageW (kernelbase.@)
5438 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid,
5439 WCHAR *buffer, DWORD size, va_list *args )
5441 ULONG retsize = 0;
5442 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
5443 const WCHAR *src;
5444 WCHAR *message = NULL;
5445 NTSTATUS status;
5447 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
5449 if (!buffer)
5451 SetLastError( ERROR_INVALID_PARAMETER );
5452 return 0;
5455 if (width == 0xff) width = ~0u;
5457 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL;
5459 if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0;
5461 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
5463 WCHAR *result;
5464 va_list args_copy;
5465 ULONG alloc = max( size * sizeof(WCHAR), 65536 );
5467 for (;;)
5469 if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc )))
5471 status = STATUS_NO_MEMORY;
5472 break;
5474 if (args && !(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY))
5476 va_copy( args_copy, *args );
5477 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5478 FALSE, FALSE, &args_copy, result, alloc, &retsize );
5479 va_end( args_copy );
5481 else
5482 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5483 FALSE, TRUE, args, result, alloc, &retsize );
5485 if (!status)
5487 if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );
5488 else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
5489 result, max( retsize, size * sizeof(WCHAR) ));
5490 break;
5492 HeapFree( GetProcessHeap(), 0, result );
5493 if (status != STATUS_BUFFER_OVERFLOW) break;
5494 alloc *= 2;
5497 else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
5498 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
5499 buffer, size * sizeof(WCHAR), &retsize );
5501 HeapFree( GetProcessHeap(), 0, message );
5503 if (status == STATUS_BUFFER_OVERFLOW)
5505 if (size) buffer[size - 1] = 0;
5506 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5507 return 0;
5509 if (!set_ntstatus( status )) return 0;
5510 if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE );
5511 return retsize / sizeof(WCHAR) - 1;
5515 /******************************************************************************
5516 * GetACP (kernelbase.@)
5518 UINT WINAPI GetACP(void)
5520 return ansi_cpinfo.CodePage;
5524 /***********************************************************************
5525 * GetCPInfo (kernelbase.@)
5527 BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
5529 const CPTABLEINFO *table;
5531 if (!cpinfo || !(table = get_codepage_table( codepage )))
5533 SetLastError( ERROR_INVALID_PARAMETER );
5534 return FALSE;
5536 cpinfo->MaxCharSize = table->MaximumCharacterSize;
5537 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
5538 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
5539 return TRUE;
5543 /***********************************************************************
5544 * GetCPInfoExW (kernelbase.@)
5546 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
5548 const CPTABLEINFO *table;
5549 int min, max, pos;
5551 if (!cpinfo || !(table = get_codepage_table( codepage )))
5553 SetLastError( ERROR_INVALID_PARAMETER );
5554 return FALSE;
5556 cpinfo->MaxCharSize = table->MaximumCharacterSize;
5557 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
5558 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
5559 cpinfo->CodePage = table->CodePage;
5560 cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
5562 min = 0;
5563 max = ARRAY_SIZE(codepage_names) - 1;
5564 cpinfo->CodePageName[0] = 0;
5565 while (min <= max)
5567 pos = (min + max) / 2;
5568 if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
5569 else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
5570 else
5572 wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
5573 break;
5576 return TRUE;
5580 /***********************************************************************
5581 * GetCalendarInfoW (kernelbase.@)
5583 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
5584 WCHAR *buffer, INT len, DWORD *value )
5586 const NLS_LOCALE_DATA *locale;
5588 TRACE( "%04lx %lu 0x%lx %p %d %p\n", lcid, calendar, type, buffer, len, value );
5590 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5592 SetLastError( ERROR_INVALID_PARAMETER );
5593 return 0;
5595 return get_calendar_info( locale, calendar, type, buffer, len, value );
5599 /***********************************************************************
5600 * GetCalendarInfoEx (kernelbase.@)
5602 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *name, CALID calendar, const WCHAR *reserved,
5603 CALTYPE type, WCHAR *buffer, INT len, DWORD *value )
5605 LCID lcid;
5606 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5608 TRACE( "%s %lu 0x%lx %p %d %p\n", debugstr_w(name), calendar, type, buffer, len, value );
5610 if (!locale)
5612 SetLastError( ERROR_INVALID_PARAMETER );
5613 return 0;
5615 return get_calendar_info( locale, calendar, type, buffer, len, value );
5619 static CRITICAL_SECTION tzname_section;
5620 static CRITICAL_SECTION_DEBUG tzname_section_debug =
5622 0, 0, &tzname_section,
5623 { &tzname_section_debug.ProcessLocksList, &tzname_section_debug.ProcessLocksList },
5624 0, 0, { (DWORD_PTR)(__FILE__ ": tzname_section") }
5626 static CRITICAL_SECTION tzname_section = { &tzname_section_debug, -1, 0, 0, 0, 0 };
5627 static struct {
5628 LCID lcid;
5629 WCHAR key_name[128];
5630 WCHAR standard_name[32];
5631 WCHAR daylight_name[32];
5632 } cached_tzname;
5634 /***********************************************************************
5635 * GetDynamicTimeZoneInformation (kernelbase.@)
5637 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info )
5639 HKEY key;
5640 LARGE_INTEGER now;
5642 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info )))
5643 return TIME_ZONE_ID_INVALID;
5645 RtlEnterCriticalSection( &tzname_section );
5646 if (cached_tzname.lcid == GetThreadLocale() &&
5647 !wcscmp( info->TimeZoneKeyName, cached_tzname.key_name ))
5649 wcscpy( info->StandardName, cached_tzname.standard_name );
5650 wcscpy( info->DaylightName, cached_tzname.daylight_name );
5651 RtlLeaveCriticalSection( &tzname_section );
5653 else
5655 RtlLeaveCriticalSection( &tzname_section );
5656 if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))
5658 RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
5659 sizeof(info->StandardName), NULL, 0, system_dir );
5660 RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
5661 sizeof(info->DaylightName), NULL, 0, system_dir );
5662 RegCloseKey( key );
5664 else return TIME_ZONE_ID_INVALID;
5666 RtlEnterCriticalSection( &tzname_section );
5667 cached_tzname.lcid = GetThreadLocale();
5668 wcscpy( cached_tzname.key_name, info->TimeZoneKeyName );
5669 wcscpy( cached_tzname.standard_name, info->StandardName );
5670 wcscpy( cached_tzname.daylight_name, info->DaylightName );
5671 RtlLeaveCriticalSection( &tzname_section );
5674 NtQuerySystemTime( &now );
5675 return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE );
5679 /******************************************************************************
5680 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
5682 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info,
5683 DWORD *first, DWORD *last )
5685 HKEY key, dst_key = 0;
5686 DWORD type, count, ret = ERROR_FILE_NOT_FOUND;
5688 if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret;
5690 if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done;
5691 count = sizeof(DWORD);
5692 if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done;
5693 if (type != REG_DWORD) goto done;
5694 count = sizeof(DWORD);
5695 if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done;
5696 if (type != REG_DWORD) goto done;
5697 ret = 0;
5699 done:
5700 RegCloseKey( dst_key );
5701 RegCloseKey( key );
5702 return ret;
5706 /******************************************************************************
5707 * GetFileMUIInfo (kernelbase.@)
5709 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIInfo( DWORD flags, const WCHAR *path,
5710 FILEMUIINFO *info, DWORD *size )
5712 FIXME( "stub: %lu, %s, %p, %p\n", flags, debugstr_w(path), info, size );
5713 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
5714 return FALSE;
5718 /******************************************************************************
5719 * GetFileMUIPath (kernelbase.@)
5721 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags, const WCHAR *filepath,
5722 WCHAR *language, ULONG *languagelen,
5723 WCHAR *muipath, ULONG *muipathlen,
5724 ULONGLONG *enumerator )
5726 FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5727 debugstr_w(language), languagelen, muipath, muipathlen, enumerator );
5728 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
5729 return FALSE;
5733 /******************************************************************************
5734 * GetGeoInfoW (kernelbase.@)
5736 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang )
5738 const struct geo_id *ptr = find_geo_id_entry( id );
5740 TRACE( "%ld %ld %p %d %d\n", id, type, data, count, lang );
5742 if (!ptr)
5744 SetLastError( ERROR_INVALID_PARAMETER );
5745 return 0;
5747 return get_geo_info( ptr, type, data, count, lang );
5751 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoEx( WCHAR *location, GEOTYPE type, WCHAR *data, int data_count )
5753 const struct geo_id *ptr = find_geo_name_entry( location );
5755 TRACE( "%s %lx %p %d\n", wine_dbgstr_w(location), type, data, data_count );
5757 if (!ptr)
5759 SetLastError( ERROR_INVALID_PARAMETER );
5760 return 0;
5763 if (type == GEO_LCID || type == GEO_NATION || type == GEO_RFC1766)
5765 SetLastError( ERROR_INVALID_FLAGS );
5766 return 0;
5769 return get_geo_info( ptr, type, data, data_count, 0 );
5773 /******************************************************************************
5774 * GetLocaleInfoA (kernelbase.@)
5776 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
5778 const NLS_LOCALE_DATA *locale;
5779 WCHAR *bufferW;
5780 INT lenW, ret;
5782 TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid, lctype, buffer, len );
5784 if (len < 0 || (len && !buffer))
5786 SetLastError( ERROR_INVALID_PARAMETER );
5787 return 0;
5789 if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
5791 SetLastError( ERROR_INVALID_FLAGS );
5792 return 0;
5794 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5796 SetLastError( ERROR_INVALID_PARAMETER );
5797 return 0;
5799 if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
5801 ret = get_locale_info( locale, lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) );
5802 return ret * sizeof(WCHAR);
5805 if (!(lenW = get_locale_info( locale, lcid, lctype, NULL, 0 ))) return 0;
5807 if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
5809 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5810 return 0;
5812 ret = get_locale_info( locale, lcid, lctype, bufferW, lenW );
5813 if (ret) ret = WideCharToMultiByte( get_locale_codepage( locale, lctype ), 0,
5814 bufferW, ret, buffer, len, NULL, NULL );
5815 RtlFreeHeap( GetProcessHeap(), 0, bufferW );
5816 return ret;
5820 /******************************************************************************
5821 * GetLocaleInfoW (kernelbase.@)
5823 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
5825 const NLS_LOCALE_DATA *locale;
5827 if (len < 0 || (len && !buffer))
5829 SetLastError( ERROR_INVALID_PARAMETER );
5830 return 0;
5833 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
5835 if (!(locale = NlsValidateLocale( &lcid, 0 )))
5837 SetLastError( ERROR_INVALID_PARAMETER );
5838 return 0;
5840 return get_locale_info( locale, lcid, lctype, buffer, len );
5844 /******************************************************************************
5845 * GetLocaleInfoEx (kernelbase.@)
5847 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *name, LCTYPE info, WCHAR *buffer, INT len )
5849 LCID lcid;
5850 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5852 TRACE( "%s 0x%lx %p %d\n", debugstr_w(name), info, buffer, len );
5854 if (!locale)
5856 SetLastError( ERROR_INVALID_PARAMETER );
5857 return 0;
5859 return get_locale_info( locale, lcid, info, buffer, len );
5863 /******************************************************************************
5864 * GetNLSVersion (kernelbase.@)
5866 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info )
5868 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
5870 if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId ))
5872 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5873 return FALSE;
5875 if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES ))
5877 SetLastError( ERROR_INVALID_PARAMETER );
5878 return FALSE;
5880 return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info );
5884 /******************************************************************************
5885 * GetNLSVersionEx (kernelbase.@)
5887 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale,
5888 NLSVERSIONINFOEX *info )
5890 const struct sortguid *sortid;
5892 if (func != COMPARE_STRING)
5894 SetLastError( ERROR_INVALID_FLAGS );
5895 return FALSE;
5897 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5898 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5900 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5901 return FALSE;
5904 if (!(sortid = get_language_sort( locale ))) return FALSE;
5906 info->dwNLSVersion = info->dwDefinedVersion = sort.version;
5907 if (info->dwNLSVersionInfoSize >= sizeof(*info))
5909 info->dwEffectiveId = LocaleNameToLCID( locale, 0 );
5910 info->guidCustomVersion = sortid->id;
5912 return TRUE;
5916 /******************************************************************************
5917 * GetOEMCP (kernelbase.@)
5919 UINT WINAPI GetOEMCP(void)
5921 return oem_cpinfo.CodePage;
5925 /***********************************************************************
5926 * GetProcessPreferredUILanguages (kernelbase.@)
5928 BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPreferredUILanguages( DWORD flags, ULONG *count,
5929 WCHAR *buffer, ULONG *size )
5931 return set_ntstatus( RtlGetProcessPreferredUILanguages( flags, count, buffer, size ));
5935 /***********************************************************************
5936 * GetStringTypeA (kernelbase.@)
5938 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
5939 WORD *chartype )
5941 UINT cp;
5942 INT countW;
5943 LPWSTR srcW;
5944 BOOL ret = FALSE;
5946 if (count == -1) count = strlen(src) + 1;
5948 cp = get_lcid_codepage( locale, 0 );
5949 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
5950 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5952 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
5954 * NOTE: the target buffer has 1 word for each CHARACTER in the source
5955 * string, with multibyte characters there maybe be more bytes in count
5956 * than character space in the buffer!
5958 ret = GetStringTypeW(type, srcW, countW, chartype);
5959 HeapFree(GetProcessHeap(), 0, srcW);
5961 return ret;
5965 /***********************************************************************
5966 * GetStringTypeW (kernelbase.@)
5968 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
5970 if (!src)
5972 SetLastError( ERROR_INVALID_PARAMETER );
5973 return FALSE;
5975 if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3)
5977 SetLastError( ERROR_INVALID_PARAMETER );
5978 return FALSE;
5981 if (count == -1) count = lstrlenW(src) + 1;
5983 while (count--) *chartype++ = get_char_type( type, *src++ );
5985 return TRUE;
5989 /***********************************************************************
5990 * GetStringTypeExW (kernelbase.@)
5992 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
5993 WORD *chartype )
5995 /* locale is ignored for Unicode */
5996 return GetStringTypeW( type, src, count, chartype );
6000 /***********************************************************************
6001 * GetSystemDefaultLCID (kernelbase.@)
6003 LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
6005 return system_lcid;
6009 /***********************************************************************
6010 * GetSystemDefaultLangID (kernelbase.@)
6012 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
6014 return LANGIDFROMLCID( GetSystemDefaultLCID() );
6018 /***********************************************************************
6019 * GetSystemDefaultLocaleName (kernelbase.@)
6021 INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT count )
6023 return get_locale_info( system_locale, system_lcid, LOCALE_SNAME, name, count );
6027 /***********************************************************************
6028 * GetSystemDefaultUILanguage (kernelbase.@)
6030 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
6032 LANGID lang;
6033 NtQueryInstallUILanguage( &lang );
6034 return lang;
6038 /***********************************************************************
6039 * GetSystemPreferredUILanguages (kernelbase.@)
6041 BOOL WINAPI DECLSPEC_HOTPATCH GetSystemPreferredUILanguages( DWORD flags, ULONG *count,
6042 WCHAR *buffer, ULONG *size )
6044 return set_ntstatus( RtlGetSystemPreferredUILanguages( flags, 0, count, buffer, size ));
6048 /***********************************************************************
6049 * GetThreadPreferredUILanguages (kernelbase.@)
6051 BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPreferredUILanguages( DWORD flags, ULONG *count,
6052 WCHAR *buffer, ULONG *size )
6054 return set_ntstatus( RtlGetThreadPreferredUILanguages( flags, count, buffer, size ));
6058 /***********************************************************************
6059 * GetTimeZoneInformation (kernelbase.@)
6061 DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info )
6063 DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
6064 DWORD ret = GetDynamicTimeZoneInformation( &tzinfo );
6066 memcpy( info, &tzinfo, sizeof(*info) );
6067 return ret;
6071 /***********************************************************************
6072 * GetTimeZoneInformationForYear (kernelbase.@)
6074 BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year,
6075 DYNAMIC_TIME_ZONE_INFORMATION *dynamic,
6076 TIME_ZONE_INFORMATION *info )
6078 DYNAMIC_TIME_ZONE_INFORMATION local_info;
6079 HKEY key = 0, dst_key;
6080 DWORD count;
6081 LRESULT ret;
6082 struct
6084 LONG bias;
6085 LONG std_bias;
6086 LONG dlt_bias;
6087 SYSTEMTIME std_date;
6088 SYSTEMTIME dlt_date;
6089 } data;
6091 TRACE( "(%u,%p)\n", year, info );
6093 if (!dynamic)
6095 if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE;
6096 dynamic = &local_info;
6099 if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done;
6100 if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
6101 sizeof(info->StandardName), NULL, 0, system_dir ))
6103 count = sizeof(info->StandardName);
6104 if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count )))
6105 goto done;
6107 if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
6108 sizeof(info->DaylightName), NULL, 0, system_dir ))
6110 count = sizeof(info->DaylightName);
6111 if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count )))
6112 goto done;
6115 ret = ERROR_FILE_NOT_FOUND;
6116 if (!dynamic->DynamicDaylightTimeDisabled &&
6117 !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key ))
6119 WCHAR yearW[16];
6120 swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year );
6121 count = sizeof(data);
6122 ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count );
6123 RegCloseKey( dst_key );
6125 if (ret)
6127 count = sizeof(data);
6128 ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count );
6131 if (!ret)
6133 info->Bias = data.bias;
6134 info->StandardBias = data.std_bias;
6135 info->DaylightBias = data.dlt_bias;
6136 info->StandardDate = data.std_date;
6137 info->DaylightDate = data.dlt_date;
6140 done:
6141 RegCloseKey( key );
6142 if (ret) SetLastError( ret );
6143 return !ret;
6147 /***********************************************************************
6148 * GetUserDefaultLCID (kernelbase.@)
6150 LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
6152 return user_lcid;
6156 /***********************************************************************
6157 * GetUserDefaultLangID (kernelbase.@)
6159 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
6161 return LANGIDFROMLCID( GetUserDefaultLCID() );
6165 /***********************************************************************
6166 * GetUserDefaultLocaleName (kernelbase.@)
6168 INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
6170 return get_locale_info( user_locale, user_lcid, LOCALE_SNAME, name, len );
6174 /***********************************************************************
6175 * GetUserDefaultUILanguage (kernelbase.@)
6177 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
6179 return LANGIDFROMLCID( GetUserDefaultLCID() );
6183 /******************************************************************************
6184 * GetUserGeoID (kernelbase.@)
6186 GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass )
6188 GEOID ret = 39070;
6189 const WCHAR *name;
6190 WCHAR bufferW[40];
6191 HKEY hkey;
6193 switch (geoclass)
6195 case GEOCLASS_NATION:
6196 name = L"Nation";
6197 break;
6198 case GEOCLASS_REGION:
6199 name = L"Region";
6200 break;
6201 default:
6202 WARN("Unknown geoclass %ld\n", geoclass);
6203 return GEOID_NOT_AVAILABLE;
6205 if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey ))
6207 DWORD count = sizeof(bufferW);
6208 if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count ))
6209 ret = wcstol( bufferW, NULL, 10 );
6210 RegCloseKey( hkey );
6212 return ret;
6216 /******************************************************************************
6217 * GetUserPreferredUILanguages (kernelbase.@)
6219 BOOL WINAPI DECLSPEC_HOTPATCH GetUserPreferredUILanguages( DWORD flags, ULONG *count,
6220 WCHAR *buffer, ULONG *size )
6222 return set_ntstatus( RtlGetUserPreferredUILanguages( flags, 0, count, buffer, size ));
6226 /******************************************************************************
6227 * IdnToAscii (kernelbase.@)
6229 INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen,
6230 WCHAR *dst, INT dstlen )
6232 NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
6233 if (!set_ntstatus( status )) return 0;
6234 return dstlen;
6238 /******************************************************************************
6239 * IdnToNameprepUnicode (kernelbase.@)
6241 INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
6242 WCHAR *dst, INT dstlen )
6244 NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen );
6245 if (!set_ntstatus( status )) return 0;
6246 return dstlen;
6250 /******************************************************************************
6251 * IdnToUnicode (kernelbase.@)
6253 INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen,
6254 WCHAR *dst, INT dstlen )
6256 NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen );
6257 if (!set_ntstatus( status )) return 0;
6258 return dstlen;
6262 /******************************************************************************
6263 * IsCharAlphaA (kernelbase.@)
6265 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
6267 WCHAR wc;
6268 DWORD reslen;
6269 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6270 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
6274 /******************************************************************************
6275 * IsCharAlphaW (kernelbase.@)
6277 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
6279 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
6283 /******************************************************************************
6284 * IsCharAlphaNumericA (kernelbase.@)
6286 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
6288 WCHAR wc;
6289 DWORD reslen;
6290 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6291 return reslen && (get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
6295 /******************************************************************************
6296 * IsCharAlphaNumericW (kernelbase.@)
6298 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
6300 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
6304 /******************************************************************************
6305 * IsCharBlankW (kernelbase.@)
6307 BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
6309 return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK);
6313 /******************************************************************************
6314 * IsCharCntrlW (kernelbase.@)
6316 BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
6318 return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL);
6322 /******************************************************************************
6323 * IsCharDigitW (kernelbase.@)
6325 BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
6327 return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT);
6331 /******************************************************************************
6332 * IsCharLowerA (kernelbase.@)
6334 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
6336 WCHAR wc;
6337 DWORD reslen;
6338 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6339 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
6343 /******************************************************************************
6344 * IsCharLowerW (kernelbase.@)
6346 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
6348 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
6352 /******************************************************************************
6353 * IsCharPunctW (kernelbase.@)
6355 BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
6357 return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT);
6361 /******************************************************************************
6362 * IsCharSpaceA (kernelbase.@)
6364 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
6366 WCHAR wc;
6367 DWORD reslen;
6368 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6369 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
6373 /******************************************************************************
6374 * IsCharSpaceW (kernelbase.@)
6376 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
6378 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
6382 /******************************************************************************
6383 * IsCharUpperA (kernelbase.@)
6385 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
6387 WCHAR wc;
6388 DWORD reslen;
6389 RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 );
6390 return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
6394 /******************************************************************************
6395 * IsCharUpperW (kernelbase.@)
6397 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
6399 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
6403 /******************************************************************************
6404 * IsCharXDigitW (kernelbase.@)
6406 BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
6408 return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT);
6412 /******************************************************************************
6413 * IsDBCSLeadByte (kernelbase.@)
6415 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
6417 return ansi_cpinfo.DBCSCodePage && ansi_cpinfo.DBCSOffsets[testchar];
6421 /******************************************************************************
6422 * IsDBCSLeadByteEx (kernelbase.@)
6424 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
6426 const CPTABLEINFO *table = get_codepage_table( codepage );
6427 return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
6431 /******************************************************************************
6432 * IsNormalizedString (kernelbase.@)
6434 BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
6436 BOOLEAN res;
6437 if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
6438 return res;
6442 /******************************************************************************
6443 * IsValidCodePage (kernelbase.@)
6445 BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
6447 switch (codepage)
6449 case CP_ACP:
6450 case CP_OEMCP:
6451 case CP_MACCP:
6452 case CP_THREAD_ACP:
6453 return FALSE;
6454 default:
6455 return get_codepage_table( codepage ) != NULL;
6460 /******************************************************************************
6461 * IsValidLanguageGroup (kernelbase.@)
6463 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
6465 WCHAR name[10], value[10];
6466 DWORD type, value_len = sizeof(value);
6467 BOOL ret = FALSE;
6468 HKEY key;
6470 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
6472 swprintf( name, ARRAY_SIZE(name), L"%x", id );
6473 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
6474 ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
6476 RegCloseKey( key );
6477 return ret;
6481 /******************************************************************************
6482 * IsValidLocale (kernelbase.@)
6484 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
6486 switch (lcid)
6488 case LOCALE_NEUTRAL:
6489 case LOCALE_USER_DEFAULT:
6490 case LOCALE_SYSTEM_DEFAULT:
6491 return FALSE;
6492 default:
6493 return !!NlsValidateLocale( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES );
6498 /******************************************************************************
6499 * IsValidLocaleName (kernelbase.@)
6501 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
6503 if (locale == LOCALE_NAME_USER_DEFAULT) return FALSE;
6504 return !!find_lcname_entry( locale );
6508 /******************************************************************************
6509 * IsNLSDefinedString (kernelbase.@)
6511 BOOL WINAPI DECLSPEC_HOTPATCH IsNLSDefinedString( NLS_FUNCTION func, DWORD flags, NLSVERSIONINFO *info,
6512 const WCHAR *str, int len )
6514 int i;
6516 if (func != COMPARE_STRING)
6518 SetLastError( ERROR_INVALID_FLAGS );
6519 return FALSE;
6521 if (info)
6523 if (info->dwNLSVersionInfoSize != sizeof(*info) &&
6524 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
6526 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6527 return FALSE;
6531 if (len < 0) len = lstrlenW( str ) + 1;
6533 for (i = 0; i < len; i++)
6535 if (is_private_use_area_char( str[i] )) return FALSE;
6536 if (IS_LOW_SURROGATE( str[i] )) return FALSE;
6537 if (IS_HIGH_SURROGATE( str[i] ))
6539 if (++i == len) return FALSE;
6540 if (!IS_LOW_SURROGATE( str[i] )) return FALSE;
6541 continue;
6543 if (!(get_char_type( CT_CTYPE1, str[i] ) & C1_DEFINED)) return FALSE;
6545 return TRUE;
6549 /******************************************************************************
6550 * IsValidNLSVersion (kernelbase.@)
6552 DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale,
6553 NLSVERSIONINFOEX *info )
6555 static const GUID GUID_NULL;
6556 NLSVERSIONINFOEX infoex;
6557 DWORD ret;
6559 if (func != COMPARE_STRING)
6561 SetLastError( ERROR_INVALID_PARAMETER );
6562 return FALSE;
6564 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
6565 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
6567 SetLastError( ERROR_INVALID_PARAMETER );
6568 return FALSE;
6570 infoex.dwNLSVersionInfoSize = sizeof(infoex);
6571 if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE;
6573 ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff);
6574 if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL ))
6575 ret = find_sortguid( &info->guidCustomVersion ) != NULL;
6577 if (!ret) SetLastError( ERROR_SUCCESS );
6578 return ret;
6582 /***********************************************************************
6583 * LCIDToLocaleName (kernelbase.@)
6585 INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
6587 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, flags );
6589 if (!locale)
6591 SetLastError( ERROR_INVALID_PARAMETER );
6592 return 0;
6594 return get_locale_info( locale, lcid, LOCALE_SNAME, name, count );
6598 /***********************************************************************
6599 * LCMapStringEx (kernelbase.@)
6601 INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
6602 WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
6603 void *reserved, LPARAM handle )
6605 const struct sortguid *sortid = NULL;
6607 if (version) FIXME( "unsupported version structure %p\n", version );
6608 if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
6609 if (handle)
6611 static int once;
6612 if (!once++) FIXME( "unsupported lparam %Ix\n", handle );
6615 if (!src || !srclen || dstlen < 0)
6617 SetLastError( ERROR_INVALID_PARAMETER );
6618 return 0;
6621 if (srclen < 0) srclen = lstrlenW(src) + 1;
6623 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
6624 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
6626 flags &= ~LOCALE_USE_CP_ACP;
6628 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
6630 SetLastError( ERROR_INVALID_FLAGS );
6631 return 0;
6633 if (flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_SORTKEY))
6635 if (!(sortid = get_language_sort( locale ))) return 0;
6637 if (flags & LCMAP_HASH)
6639 FIXME( "LCMAP_HASH %s not supported\n", debugstr_wn( src, srclen ));
6640 return 0;
6642 if (flags & LCMAP_SORTHANDLE)
6644 FIXME( "LCMAP_SORTHANDLE not supported\n" );
6645 return 0;
6647 if (flags & LCMAP_SORTKEY) return get_sortkey( sortid, flags, src, srclen, (BYTE *)dst, dstlen );
6649 return lcmap_string( sortid, flags, src, srclen, dst, dstlen );
6653 /***********************************************************************
6654 * LCMapStringA (kernelbase.@)
6656 INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
6657 char *dst, int dstlen )
6659 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
6660 LPWSTR srcW, dstW;
6661 INT ret = 0, srclenW, dstlenW;
6662 UINT locale_cp = CP_ACP;
6664 if (!src || !srclen || dstlen < 0)
6666 SetLastError( ERROR_INVALID_PARAMETER );
6667 return 0;
6670 locale_cp = get_lcid_codepage( lcid, flags );
6672 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
6673 if (srclenW) srcW = bufW;
6674 else
6676 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
6677 srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
6678 if (!srcW)
6680 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6681 return 0;
6683 MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
6686 if (flags & LCMAP_SORTKEY)
6688 if (src == dst)
6690 SetLastError( ERROR_INVALID_FLAGS );
6691 goto done;
6693 ret = LCMapStringW( lcid, flags, srcW, srclenW, (WCHAR *)dst, dstlen );
6694 goto done;
6697 if (flags & SORT_STRINGSORT)
6699 SetLastError( ERROR_INVALID_FLAGS );
6700 goto done;
6703 dstlenW = LCMapStringW( lcid, flags, srcW, srclenW, NULL, 0 );
6704 if (!dstlenW) goto done;
6706 dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
6707 if (!dstW)
6709 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6710 goto done;
6712 LCMapStringW( lcid, flags, srcW, srclenW, dstW, dstlenW );
6713 ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
6714 HeapFree( GetProcessHeap(), 0, dstW );
6716 done:
6717 if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
6718 return ret;
6722 /***********************************************************************
6723 * LCMapStringW (kernelbase.@)
6725 INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
6726 WCHAR *dst, int dstlen )
6728 const WCHAR *locale = LOCALE_NAME_USER_DEFAULT;
6729 const NLS_LOCALE_LCID_INDEX *entry;
6731 switch (lcid)
6733 case LOCALE_NEUTRAL:
6734 case LOCALE_USER_DEFAULT:
6735 case LOCALE_SYSTEM_DEFAULT:
6736 case LOCALE_CUSTOM_DEFAULT:
6737 case LOCALE_CUSTOM_UNSPECIFIED:
6738 case LOCALE_CUSTOM_UI_DEFAULT:
6739 break;
6740 default:
6741 if (lcid == user_lcid || lcid == system_lcid) break;
6742 if (!(entry = find_lcid_entry( lcid )))
6744 WARN( "unknown locale %04lx\n", lcid );
6745 SetLastError( ERROR_INVALID_PARAMETER );
6746 return 0;
6748 locale = locale_strings + entry->name + 1;
6749 break;
6752 return LCMapStringEx( locale, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
6756 /***********************************************************************
6757 * LocaleNameToLCID (kernelbase.@)
6759 LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
6761 LCID lcid;
6762 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6764 if (!locale)
6766 SetLastError( ERROR_INVALID_PARAMETER );
6767 return 0;
6769 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
6770 lcid = locale->idefaultlanguage;
6771 return lcid;
6775 /******************************************************************************
6776 * MultiByteToWideChar (kernelbase.@)
6778 INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
6779 WCHAR *dst, INT dstlen )
6781 const CPTABLEINFO *info;
6782 int ret;
6784 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6786 SetLastError( ERROR_INVALID_PARAMETER );
6787 return 0;
6789 if (srclen < 0) srclen = strlen(src) + 1;
6791 switch (codepage)
6793 case CP_SYMBOL:
6794 ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
6795 break;
6796 case CP_UTF7:
6797 ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
6798 break;
6799 case CP_UNIXCP:
6800 codepage = unix_cp;
6801 /* fall through */
6802 default:
6803 if (!(info = get_codepage_table( codepage )))
6805 SetLastError( ERROR_INVALID_PARAMETER );
6806 return 0;
6808 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
6810 SetLastError( ERROR_INVALID_FLAGS );
6811 return 0;
6813 if (info->CodePage == CP_UTF8)
6814 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6815 else
6816 ret = mbstowcs_codepage( info, flags, src, srclen, dst, dstlen );
6817 break;
6819 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
6820 return ret;
6824 /******************************************************************************
6825 * NormalizeString (kernelbase.@)
6827 INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
6828 WCHAR *dst, INT dst_len)
6830 NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len );
6832 switch (status)
6834 case STATUS_OBJECT_NAME_NOT_FOUND:
6835 status = STATUS_INVALID_PARAMETER;
6836 break;
6837 case STATUS_BUFFER_TOO_SMALL:
6838 case STATUS_NO_UNICODE_TRANSLATION:
6839 dst_len = -dst_len;
6840 break;
6842 SetLastError( RtlNtStatusToDosError( status ));
6843 return dst_len;
6847 /******************************************************************************
6848 * ResolveLocaleName (kernelbase.@)
6850 INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
6852 LCID lcid;
6853 UINT pos, datalen;
6854 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6856 if (!locale)
6858 static const WCHAR valid[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
6859 WCHAR *p, tmp[LOCALE_NAME_MAX_LENGTH];
6861 if (wcsspn( name, valid ) < wcslen( name ))
6863 SetLastError( ERROR_INVALID_PARAMETER );
6864 return 0;
6866 lstrcpynW( tmp, name, LOCALE_NAME_MAX_LENGTH );
6867 while (!locale)
6869 for (p = tmp + wcslen(tmp) - 1; p >= tmp; p--) if (*p == '-' || *p == '_') break;
6870 if (p <= tmp) break;
6871 *p = 0;
6872 locale = get_locale_by_name( tmp, &lcid );
6876 pos = locale ? (locale->inotneutral ? locale->sname : locale->ssortlocale) : 0;
6877 datalen = locale_strings[pos] + 1;
6879 if (!len) return datalen;
6880 lstrcpynW( buffer, locale_strings + pos + 1, len );
6881 if (datalen > len)
6883 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6884 return 0;
6886 return datalen;
6890 /******************************************************************************
6891 * SetLocaleInfoW (kernelbase.@)
6893 BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
6895 WCHAR *str, tmp[80];
6897 if (!data)
6899 SetLastError( ERROR_INVALID_PARAMETER );
6900 return FALSE;
6903 switch (LOWORD(lctype))
6905 case LOCALE_ICALENDARTYPE: return set_registry_entry( &entry_icalendartype, data );
6906 case LOCALE_ICURRDIGITS: return set_registry_entry( &entry_icurrdigits, data );
6907 case LOCALE_ICURRENCY: return set_registry_entry( &entry_icurrency, data );
6908 case LOCALE_IDIGITS: return set_registry_entry( &entry_idigits, data );
6909 case LOCALE_IDIGITSUBSTITUTION: return set_registry_entry( &entry_idigitsubstitution, data );
6910 case LOCALE_IFIRSTDAYOFWEEK: return set_registry_entry( &entry_ifirstdayofweek, data );
6911 case LOCALE_IFIRSTWEEKOFYEAR: return set_registry_entry( &entry_ifirstweekofyear, data );
6912 case LOCALE_ILZERO: return set_registry_entry( &entry_ilzero, data );
6913 case LOCALE_IMEASURE: return set_registry_entry( &entry_imeasure, data );
6914 case LOCALE_INEGCURR: return set_registry_entry( &entry_inegcurr, data );
6915 case LOCALE_INEGNUMBER: return set_registry_entry( &entry_inegnumber, data );
6916 case LOCALE_IPAPERSIZE: return set_registry_entry( &entry_ipapersize, data );
6917 case LOCALE_S1159: return set_registry_entry( &entry_s1159, data );
6918 case LOCALE_S2359: return set_registry_entry( &entry_s2359, data );
6919 case LOCALE_SCURRENCY: return set_registry_entry( &entry_scurrency, data );
6920 case LOCALE_SDECIMAL: return set_registry_entry( &entry_sdecimal, data );
6921 case LOCALE_SGROUPING: return set_registry_entry( &entry_sgrouping, data );
6922 case LOCALE_SLIST: return set_registry_entry( &entry_slist, data );
6923 case LOCALE_SLONGDATE: return set_registry_entry( &entry_slongdate, data );
6924 case LOCALE_SMONDECIMALSEP: return set_registry_entry( &entry_smondecimalsep, data );
6925 case LOCALE_SMONGROUPING: return set_registry_entry( &entry_smongrouping, data );
6926 case LOCALE_SMONTHOUSANDSEP: return set_registry_entry( &entry_smonthousandsep, data );
6927 case LOCALE_SNATIVEDIGITS: return set_registry_entry( &entry_snativedigits, data );
6928 case LOCALE_SNEGATIVESIGN: return set_registry_entry( &entry_snegativesign, data );
6929 case LOCALE_SPOSITIVESIGN: return set_registry_entry( &entry_spositivesign, data );
6930 case LOCALE_SSHORTTIME: return set_registry_entry( &entry_sshorttime, data );
6931 case LOCALE_STHOUSAND: return set_registry_entry( &entry_sthousand, data );
6932 case LOCALE_SYEARMONTH: return set_registry_entry( &entry_syearmonth, data );
6934 case LOCALE_SDATE:
6935 if (!get_locale_info( user_locale, user_lcid, LOCALE_SSHORTDATE, tmp, ARRAY_SIZE(tmp) )) break;
6936 data = locale_replace_separator( tmp, data );
6937 /* fall through */
6938 case LOCALE_SSHORTDATE:
6939 if (!set_registry_entry( &entry_sshortdate, data )) return FALSE;
6940 update_registry_value( LOCALE_IDATE, NULL, L"iDate" );
6941 update_registry_value( LOCALE_SDATE, NULL, L"sDate" );
6942 return TRUE;
6944 case LOCALE_STIME:
6945 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6946 data = locale_replace_separator( tmp, data );
6947 /* fall through */
6948 case LOCALE_STIMEFORMAT:
6949 if (!set_registry_entry( &entry_stimeformat, data )) return FALSE;
6950 update_registry_value( LOCALE_ITIME, NULL, L"iTime" );
6951 update_registry_value( LOCALE_ITIMEMARKPOSN, NULL, L"iTimePrefix" );
6952 update_registry_value( LOCALE_ITLZERO, NULL, L"iTLZero" );
6953 update_registry_value( LOCALE_STIME, NULL, L"sTime" );
6954 return TRUE;
6956 case LOCALE_ITIME:
6957 if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break;
6958 if (!(str = find_format( tmp, L"Hh" ))) break;
6959 while (*str == 'h' || *str == 'H') *str++ = (*data == '0' ? 'h' : 'H');
6960 if (!set_registry_entry( &entry_stimeformat, tmp )) break;
6961 update_registry_value( LOCALE_ITIME, NULL, L"iTime" );
6962 return TRUE;
6964 case LOCALE_SINTLSYMBOL:
6965 if (!set_registry_entry( &entry_sintlsymbol, data )) return FALSE;
6966 /* if restoring the original value, restore the original LOCALE_SCURRENCY as well */
6967 if (!wcsicmp( data, locale_strings + user_locale->sintlsymbol + 1 ))
6968 data = locale_strings + user_locale->scurrency + 1;
6969 set_registry_entry( &entry_scurrency, data );
6970 return TRUE;
6972 SetLastError( ERROR_INVALID_FLAGS );
6973 return FALSE;
6977 /***********************************************************************
6978 * SetCalendarInfoW (kernelbase.@)
6980 INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
6982 FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
6983 return 0;
6987 /***********************************************************************
6988 * SetProcessPreferredUILanguages (kernelbase.@)
6990 BOOL WINAPI DECLSPEC_HOTPATCH SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6992 return set_ntstatus( RtlSetProcessPreferredUILanguages( flags, buffer, count ));
6996 /***********************************************************************
6997 * SetThreadPreferredUILanguages (kernelbase.@)
6999 BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
7001 return set_ntstatus( RtlSetThreadPreferredUILanguages( flags, buffer, count ));
7005 /***********************************************************************
7006 * SetTimeZoneInformation (kernelbase.@)
7008 BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info )
7010 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info ));
7014 /******************************************************************************
7015 * SetUserGeoID (kernelbase.@)
7017 BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id )
7019 const struct geo_id *geo = find_geo_id_entry( id );
7020 WCHAR bufferW[10];
7021 HKEY hkey;
7023 if (!geo)
7025 SetLastError( ERROR_INVALID_PARAMETER );
7026 return FALSE;
7028 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
7030 const WCHAR *name = geo->class == GEOCLASS_NATION ? L"Nation" : L"Region";
7031 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geo->id );
7032 RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
7034 if (geo->class == GEOCLASS_NATION || wcscmp( geo->iso2, L"XX" ))
7035 lstrcpyW( bufferW, geo->iso2 );
7036 else
7037 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", geo->uncode );
7038 RegSetValueExW( hkey, L"Name", 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
7039 RegCloseKey( hkey );
7041 return TRUE;
7045 /***********************************************************************
7046 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
7048 BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info,
7049 const SYSTEMTIME *system,
7050 SYSTEMTIME *local )
7052 TIME_ZONE_INFORMATION tzinfo;
7053 LARGE_INTEGER ft;
7055 if (!info)
7057 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
7058 info = &tzinfo;
7061 if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE;
7062 switch (get_timezone_id( info, ft, FALSE ))
7064 case TIME_ZONE_ID_UNKNOWN:
7065 ft.QuadPart -= info->Bias * (LONGLONG)600000000;
7066 break;
7067 case TIME_ZONE_ID_STANDARD:
7068 ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000;
7069 break;
7070 case TIME_ZONE_ID_DAYLIGHT:
7071 ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
7072 break;
7073 default:
7074 return FALSE;
7076 return FileTimeToSystemTime( (FILETIME *)&ft, local );
7080 /***********************************************************************
7081 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
7083 BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info,
7084 const SYSTEMTIME *local,
7085 SYSTEMTIME *system )
7087 TIME_ZONE_INFORMATION tzinfo;
7088 LARGE_INTEGER ft;
7090 if (!info)
7092 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
7093 info = &tzinfo;
7096 if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE;
7097 switch (get_timezone_id( info, ft, TRUE ))
7099 case TIME_ZONE_ID_UNKNOWN:
7100 ft.QuadPart += info->Bias * (LONGLONG)600000000;
7101 break;
7102 case TIME_ZONE_ID_STANDARD:
7103 ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000;
7104 break;
7105 case TIME_ZONE_ID_DAYLIGHT:
7106 ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
7107 break;
7108 default:
7109 return FALSE;
7111 return FileTimeToSystemTime( (FILETIME *)&ft, system );
7115 /***********************************************************************
7116 * VerLanguageNameA (kernelbase.@)
7118 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
7120 return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
7124 /***********************************************************************
7125 * VerLanguageNameW (kernelbase.@)
7127 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
7129 return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
7133 /***********************************************************************
7134 * WideCharToMultiByte (kernelbase.@)
7136 INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
7137 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
7139 const CPTABLEINFO *info;
7140 int ret;
7142 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
7144 SetLastError( ERROR_INVALID_PARAMETER );
7145 return 0;
7148 if (srclen < 0) srclen = lstrlenW(src) + 1;
7150 switch (codepage)
7152 case CP_SYMBOL:
7153 ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
7154 break;
7155 case CP_UTF7:
7156 ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
7157 break;
7158 case CP_UNIXCP:
7159 codepage = unix_cp;
7160 /* fall through */
7161 default:
7162 if (!(info = get_codepage_table( codepage )))
7164 SetLastError( ERROR_INVALID_PARAMETER );
7165 return 0;
7167 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
7168 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
7170 SetLastError( ERROR_INVALID_FLAGS );
7171 return 0;
7173 if (info->CodePage == CP_UTF8)
7174 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
7175 else
7176 ret = wcstombs_codepage( info, flags, src, srclen, dst, dstlen, defchar, used );
7177 break;
7179 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
7180 return ret;
7184 /***********************************************************************
7185 * GetUserDefaultGeoName (kernelbase.@)
7187 INT WINAPI GetUserDefaultGeoName(LPWSTR geo_name, int count)
7189 WCHAR buffer[32];
7190 LSTATUS status;
7191 DWORD size;
7192 HKEY key;
7194 TRACE( "geo_name %p, count %d.\n", geo_name, count );
7196 if (count && !geo_name)
7198 SetLastError( ERROR_INVALID_PARAMETER );
7199 return 0;
7201 if (!(status = RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &key )))
7203 size = sizeof(buffer);
7204 status = RegQueryValueExW( key, L"Name", NULL, NULL, (BYTE *)buffer, &size );
7205 RegCloseKey( key );
7207 if (status)
7209 const struct geo_id *geo = find_geo_id_entry( GetUserGeoID( GEOCLASS_NATION ));
7210 if (geo && geo->id != 39070)
7211 lstrcpyW( buffer, geo->iso2 );
7212 else
7213 lstrcpyW( buffer, L"001" );
7215 size = lstrlenW( buffer ) + 1;
7216 if (count < size)
7218 if (!count)
7219 return size;
7220 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7221 return 0;
7223 lstrcpyW( geo_name, buffer );
7224 return size;
7228 /***********************************************************************
7229 * SetUserDefaultGeoName (kernelbase.@)
7231 BOOL WINAPI SetUserGeoName(PWSTR geo_name)
7233 const struct geo_id *geo;
7235 TRACE( "geo_name %s.\n", debugstr_w( geo_name ));
7237 if (!geo_name || !(geo = find_geo_name_entry( geo_name )))
7239 SetLastError( ERROR_INVALID_PARAMETER );
7240 return FALSE;
7242 return SetUserGeoID( geo->id );
7246 static void grouping_to_string( UINT grouping, WCHAR *buffer )
7248 UINT last_digit = grouping % 10;
7249 WCHAR tmp[10], *p = tmp;
7251 /* The string is confusingly different when it comes to repetitions (trailing zeros). For a string,
7252 * a 0 signals that the format needs to be repeated, which is the opposite of the grouping integer. */
7253 if (last_digit == 0)
7255 grouping /= 10;
7257 /* Special case: two or more trailing zeros result in zero-sided groupings, with no repeats */
7258 if (grouping % 10 == 0)
7259 last_digit = ~0;
7262 while (grouping)
7264 *p++ = '0' + grouping % 10;
7265 grouping /= 10;
7267 while (p > tmp)
7269 *buffer++ = *(--p);
7270 if (p > tmp) *buffer++ = ';';
7272 if (last_digit != 0)
7274 *buffer++ = ';';
7275 *buffer++ = '0';
7276 if (last_digit == ~0)
7278 /* Add another trailing zero due to the weird way trailing zeros work in grouping string */
7279 *buffer++ = ';';
7280 *buffer++ = '0';
7283 *buffer = 0;
7287 static WCHAR *prepend_str( WCHAR *end, const WCHAR *str )
7289 UINT len = wcslen( str );
7290 return memcpy( end - len, str, len * sizeof(WCHAR) );
7294 /* format a positive number with decimal part; helper for get_number_format */
7295 static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decimal_sep,
7296 const WCHAR *thousand_sep, const WCHAR *grouping, UINT digits, BOOL lzero )
7298 BOOL round = FALSE, repeat = FALSE;
7299 UINT i, len = 0, prev = ~0;
7300 const WCHAR *frac = NULL;
7302 *(--end) = 0;
7304 for (i = 0; value[i]; i++)
7306 if (value[i] >= '0' && value[i] <= '9') continue;
7307 if (value[i] != '.') return NULL;
7308 if (frac) return NULL;
7309 frac = value + i + 1;
7312 /* format fractional part */
7314 len = frac ? wcslen( frac ) : 0;
7316 if (len > digits)
7318 round = frac[digits] >= '5';
7319 len = digits;
7321 while (digits > len)
7323 (*--end) = '0';
7324 digits--;
7326 while (len)
7328 WCHAR ch = frac[--len];
7329 if (round)
7331 if (ch != '9')
7333 ch++;
7334 round = FALSE;
7336 else ch = '0';
7338 *(--end) = ch;
7340 if (*end) end = prepend_str( end, decimal_sep );
7342 /* format integer part */
7344 len = frac ? frac - value - 1 : wcslen( value );
7346 while (len && *value == '0')
7348 value++;
7349 len--;
7351 if (len) lzero = FALSE;
7353 /* leading 0s are ignored */
7354 while (grouping[0] == '0' && grouping[1] == ';')
7355 grouping += 2;
7357 while (len)
7359 UINT limit = prev;
7361 if (!repeat)
7363 limit = *grouping - '0';
7364 if (grouping[1] == ';')
7366 grouping += 2;
7367 if (limit)
7368 prev = limit;
7369 else
7371 /* Trailing 0;0 is a special case */
7372 prev = ~0;
7373 if (grouping[0] == '0' && grouping[1] != ';')
7375 repeat = TRUE;
7376 limit = prev;
7380 else
7382 repeat = TRUE;
7383 if (!limit)
7384 limit = prev;
7385 else
7386 prev = ~0;
7390 while (len && limit--)
7392 WCHAR ch = value[--len];
7393 if (round)
7395 if (ch != '9')
7397 ch++;
7398 round = FALSE;
7400 else ch = '0';
7402 *(--end) = ch;
7404 if (len) end = prepend_str( end, thousand_sep );
7406 if (round) *(--end) = '1';
7407 else if (lzero) *(--end) = '0';
7408 return end;
7412 static int get_number_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value,
7413 const NUMBERFMTW *format, WCHAR *buffer, int len )
7415 WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_neg[5], grouping[24], output[256];
7416 const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand;
7417 DWORD digits, lzero, order;
7418 int ret = 0;
7419 BOOL negative = (*value == '-');
7421 flags &= LOCALE_NOUSEROVERRIDE;
7423 if (!format)
7425 get_locale_info( locale, 0, LOCALE_SGROUPING | flags, grouping, ARRAY_SIZE(grouping) );
7426 get_locale_info( locale, 0, LOCALE_SDECIMAL | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) );
7427 get_locale_info( locale, 0, LOCALE_STHOUSAND | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) );
7428 get_locale_info( locale, 0, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER | flags,
7429 (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) );
7430 get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags,
7431 (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) );
7432 get_locale_info( locale, 0, LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER | flags,
7433 (WCHAR *)&order, sizeof(DWORD)/sizeof(WCHAR) );
7435 else
7437 if (flags)
7439 SetLastError( ERROR_INVALID_FLAGS );
7440 return 0;
7442 decimal_sep = format->lpDecimalSep;
7443 thousand_sep = format->lpThousandSep;
7444 grouping_to_string( format->Grouping, grouping );
7445 digits = format->NumDigits;
7446 lzero = format->LeadingZero;
7447 order = format->NegativeOrder;
7448 if (!decimal_sep || !thousand_sep)
7450 SetLastError( ERROR_INVALID_PARAMETER );
7451 return 0;
7455 if (negative)
7457 value++;
7458 get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) );
7461 if (!(num = format_number( output + ARRAY_SIZE(output) - 6, value,
7462 decimal_sep, thousand_sep, grouping, digits, lzero )))
7464 SetLastError( ERROR_INVALID_PARAMETER );
7465 return 0;
7468 if (negative)
7470 switch (order)
7472 case 0: /* (1.1) */
7473 num = prepend_str( num, L"(" );
7474 wcscat( num, L")" );
7475 break;
7476 case 2: /* - 1.1 */
7477 num = prepend_str( num, L" " );
7478 /* fall through */
7479 case 1: /* -1.1 */
7480 num = prepend_str( num, fmt_neg );
7481 break;
7482 case 4: /* 1.1 - */
7483 wcscat( num, L" " );
7484 /* fall through */
7485 case 3: /* 1.1- */
7486 wcscat( num, fmt_neg );
7487 break;
7488 default:
7489 SetLastError( ERROR_INVALID_PARAMETER );
7490 return 0;
7494 ret = wcslen( num ) + 1;
7495 if (!len) return ret;
7496 lstrcpynW( buffer, num, len );
7497 if (ret > len)
7499 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7500 return 0;
7502 return ret;
7506 static int get_currency_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value,
7507 const CURRENCYFMTW *format, WCHAR *buffer, int len )
7509 WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_symbol[13], fmt_neg[5], grouping[20], output[256];
7510 const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand, *symbol = fmt_symbol;
7511 DWORD digits, lzero, pos_order, neg_order;
7512 int ret = 0;
7513 BOOL negative = (*value == '-');
7515 flags &= LOCALE_NOUSEROVERRIDE;
7517 if (!format)
7519 get_locale_info( locale, 0, LOCALE_SCURRENCY | flags, fmt_symbol, ARRAY_SIZE(fmt_symbol) );
7520 get_locale_info( locale, 0, LOCALE_SMONGROUPING | flags, grouping, ARRAY_SIZE(grouping) );
7521 get_locale_info( locale, 0, LOCALE_SMONDECIMALSEP | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) );
7522 get_locale_info( locale, 0, LOCALE_SMONTHOUSANDSEP | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) );
7523 get_locale_info( locale, 0, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER | flags,
7524 (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) );
7525 get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags,
7526 (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) );
7527 get_locale_info( locale, 0, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | flags,
7528 (WCHAR *)&pos_order, sizeof(DWORD)/sizeof(WCHAR) );
7529 get_locale_info( locale, 0, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | flags,
7530 (WCHAR *)&neg_order, sizeof(DWORD)/sizeof(WCHAR) );
7532 else
7534 if (flags)
7536 SetLastError( ERROR_INVALID_FLAGS );
7537 return 0;
7539 decimal_sep = format->lpDecimalSep;
7540 thousand_sep = format->lpThousandSep;
7541 symbol = format->lpCurrencySymbol;
7542 grouping_to_string( format->Grouping, grouping );
7543 digits = format->NumDigits;
7544 lzero = format->LeadingZero;
7545 pos_order = format->PositiveOrder;
7546 neg_order = format->NegativeOrder;
7547 if (!decimal_sep || !thousand_sep || !symbol)
7549 SetLastError( ERROR_INVALID_PARAMETER );
7550 return 0;
7554 if (negative)
7556 value++;
7557 get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) );
7560 if (!(num = format_number( output + ARRAY_SIZE(output) - 20, value,
7561 decimal_sep, thousand_sep, grouping, digits, lzero )))
7563 SetLastError( ERROR_INVALID_PARAMETER );
7564 return 0;
7567 if (negative)
7569 switch (neg_order)
7571 case 14: /* ($ 1.1) */
7572 num = prepend_str( num, L" " );
7573 /* fall through */
7574 case 0: /* ($1.1) */
7575 num = prepend_str( num, symbol );
7576 num = prepend_str( num, L"(" );
7577 wcscat( num, L")" );
7578 break;
7579 case 9: /* -$ 1.1 */
7580 num = prepend_str( num, L" " );
7581 /* fall through */
7582 case 1: /* -$1.1 */
7583 num = prepend_str( num, symbol );
7584 num = prepend_str( num, fmt_neg );
7585 break;
7586 case 2: /* $-1.1 */
7587 num = prepend_str( num, fmt_neg );
7588 num = prepend_str( num, symbol );
7589 break;
7590 case 11: /* $ 1.1- */
7591 num = prepend_str( num, L" " );
7592 /* fall through */
7593 case 3: /* $1.1- */
7594 num = prepend_str( num, symbol );
7595 wcscat( num, fmt_neg );
7596 break;
7597 case 15: /* (1.1 $) */
7598 wcscat( num, L" " );
7599 /* fall through */
7600 case 4: /* (1.1$) */
7601 wcscat( num, symbol );
7602 num = prepend_str( num, L"(" );
7603 wcscat( num, L")" );
7604 break;
7605 case 8: /* -1.1 $ */
7606 wcscat( num, L" " );
7607 /* fall through */
7608 case 5: /* -1.1$ */
7609 num = prepend_str( num, fmt_neg );
7610 wcscat( num, symbol );
7611 break;
7612 case 6: /* 1.1-$ */
7613 wcscat( num, fmt_neg );
7614 wcscat( num, symbol );
7615 break;
7616 case 10: /* 1.1 $- */
7617 wcscat( num, L" " );
7618 /* fall through */
7619 case 7: /* 1.1$- */
7620 wcscat( num, symbol );
7621 wcscat( num, fmt_neg );
7622 break;
7623 case 12: /* $ -1.1 */
7624 num = prepend_str( num, fmt_neg );
7625 num = prepend_str( num, L" " );
7626 num = prepend_str( num, symbol );
7627 break;
7628 case 13: /* 1.1- $ */
7629 wcscat( num, fmt_neg );
7630 wcscat( num, L" " );
7631 wcscat( num, symbol );
7632 break;
7633 default:
7634 SetLastError( ERROR_INVALID_PARAMETER );
7635 return 0;
7638 else
7640 switch (pos_order)
7642 case 2: /* $ 1.1 */
7643 num = prepend_str( num, L" " );
7644 /* fall through */
7645 case 0: /* $1.1 */
7646 num = prepend_str( num, symbol );
7647 break;
7648 case 3: /* 1.1 $ */
7649 wcscat( num, L" " );
7650 /* fall through */
7651 case 1: /* 1.1$ */
7652 wcscat( num, symbol );
7653 break;
7654 default:
7655 SetLastError( ERROR_INVALID_PARAMETER );
7656 return 0;
7660 ret = wcslen( num ) + 1;
7661 if (!len) return ret;
7662 lstrcpynW( buffer, num, len );
7663 if (ret > len)
7665 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7666 return 0;
7668 return ret;
7672 /* get the length of a date/time formatting pattern */
7673 static int get_pattern_len( const WCHAR *pattern, const WCHAR *accept )
7675 int i;
7677 if (*pattern == '\'')
7679 for (i = 1; pattern[i]; i++)
7681 if (pattern[i] != '\'') continue;
7682 if (pattern[++i] != '\'') return i;
7684 return i;
7686 if (!wcschr( accept, *pattern )) return 1;
7687 for (i = 1; pattern[i]; i++) if (pattern[i] != pattern[0]) break;
7688 return i;
7692 static int get_date_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime,
7693 const WCHAR *format, WCHAR *buffer, int len )
7695 DWORD override = flags & LOCALE_NOUSEROVERRIDE;
7696 DWORD genitive = 0;
7697 WCHAR *p, fmt[80], output[256];
7698 SYSTEMTIME time;
7699 int ret, val, count, i;
7701 if (!format)
7703 if (flags & DATE_USE_ALT_CALENDAR) FIXME( "alt calendar not supported\n" );
7704 switch (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY))
7706 case 0:
7707 case DATE_SHORTDATE:
7708 get_locale_info( locale, 0, LOCALE_SSHORTDATE | override, fmt, ARRAY_SIZE(fmt) );
7709 break;
7710 case DATE_LONGDATE:
7711 get_locale_info( locale, 0, LOCALE_SLONGDATE | override, fmt, ARRAY_SIZE(fmt) );
7712 break;
7713 case DATE_YEARMONTH:
7714 get_locale_info( locale, 0, LOCALE_SYEARMONTH | override, fmt, ARRAY_SIZE(fmt) );
7715 break;
7716 case DATE_MONTHDAY:
7717 get_locale_info( locale, 0, LOCALE_SMONTHDAY | override, fmt, ARRAY_SIZE(fmt) );
7718 break;
7719 default:
7720 SetLastError( ERROR_INVALID_FLAGS );
7721 return 0;
7723 format = fmt;
7725 else if (override || (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY)))
7727 SetLastError( ERROR_INVALID_FLAGS );
7728 return 0;
7731 if (systime)
7733 FILETIME ft;
7735 time = *systime;
7736 time.wHour = time.wMinute = time.wSecond = time.wMilliseconds = 0;
7737 if (!SystemTimeToFileTime( &time, &ft ) || !FileTimeToSystemTime( &ft, &time )) return 0;
7739 else GetLocalTime( &time );
7741 for (p = output; *format; format += count)
7743 count = get_pattern_len( format, L"yMd" );
7745 switch (*format)
7747 case '\'':
7748 for (i = 1; i < count; i++)
7750 if (format[i] == '\'') i++;
7751 if (i < count) *p++ = format[i];
7753 break;
7755 case 'y':
7756 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%02u",
7757 (count <= 2) ? time.wYear % 100 : time.wYear );
7758 break;
7760 case 'M':
7761 if (count <= 2)
7763 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wMonth );
7764 break;
7766 val = (count == 3 ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1) + time.wMonth - 1;
7767 if (!genitive)
7769 for (i = count; format[i]; i += get_pattern_len( format, L"yMd" ))
7771 if (format[i] != 'd') continue;
7772 if (format[i + 1] != 'd' || format[i + 2] != 'd')
7773 genitive = LOCALE_RETURN_GENITIVE_NAMES;
7774 break;
7777 p += get_locale_info( locale, 0, val | override | genitive,
7778 p, output + ARRAY_SIZE(output) - p ) - 1;
7779 break;
7781 case 'd':
7782 if (count <= 2)
7784 genitive = LOCALE_RETURN_GENITIVE_NAMES;
7785 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wDay );
7786 break;
7788 genitive = 0;
7789 val = (count == 3 ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1) + (time.wDayOfWeek + 6) % 7;
7790 p += get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p ) - 1;
7791 break;
7793 case 'g':
7794 p += locale_return_string( count >= 2 ? locale->serastring : locale->sabbreverastring,
7795 override, p, output + ARRAY_SIZE(output) - p ) - 1;
7796 break;
7798 default:
7799 *p++ = *format;
7800 break;
7803 *p++ = 0;
7804 ret = p - output;
7806 if (!len) return ret;
7807 lstrcpynW( buffer, output, len );
7808 if (ret > len)
7810 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7811 return 0;
7813 return ret;
7817 static int get_time_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime,
7818 const WCHAR *format, WCHAR *buffer, int len )
7820 DWORD override = flags & LOCALE_NOUSEROVERRIDE;
7821 WCHAR *p, *last, fmt[80], output[256];
7822 SYSTEMTIME time;
7823 int i, ret, val, count;
7824 BOOL skip = FALSE;
7826 if (!format)
7828 get_locale_info( locale, 0, LOCALE_STIMEFORMAT | override, fmt, ARRAY_SIZE(fmt) );
7829 format = fmt;
7831 else if (override)
7833 SetLastError( ERROR_INVALID_FLAGS );
7834 return 0;
7837 if (systime)
7839 time = *systime;
7840 if (time.wMilliseconds > 999 || time.wSecond > 59 || time.wMinute > 59 || time.wHour > 23)
7842 SetLastError( ERROR_INVALID_PARAMETER );
7843 return 0;
7846 else GetLocalTime( &time );
7848 for (p = last = output; *format; format += count)
7850 count = get_pattern_len( format, L"Hhmst" );
7852 switch (*format)
7854 case '\'':
7855 for (i = 1; i < count; i++)
7857 if (format[i] == '\'') i++;
7858 if (!skip && i < count) *p++ = format[i];
7860 continue;
7862 case 'H':
7863 val = time.wHour;
7864 break;
7866 case 'h':
7867 val = time.wHour;
7868 if (!(flags & TIME_FORCE24HOURFORMAT))
7870 val %= 12;
7871 if (!val) val = 12;
7873 break;
7875 case 'm':
7876 if (flags & TIME_NOMINUTESORSECONDS)
7878 p = last;
7879 skip = TRUE;
7880 continue;
7882 val = time.wMinute;
7883 break;
7885 case 's':
7886 if (flags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS))
7888 p = last;
7889 skip = TRUE;
7890 continue;
7892 val = time.wSecond;
7893 break;
7895 case 't':
7896 if (flags & TIME_NOTIMEMARKER)
7898 p = last;
7899 skip = TRUE;
7900 continue;
7902 val = time.wHour < 12 ? LOCALE_S1159 : LOCALE_S2359;
7903 ret = get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p );
7904 p += (count > 1) ? ret - 1 : 1;
7905 skip = FALSE;
7906 continue;
7908 default:
7909 if (!skip || *format == ' ') *p++ = *format;
7910 continue;
7913 p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", min( 2, count ), val );
7914 last = p;
7915 skip = FALSE;
7917 *p++ = 0;
7918 ret = p - output;
7920 if (!len) return ret;
7921 lstrcpynW( buffer, output, len );
7922 if (ret > len)
7924 SetLastError( ERROR_INSUFFICIENT_BUFFER );
7925 return 0;
7927 return ret;
7931 /**************************************************************************
7932 * GetNumberFormatW (kernelbase.@)
7934 int WINAPI GetNumberFormatW( LCID lcid, DWORD flags, const WCHAR *value,
7935 const NUMBERFMTW *format, WCHAR *buffer, int len )
7937 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7939 if (len < 0 || (len && !buffer) || !value || !locale)
7941 SetLastError( ERROR_INVALID_PARAMETER );
7942 return 0;
7945 TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len );
7946 return get_number_format( locale, flags, value, format, buffer, len );
7950 /**************************************************************************
7951 * GetNumberFormatEx (kernelbase.@)
7953 int WINAPI GetNumberFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value,
7954 const NUMBERFMTW *format, WCHAR *buffer, int len )
7956 LCID lcid;
7957 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
7959 if (len < 0 || (len && !buffer) || !value || !locale)
7961 SetLastError( ERROR_INVALID_PARAMETER );
7962 return 0;
7965 TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len );
7966 return get_number_format( locale, flags, value, format, buffer, len );
7970 /***********************************************************************
7971 * GetCurrencyFormatW (kernelbase.@)
7973 int WINAPI GetCurrencyFormatW( LCID lcid, DWORD flags, const WCHAR *value,
7974 const CURRENCYFMTW *format, WCHAR *buffer, int len )
7976 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
7978 if (len < 0 || (len && !buffer) || !value || !locale)
7980 SetLastError( ERROR_INVALID_PARAMETER );
7981 return 0;
7984 TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len );
7985 return get_currency_format( locale, flags, value, format, buffer, len );
7989 /***********************************************************************
7990 * GetCurrencyFormatEx (kernelbase.@)
7992 int WINAPI GetCurrencyFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value,
7993 const CURRENCYFMTW *format, WCHAR *buffer, int len )
7995 LCID lcid;
7996 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
7998 if (len < 0 || (len && !buffer) || !value || !locale)
8000 SetLastError( ERROR_INVALID_PARAMETER );
8001 return 0;
8004 TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len );
8005 return get_currency_format( locale, flags, value, format, buffer, len );
8009 /******************************************************************************
8010 * GetDateFormatA (KERNEL32.@)
8012 int WINAPI GetDateFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time,
8013 const char *format, char *buffer, int len )
8015 UINT cp = get_lcid_codepage( lcid, flags );
8016 WCHAR formatW[128], output[128];
8017 int ret;
8019 TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len );
8021 if (len < 0 || (len && !buffer))
8023 SetLastError( ERROR_INVALID_PARAMETER );
8024 return 0;
8026 if (format)
8028 MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) );
8029 ret = GetDateFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) );
8031 else ret = GetDateFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) );
8033 if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 );
8034 return ret;
8038 /***********************************************************************
8039 * GetDateFormatW (kernelbase.@)
8041 int WINAPI GetDateFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime,
8042 const WCHAR *format, WCHAR *buffer, int len )
8044 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
8046 if (len < 0 || (len && !buffer) || !locale)
8048 SetLastError( ERROR_INVALID_PARAMETER );
8049 return 0;
8052 TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len );
8053 return get_date_format( locale, flags, systime, format, buffer, len );
8057 /***********************************************************************
8058 * GetDateFormatEx (kernelbase.@)
8060 int WINAPI GetDateFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime,
8061 const WCHAR *format, WCHAR *buffer, int len, const WCHAR *calendar )
8063 LCID lcid;
8064 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
8066 if (len < 0 || (len && !buffer) || !locale || calendar)
8068 SetLastError( ERROR_INVALID_PARAMETER );
8069 return 0;
8072 TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len );
8073 return get_date_format( locale, flags, systime, format, buffer, len );
8077 /******************************************************************************
8078 * GetTimeFormatA (kernelbase.@)
8080 int WINAPI GetTimeFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time,
8081 const char *format, char *buffer, int len )
8083 UINT cp = get_lcid_codepage( lcid, flags );
8084 WCHAR formatW[128], output[128];
8085 int ret;
8087 TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len );
8089 if (len < 0 || (len && !buffer))
8091 SetLastError( ERROR_INVALID_PARAMETER );
8092 return 0;
8094 if (format)
8096 MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) );
8097 ret = GetTimeFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) );
8099 else ret = GetTimeFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) );
8101 if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 );
8102 return ret;
8106 /***********************************************************************
8107 * GetTimeFormatW (kernelbase.@)
8109 int WINAPI GetTimeFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime,
8110 const WCHAR *format, WCHAR *buffer, int len )
8112 const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 );
8114 if (len < 0 || (len && !buffer) || !locale)
8116 SetLastError( ERROR_INVALID_PARAMETER );
8117 return 0;
8120 TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len );
8121 return get_time_format( locale, flags, systime, format, buffer, len );
8125 /***********************************************************************
8126 * GetTimeFormatEx (kernelbase.@)
8128 int WINAPI GetTimeFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime,
8129 const WCHAR *format, WCHAR *buffer, int len )
8131 LCID lcid;
8132 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
8134 if (len < 0 || (len && !buffer) || !locale)
8136 SetLastError( ERROR_INVALID_PARAMETER );
8137 return 0;
8140 TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len );
8141 return get_time_format( locale, flags, systime, format, buffer, len );