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