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
;
52 enum { NOT_CACHED
, CACHED
, MISSING
} status
;
56 static struct registry_entry entry_icalendartype
= { L
"iCalendarType" };
57 static struct registry_entry entry_icountry
= { L
"iCountry" };
58 static struct registry_entry entry_icurrdigits
= { L
"iCurrDigits" };
59 static struct registry_entry entry_icurrency
= { L
"iCurrency" };
60 static struct registry_entry entry_idigits
= { L
"iDigits" };
61 static struct registry_entry entry_idigitsubstitution
= { L
"NumShape" };
62 static struct registry_entry entry_ifirstdayofweek
= { L
"iFirstDayOfWeek" };
63 static struct registry_entry entry_ifirstweekofyear
= { L
"iFirstWeekOfYear" };
64 static struct registry_entry entry_ilzero
= { L
"iLZero" };
65 static struct registry_entry entry_imeasure
= { L
"iMeasure" };
66 static struct registry_entry entry_inegcurr
= { L
"iNegCurr" };
67 static struct registry_entry entry_inegnumber
= { L
"iNegNumber" };
68 static struct registry_entry entry_ipapersize
= { L
"iPaperSize" };
69 static struct registry_entry entry_s1159
= { L
"s1159" };
70 static struct registry_entry entry_s2359
= { L
"s2359" };
71 static struct registry_entry entry_scurrency
= { L
"sCurrency" };
72 static struct registry_entry entry_sdecimal
= { L
"sDecimal" };
73 static struct registry_entry entry_sgrouping
= { L
"sGrouping" };
74 static struct registry_entry entry_slist
= { L
"sList" };
75 static struct registry_entry entry_slongdate
= { L
"sLongDate" };
76 static struct registry_entry entry_smondecimalsep
= { L
"sMonDecimalSep" };
77 static struct registry_entry entry_smongrouping
= { L
"sMonGrouping" };
78 static struct registry_entry entry_smonthousandsep
= { L
"sMonThousandSep" };
79 static struct registry_entry entry_snativedigits
= { L
"sNativeDigits" };
80 static struct registry_entry entry_snegativesign
= { L
"sNegativeSign" };
81 static struct registry_entry entry_spositivesign
= { L
"sPositiveSign" };
82 static struct registry_entry entry_sshortdate
= { L
"sShortDate" };
83 static struct registry_entry entry_sshorttime
= { L
"sShortTime" };
84 static struct registry_entry entry_sthousand
= { L
"sThousand" };
85 static struct registry_entry entry_stimeformat
= { L
"sTimeFormat" };
86 static struct registry_entry entry_syearmonth
= { L
"sYearMonth" };
89 static const struct { UINT cp
; const WCHAR
*name
; } codepage_names
[] =
91 { 37, L
"IBM EBCDIC US Canada" },
92 { 424, L
"IBM EBCDIC Hebrew" },
93 { 437, L
"OEM United States" },
94 { 500, L
"IBM EBCDIC International" },
95 { 708, L
"Arabic ASMO" },
96 { 720, L
"Arabic (Transparent ASMO)" },
97 { 737, L
"OEM Greek 437G" },
98 { 775, L
"OEM Baltic" },
99 { 850, L
"OEM Multilingual Latin 1" },
100 { 852, L
"OEM Slovak Latin 2" },
101 { 855, L
"OEM Cyrillic" },
102 { 856, L
"Hebrew PC" },
103 { 857, L
"OEM Turkish" },
104 { 860, L
"OEM Portuguese" },
105 { 861, L
"OEM Icelandic" },
106 { 862, L
"OEM Hebrew" },
107 { 863, L
"OEM Canadian French" },
108 { 864, L
"OEM Arabic" },
109 { 865, L
"OEM Nordic" },
110 { 866, L
"OEM Russian" },
111 { 869, L
"OEM Greek" },
112 { 874, L
"ANSI/OEM Thai" },
113 { 875, L
"IBM EBCDIC Greek" },
114 { 878, L
"Russian KOI8" },
115 { 932, L
"ANSI/OEM Japanese Shift-JIS" },
116 { 936, L
"ANSI/OEM Simplified Chinese GBK" },
117 { 949, L
"ANSI/OEM Korean Unified Hangul" },
118 { 950, L
"ANSI/OEM Traditional Chinese Big5" },
119 { 1006, L
"IBM Arabic" },
120 { 1026, L
"IBM EBCDIC Latin 5 Turkish" },
121 { 1250, L
"ANSI Eastern Europe" },
122 { 1251, L
"ANSI Cyrillic" },
123 { 1252, L
"ANSI Latin 1" },
124 { 1253, L
"ANSI Greek" },
125 { 1254, L
"ANSI Turkish" },
126 { 1255, L
"ANSI Hebrew" },
127 { 1256, L
"ANSI Arabic" },
128 { 1257, L
"ANSI Baltic" },
129 { 1258, L
"ANSI/OEM Viet Nam" },
130 { 1361, L
"Korean Johab" },
131 { 10000, L
"Mac Roman" },
132 { 10001, L
"Mac Japanese" },
133 { 10002, L
"Mac Traditional Chinese" },
134 { 10003, L
"Mac Korean" },
135 { 10004, L
"Mac Arabic" },
136 { 10005, L
"Mac Hebrew" },
137 { 10006, L
"Mac Greek" },
138 { 10007, L
"Mac Cyrillic" },
139 { 10008, L
"Mac Simplified Chinese" },
140 { 10010, L
"Mac Romanian" },
141 { 10017, L
"Mac Ukrainian" },
142 { 10021, L
"Mac Thai" },
143 { 10029, L
"Mac Latin 2" },
144 { 10079, L
"Mac Icelandic" },
145 { 10081, L
"Mac Turkish" },
146 { 10082, L
"Mac Croatian" },
147 { 20127, L
"US-ASCII (7bit)" },
148 { 20866, L
"Russian KOI8" },
149 { 20932, L
"EUC-JP" },
150 { 20949, L
"Korean Wansung" },
151 { 21866, L
"Ukrainian KOI8" },
152 { 28591, L
"ISO 8859-1 Latin 1" },
153 { 28592, L
"ISO 8859-2 Latin 2 (East European)" },
154 { 28593, L
"ISO 8859-3 Latin 3 (South European)" },
155 { 28594, L
"ISO 8859-4 Latin 4 (Baltic old)" },
156 { 28595, L
"ISO 8859-5 Cyrillic" },
157 { 28596, L
"ISO 8859-6 Arabic" },
158 { 28597, L
"ISO 8859-7 Greek" },
159 { 28598, L
"ISO 8859-8 Hebrew" },
160 { 28599, L
"ISO 8859-9 Latin 5 (Turkish)" },
161 { 28600, L
"ISO 8859-10 Latin 6 (Nordic)" },
162 { 28601, L
"ISO 8859-11 Latin (Thai)" },
163 { 28603, L
"ISO 8859-13 Latin 7 (Baltic)" },
164 { 28604, L
"ISO 8859-14 Latin 8 (Celtic)" },
165 { 28605, L
"ISO 8859-15 Latin 9 (Euro)" },
166 { 28606, L
"ISO 8859-16 Latin 10 (Balkan)" },
167 { 65000, L
"Unicode (UTF-7)" },
168 { 65001, L
"Unicode (UTF-8)" }
171 /* Unicode expanded ligatures */
172 static const WCHAR ligatures
[][5] =
174 { 0x00c6, 'A','E',0 },
175 { 0x00de, 'T','H',0 },
176 { 0x00df, 's','s',0 },
177 { 0x00e6, 'a','e',0 },
178 { 0x00fe, 't','h',0 },
179 { 0x0132, 'I','J',0 },
180 { 0x0133, 'i','j',0 },
181 { 0x0152, 'O','E',0 },
182 { 0x0153, 'o','e',0 },
183 { 0x01c4, 'D',0x017d,0 },
184 { 0x01c5, 'D',0x017e,0 },
185 { 0x01c6, 'd',0x017e,0 },
186 { 0x01c7, 'L','J',0 },
187 { 0x01c8, 'L','j',0 },
188 { 0x01c9, 'l','j',0 },
189 { 0x01ca, 'N','J',0 },
190 { 0x01cb, 'N','j',0 },
191 { 0x01cc, 'n','j',0 },
192 { 0x01e2, 0x0100,0x0112,0 },
193 { 0x01e3, 0x0101,0x0113,0 },
194 { 0x01f1, 'D','Z',0 },
195 { 0x01f2, 'D','z',0 },
196 { 0x01f3, 'd','z',0 },
197 { 0x01fc, 0x00c1,0x00c9,0 },
198 { 0x01fd, 0x00e1,0x00e9,0 },
199 { 0x05f0, 0x05d5,0x05d5,0 },
200 { 0x05f1, 0x05d5,0x05d9,0 },
201 { 0x05f2, 0x05d9,0x05d9,0 },
202 { 0xfb00, 'f','f',0 },
203 { 0xfb01, 'f','i',0 },
204 { 0xfb02, 'f','l',0 },
205 { 0xfb03, 'f','f','i',0 },
206 { 0xfb04, 'f','f','l',0 },
207 { 0xfb05, 0x017f,'t',0 },
208 { 0xfb06, 's','t',0 },
212 static const struct geo_id
227 static const struct geo_index
233 static unsigned int geo_ids_count
;
234 static unsigned int geo_index_count
;
236 /* NLS normalization file */
239 WCHAR name
[13]; /* 00 file name */
240 USHORT checksum
[3]; /* 1a checksum? */
241 USHORT version
[4]; /* 20 Unicode version */
242 USHORT form
; /* 28 normalization form */
243 USHORT len_factor
; /* 2a factor for length estimates */
244 USHORT unknown1
; /* 2c */
245 USHORT decomp_size
; /* 2e decomposition hash size */
246 USHORT comp_size
; /* 30 composition hash size */
247 USHORT unknown2
; /* 32 */
248 USHORT classes
; /* 34 combining classes table offset */
249 USHORT props_level1
; /* 36 char properties table level 1 offset */
250 USHORT props_level2
; /* 38 char properties table level 2 offset */
251 USHORT decomp_hash
; /* 3a decomposition hash table offset */
252 USHORT decomp_map
; /* 3c decomposition character map table offset */
253 USHORT decomp_seq
; /* 3e decomposition character sequences offset */
254 USHORT comp_hash
; /* 40 composition hash table offset */
255 USHORT comp_seq
; /* 42 composition character sequences offset */
256 /* BYTE[] combining class values */
257 /* BYTE[0x2200] char properties index level 1 */
258 /* BYTE[] char properties index level 2 */
259 /* WORD[] decomposition hash table */
260 /* WORD[] decomposition character map */
261 /* WORD[] decomposition character sequences */
262 /* WORD[] composition hash table */
263 /* WORD[] composition character sequences */
266 static NLSTABLEINFO nls_info
;
267 static UINT unix_cp
= CP_UTF8
;
268 static UINT mac_cp
= 10000;
269 static LCID system_lcid
;
270 static LCID user_lcid
;
271 static HKEY intl_key
;
274 static const NLS_LOCALE_LCID_INDEX
*lcids_index
;
275 static const NLS_LOCALE_LCNAME_INDEX
*lcnames_index
;
276 static const NLS_LOCALE_HEADER
*locale_table
;
277 static const WCHAR
*locale_strings
;
278 static const NLS_LOCALE_DATA
*system_locale
;
279 static const NLS_LOCALE_DATA
*user_locale
;
281 static CPTABLEINFO codepages
[128];
282 static unsigned int nb_codepages
;
284 static struct norm_table
*norm_info
;
288 GUID id
; /* sort GUID */
289 DWORD flags
; /* flags */
290 DWORD compr
; /* offset to compression table */
291 DWORD except
; /* exception table offset in sortkey table */
292 DWORD ling_except
; /* exception table offset for linguistic casing */
293 DWORD casemap
; /* linguistic casemap table offset */
296 #define FLAG_HAS_3_BYTE_WEIGHTS 0x01
297 #define FLAG_REVERSEDIACRITICS 0x10
298 #define FLAG_DOUBLECOMPRESSION 0x20
299 #define FLAG_INVERSECASING 0x40
301 static const struct sortguid
*current_locale_sort
;
303 static const GUID default_sort_guid
= { 0x00000001, 0x57ee, 0x1e5c, { 0x00, 0xb4, 0xd0, 0x00, 0x0b, 0xb1, 0xe1, 0x1e }};
307 DWORD
*keys
; /* sortkey table, indexed by char */
308 USHORT
*casemap
; /* casemap table, in l_intl.nls format */
309 WORD
*ctypes
; /* CT_CTYPE1,2,3 values */
310 BYTE
*ctype_idx
; /* index to map char to ctypes array entry */
311 DWORD version
; /* NLS version */
312 DWORD guid_count
; /* number of sort GUIDs */
313 struct sortguid
*guids
; /* table of sort GUIDs */
316 static CRITICAL_SECTION locale_section
;
317 static CRITICAL_SECTION_DEBUG critsect_debug
=
319 0, 0, &locale_section
,
320 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
321 0, 0, { (DWORD_PTR
)(__FILE__
": locale_section") }
323 static CRITICAL_SECTION locale_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
326 static void load_locale_nls(void)
341 WCHAR signature
[4]; /* L"geo" */
352 RtlGetLocaleFileMappingAddress( (void **)&header
, &lcid
, &dummy
);
353 locale_table
= (const NLS_LOCALE_HEADER
*)((char *)header
+ header
->locales
);
354 lcids_index
= (const NLS_LOCALE_LCID_INDEX
*)((char *)locale_table
+ locale_table
->lcids_offset
);
355 lcnames_index
= (const NLS_LOCALE_LCNAME_INDEX
*)((char *)locale_table
+ locale_table
->lcnames_offset
);
356 locale_strings
= (const WCHAR
*)((char *)locale_table
+ locale_table
->strings_offset
);
357 geo_header
= (struct geo_header
*)((char *)header
+ header
->geoids
);
358 geo_ids
= (const struct geo_id
*)((char *)geo_header
+ geo_header
->ids_offset
);
359 geo_index
= (const struct geo_index
*)((char *)geo_header
+ geo_header
->index_offset
);
360 geo_ids_count
= geo_header
->ids_count
;
361 geo_index_count
= geo_header
->index_count
;
365 static void init_sortkeys( DWORD
*ptr
)
370 sort
.keys
= (DWORD
*)((char *)ptr
+ ptr
[0]);
371 sort
.casemap
= (USHORT
*)((char *)ptr
+ ptr
[1]);
373 ctype
= (WORD
*)((char *)ptr
+ ptr
[2]);
374 sort
.ctypes
= ctype
+ 2;
375 sort
.ctype_idx
= (BYTE
*)ctype
+ ctype
[1] + 2;
377 table
= (DWORD
*)((char *)ptr
+ ptr
[3]);
378 sort
.version
= table
[0];
379 sort
.guid_count
= table
[1];
380 sort
.guids
= (struct sortguid
*)(table
+ 2);
384 static const struct sortguid
*find_sortguid( const GUID
*guid
)
386 int pos
, ret
, min
= 0, max
= sort
.guid_count
- 1;
390 pos
= (min
+ max
) / 2;
391 ret
= memcmp( guid
, &sort
.guids
[pos
].id
, sizeof(*guid
) );
392 if (!ret
) return &sort
.guids
[pos
];
393 if (ret
> 0) min
= pos
+ 1;
396 ERR( "no sort found for %s\n", debugstr_guid( guid
));
401 static const struct sortguid
*get_language_sort( const WCHAR
*locale
)
403 WCHAR
*p
, *end
, buffer
[LOCALE_NAME_MAX_LENGTH
], guidstr
[39];
404 const struct sortguid
*ret
;
410 if (locale
== LOCALE_NAME_USER_DEFAULT
)
412 if (current_locale_sort
) return current_locale_sort
;
413 GetUserDefaultLocaleName( buffer
, ARRAY_SIZE( buffer
));
415 else lstrcpynW( buffer
, locale
, LOCALE_NAME_MAX_LENGTH
);
417 if (buffer
[0] && !RegOpenKeyExW( nls_key
, L
"Sorting\\Ids", 0, KEY_READ
, &key
))
421 size
= sizeof(guidstr
);
422 if (!RegQueryValueExW( key
, buffer
, NULL
, &type
, (BYTE
*)guidstr
, &size
) && type
== REG_SZ
)
424 RtlInitUnicodeString( &str
, guidstr
);
425 if (!RtlGUIDFromString( &str
, &guid
))
427 ret
= find_sortguid( &guid
);
432 for (p
= end
= buffer
; *p
; p
++) if (*p
== '-' || *p
== '_') end
= p
;
433 if (end
== buffer
) break;
437 ret
= find_sortguid( &default_sort_guid
);
444 static const NLS_LOCALE_DATA
*get_locale_data( UINT idx
)
446 ULONG offset
= locale_table
->locales_offset
+ idx
* locale_table
->locale_size
;
447 return (const NLS_LOCALE_DATA
*)((const char *)locale_table
+ offset
);
451 static int compare_locale_names( const WCHAR
*n1
, const WCHAR
*n2
)
457 if (ch1
>= 'a' && ch1
<= 'z') ch1
-= 'a' - 'A';
458 else if (ch1
== '_') ch1
= '-';
459 if (ch2
>= 'a' && ch2
<= 'z') ch2
-= 'a' - 'A';
460 else if (ch2
== '_') ch2
= '-';
461 if (!ch1
|| ch1
!= ch2
) return ch1
- ch2
;
466 static const NLS_LOCALE_LCNAME_INDEX
*find_lcname_entry( const WCHAR
*name
)
468 int min
= 0, max
= locale_table
->nb_lcnames
- 1;
472 int res
, pos
= (min
+ max
) / 2;
473 const WCHAR
*str
= locale_strings
+ lcnames_index
[pos
].name
;
474 res
= compare_locale_names( name
, str
+ 1 );
475 if (res
< 0) max
= pos
- 1;
476 else if (res
> 0) min
= pos
+ 1;
477 else return &lcnames_index
[pos
];
483 static const NLS_LOCALE_LCID_INDEX
*find_lcid_entry( LCID lcid
)
485 int min
= 0, max
= locale_table
->nb_lcids
- 1;
489 int pos
= (min
+ max
) / 2;
490 if (lcid
< lcids_index
[pos
].id
) max
= pos
- 1;
491 else if (lcid
> lcids_index
[pos
].id
) min
= pos
+ 1;
492 else return &lcids_index
[pos
];
498 static const struct geo_id
*find_geo_id_entry( GEOID id
)
500 int min
= 0, max
= geo_ids_count
- 1;
504 int pos
= (min
+ max
) / 2;
505 if (id
< geo_ids
[pos
].id
) max
= pos
- 1;
506 else if (id
> geo_ids
[pos
].id
) min
= pos
+ 1;
507 else return &geo_ids
[pos
];
513 static const struct geo_id
*find_geo_name_entry( const WCHAR
*name
)
515 int min
= 0, max
= geo_index_count
- 1;
519 int res
, pos
= (min
+ max
) / 2;
520 res
= wcsicmp( name
, geo_index
[pos
].name
);
521 if (res
< 0) max
= pos
- 1;
522 else if (res
> 0) min
= pos
+ 1;
523 else return &geo_ids
[geo_index
[pos
].idx
];
529 static const NLS_LOCALE_DATA
*get_locale_by_name( const WCHAR
*name
, LCID
*lcid
)
531 const NLS_LOCALE_LCNAME_INDEX
*entry
;
533 if (name
== LOCALE_NAME_USER_DEFAULT
)
538 if (!(entry
= find_lcname_entry( name
))) return NULL
;
540 return get_locale_data( entry
->idx
);
544 static const NLS_LOCALE_DATA
*get_locale_by_id( LCID
*lcid
, DWORD flags
)
546 const NLS_LOCALE_LCID_INDEX
*entry
;
547 const NLS_LOCALE_DATA
*locale
;
551 case LOCALE_SYSTEM_DEFAULT
:
553 return system_locale
;
555 case LOCALE_USER_DEFAULT
:
556 case LOCALE_CUSTOM_DEFAULT
:
560 if (!(entry
= find_lcid_entry( *lcid
))) return NULL
;
561 locale
= get_locale_data( entry
->idx
);
562 if (!(flags
& LOCALE_ALLOW_NEUTRAL_NAMES
) && !locale
->inotneutral
)
563 locale
= get_locale_by_name( locale_strings
+ locale
->ssortlocale
+ 1, lcid
);
569 static int locale_return_data( const WCHAR
*data
, int datalen
, LCTYPE type
, WCHAR
*buffer
, int len
)
571 if (type
& LOCALE_RETURN_NUMBER
)
573 SetLastError( ERROR_INVALID_FLAGS
);
577 if (!len
) return datalen
;
580 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
583 memcpy( buffer
, data
, datalen
* sizeof(WCHAR
) );
588 static BOOL
set_registry_entry( struct registry_entry
*entry
, const WCHAR
*data
)
590 DWORD size
= (wcslen(data
) + 1) * sizeof(WCHAR
);
593 if (size
> sizeof(entry
->data
))
595 SetLastError( ERROR_INVALID_FLAGS
);
598 TRACE( "setting %s to %s\n", debugstr_w(entry
->value
), debugstr_w(data
) );
600 RtlEnterCriticalSection( &locale_section
);
601 if (!(ret
= RegSetValueExW( intl_key
, entry
->value
, 0, REG_SZ
, (BYTE
*)data
, size
)))
603 wcscpy( entry
->data
, data
);
604 entry
->status
= CACHED
;
606 RtlLeaveCriticalSection( &locale_section
);
607 if (ret
) SetLastError( ret
);
612 static int locale_return_reg_string( struct registry_entry
*entry
, LCTYPE type
, WCHAR
*buffer
, int len
)
618 if (type
& LOCALE_NOUSEROVERRIDE
) return -1;
620 RtlEnterCriticalSection( &locale_section
);
621 switch (entry
->status
)
624 size
= sizeof(entry
->data
);
625 res
= RegQueryValueExW( intl_key
, entry
->value
, NULL
, NULL
, (BYTE
*)entry
->data
, &size
);
628 entry
->status
= MISSING
;
631 entry
->status
= CACHED
;
634 ret
= locale_return_data( entry
->data
, wcslen(entry
->data
) + 1, type
, buffer
, len
);
639 RtlLeaveCriticalSection( &locale_section
);
644 static int locale_return_string( DWORD pos
, LCTYPE type
, WCHAR
*buffer
, int len
)
646 return locale_return_data( locale_strings
+ pos
+ 1, locale_strings
[pos
] + 1, type
, buffer
, len
);
650 static int locale_return_number( UINT val
, LCTYPE type
, WCHAR
*buffer
, int len
)
655 if (!(type
& LOCALE_RETURN_NUMBER
))
657 switch (LOWORD(type
))
659 case LOCALE_ILANGUAGE
:
660 case LOCALE_IDEFAULTLANGUAGE
:
661 ret
= swprintf( tmp
, ARRAY_SIZE(tmp
), L
"%04x", val
) + 1;
663 case LOCALE_IDEFAULTEBCDICCODEPAGE
:
664 ret
= swprintf( tmp
, ARRAY_SIZE(tmp
), L
"%03u", val
) + 1;
667 ret
= swprintf( tmp
, ARRAY_SIZE(tmp
), L
"%u", val
) + 1;
671 else ret
= sizeof(UINT
) / sizeof(WCHAR
);
673 if (!len
) return ret
;
676 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
680 if (type
& LOCALE_RETURN_NUMBER
) memcpy( buffer
, &val
, sizeof(val
) );
681 else wcscpy( buffer
, tmp
);
687 static int locale_return_reg_number( struct registry_entry
*entry
, LCTYPE type
, WCHAR
*buffer
, int len
)
692 if (type
& LOCALE_RETURN_NUMBER
)
694 ret
= locale_return_reg_string( entry
, type
& ~LOCALE_RETURN_NUMBER
, tmp
, ARRAY_SIZE( tmp
));
695 if (ret
== -1) return ret
;
696 val
= wcstol( tmp
, &end
, 10 );
697 if (*end
) /* invalid number */
699 SetLastError( ERROR_INVALID_FLAGS
);
702 return locale_return_number( val
, type
, buffer
, len
);
704 return locale_return_reg_string( entry
, type
, buffer
, len
);
708 static int locale_return_grouping( DWORD pos
, LCTYPE type
, WCHAR
*buffer
, int len
)
710 WORD i
, count
= locale_strings
[pos
];
711 const WCHAR
*str
= locale_strings
+ pos
+ 1;
714 if (type
& LOCALE_RETURN_NUMBER
)
716 SetLastError( ERROR_INVALID_FLAGS
);
720 if (str
[count
- 1]) ret
+= 2; /* for final zero */
722 if (!len
) return ret
;
725 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
728 for (i
= 0; i
< count
; i
++)
730 if (!str
[i
]) /* explicit null termination */
735 *buffer
++ = '0' + str
[i
];
744 static int locale_return_strarray( DWORD pos
, WORD idx
, LCTYPE type
, WCHAR
*buffer
, int len
)
746 const DWORD
*array
= (const DWORD
*)(locale_strings
+ pos
+ 1);
747 WORD count
= locale_strings
[pos
];
749 return locale_return_string( idx
< count
? array
[idx
] : 0, type
, buffer
, len
);
753 static int locale_return_strarray_concat( DWORD pos
, LCTYPE type
, WCHAR
*buffer
, int len
)
755 WORD i
, count
= locale_strings
[pos
];
756 const DWORD
*array
= (const DWORD
*)(locale_strings
+ pos
+ 1);
759 if (type
& LOCALE_RETURN_NUMBER
)
761 SetLastError( ERROR_INVALID_FLAGS
);
764 for (i
= 0, ret
= 1; i
< count
; i
++) ret
+= locale_strings
[array
[i
]];
766 if (!len
) return ret
;
769 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
772 for (i
= 0; i
< count
; i
++)
774 memcpy( buffer
, locale_strings
+ array
[i
] + 1, locale_strings
[array
[i
]] * sizeof(WCHAR
) );
775 buffer
+= locale_strings
[array
[i
]];
782 /* find the first format char in a format string */
783 static WCHAR
*find_format( WCHAR
*str
, const WCHAR
*accept
)
789 if (!(str
= wcschr( str
+ 1, '\'' ))) return NULL
;
791 else if (wcschr( accept
, *str
))
793 /* ignore "ddd" and "dddd" */
794 if (str
[0] != 'd' || str
[1] != 'd' || str
[2] != 'd') return str
;
796 while (str
[1] == 'd') str
++;
803 /* replace the separator in a date/time format string */
804 static WCHAR
*locale_replace_separator( WCHAR
*buffer
, const WCHAR
*sep
)
808 WCHAR
*next
, *str
= find_format( buffer
, L
"dMyHhms" );
810 if (!str
) return buffer
;
812 memcpy( res
, buffer
, pos
* sizeof(WCHAR
) );
816 while (str
[0] == str
[-1]) res
[pos
++] = *str
++; /* copy repeated chars */
817 if (!(next
= find_format( str
, L
"dMyHhms" ))) break;
818 wcscpy( res
+ pos
, sep
);
822 wcscpy( res
+ pos
, str
);
823 return wcscpy( buffer
, res
);
827 /* FIXME: hardcoded, sortname is apparently not available in locale.nls */
828 static const WCHAR
*get_locale_sortname( LCID lcid
)
830 switch (PRIMARYLANGID( lcid
))
833 switch (SORTIDFROMLCID( lcid
))
835 case SORT_CHINESE_PRCP
:
836 switch (SUBLANGID( lcid
))
838 case SUBLANG_CHINESE_TRADITIONAL
:
839 case SUBLANG_CHINESE_HONGKONG
:
841 return L
"Stroke Count";
843 return L
"Pronunciation";
845 case SORT_CHINESE_UNICODE
: return L
"Unicode";
846 case SORT_CHINESE_PRC
: return L
"Stroke Count";
847 case SORT_CHINESE_BOPOMOFO
: return L
"Bopomofo";
848 case SORT_CHINESE_RADICALSTROKE
: return L
"Radical/Stroke";
849 case 5: return L
"Surname";
854 if (SORTIDFROMLCID( lcid
) == SORT_GEORGIAN_MODERN
) return L
"Modern";
855 return L
"Traditional";
858 switch (SUBLANGID( lcid
))
860 case SUBLANG_NEUTRAL
:
861 case SUBLANG_DEFAULT
:
862 if (SORTIDFROMLCID( lcid
) == SORT_GERMAN_PHONE_BOOK
) return L
"Phone Book (DIN)";
863 return L
"Dictionary";
868 if (SORTIDFROMLCID( lcid
) == SORT_HUNGARIAN_TECHNICAL
) return L
"Technical";
872 if (SORTIDFROMLCID( lcid
) == SORT_INVARIANT_MATH
) return L
"Default";
873 return L
"Maths Alphanumerics";
876 switch (SORTIDFROMLCID( lcid
))
878 case SORT_JAPANESE_XJIS
: return L
"XJIS";
879 case SORT_JAPANESE_UNICODE
: return L
"Unicode";
880 case SORT_JAPANESE_RADICALSTROKE
: return L
"Radical/Stroke";
885 if (SORTIDFROMLCID( lcid
) == SORT_KOREAN_UNICODE
) return L
"Unicode";
886 return L
"Dictionary";
889 switch (SUBLANGID( lcid
))
891 case SUBLANG_NEUTRAL
:
892 case SUBLANG_SPANISH_MODERN
:
893 return L
"International";
894 case SUBLANG_DEFAULT
:
895 return L
"Traditional";
903 /* get locale information from the locale.nls file */
904 static int get_locale_info( const NLS_LOCALE_DATA
*locale
, LCID lcid
, LCTYPE type
,
905 WCHAR
*buffer
, int len
)
907 static const WCHAR spermille
[] = { 0x2030, 0 }; /* this one seems hardcoded */
908 static const BYTE ipossignposn
[] = { 3, 3, 4, 2, 1, 1, 3, 4, 1, 3, 4, 2, 4, 3, 3, 1 };
909 static const BYTE inegsignposn
[] = { 0, 3, 4, 2, 0, 1, 3, 4, 1, 3, 4, 2, 4, 3, 0, 0 };
910 static const BYTE inegsymprecedes
[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, };
912 WCHAR
*str
, *end
, tmp
[80];
916 if (locale
!= user_locale
) type
|= LOCALE_NOUSEROVERRIDE
;
918 switch (LOWORD(type
))
920 case LOCALE_ILANGUAGE
:
921 /* return default language for neutral locales */
922 val
= locale
->inotneutral
? locale
->ilanguage
: locale
->idefaultlanguage
;
923 return locale_return_number( val
, type
, buffer
, len
);
925 case LOCALE_SLOCALIZEDDISPLAYNAME
:
926 /* FIXME: localization */
927 return locale_return_string( locale
->sengdisplayname
, type
, buffer
, len
);
929 case LOCALE_SABBREVLANGNAME
:
930 return locale_return_string( locale
->sabbrevlangname
, type
, buffer
, len
);
932 case LOCALE_SNATIVELANGNAME
:
933 return locale_return_string( locale
->snativelangname
, type
, buffer
, len
);
935 case LOCALE_ICOUNTRY
:
936 if ((ret
= locale_return_reg_number( &entry_icountry
, type
, buffer
, len
)) != -1) return ret
;
937 return locale_return_number( locale
->icountry
, type
, buffer
, len
);
939 case LOCALE_SLOCALIZEDCOUNTRYNAME
:
940 /* FIXME: localization */
941 return locale_return_string( locale
->sengcountry
, type
, buffer
, len
);
943 case LOCALE_SABBREVCTRYNAME
:
944 return locale_return_string( locale
->sabbrevctryname
, type
, buffer
, len
);
946 case LOCALE_SNATIVECTRYNAME
:
947 return locale_return_string( locale
->snativectryname
, type
, buffer
, len
);
949 case LOCALE_IDEFAULTLANGUAGE
:
950 return locale_return_number( locale
->idefaultlanguage
, type
, buffer
, len
);
952 case LOCALE_IDEFAULTCOUNTRY
:
953 return locale_return_number( locale
->icountry
, type
, buffer
, len
);
955 case LOCALE_IDEFAULTCODEPAGE
:
956 val
= locale
->idefaultcodepage
== CP_UTF8
? CP_OEMCP
: locale
->idefaultcodepage
;
957 return locale_return_number( val
, type
, buffer
, len
);
960 if ((ret
= locale_return_reg_string( &entry_slist
, type
, buffer
, len
)) != -1) return ret
;
961 return locale_return_string( locale
->slist
, type
, buffer
, len
);
963 case LOCALE_IMEASURE
:
964 if ((ret
= locale_return_reg_number( &entry_imeasure
, type
, buffer
, len
)) != -1) return ret
;
965 return locale_return_number( locale
->imeasure
, type
, buffer
, len
);
967 case LOCALE_SDECIMAL
:
968 if ((ret
= locale_return_reg_string( &entry_sdecimal
, type
, buffer
, len
)) != -1) return ret
;
969 return locale_return_string( locale
->sdecimal
, type
, buffer
, len
);
971 case LOCALE_STHOUSAND
:
972 if ((ret
= locale_return_reg_string( &entry_sthousand
, type
, buffer
, len
)) != -1) return ret
;
973 return locale_return_string( locale
->sthousand
, type
, buffer
, len
);
975 case LOCALE_SGROUPING
:
976 if ((ret
= locale_return_reg_string( &entry_sgrouping
, type
, buffer
, len
)) != -1) return ret
;
977 return locale_return_grouping( locale
->sgrouping
, type
, buffer
, len
);
980 if ((ret
= locale_return_reg_number( &entry_idigits
, type
, buffer
, len
)) != -1) return ret
;
981 return locale_return_number( locale
->idigits
, type
, buffer
, len
);
984 if ((ret
= locale_return_reg_number( &entry_ilzero
, type
, buffer
, len
)) != -1) return ret
;
985 return locale_return_number( locale
->ilzero
, type
, buffer
, len
);
987 case LOCALE_SNATIVEDIGITS
:
988 if ((ret
= locale_return_reg_string( &entry_snativedigits
, type
, buffer
, len
)) != -1) return ret
;
989 return locale_return_strarray_concat( locale
->snativedigits
, type
, buffer
, len
);
991 case LOCALE_SCURRENCY
:
992 if ((ret
= locale_return_reg_string( &entry_scurrency
, type
, buffer
, len
)) != -1) return ret
;
993 return locale_return_string( locale
->scurrency
, type
, buffer
, len
);
995 case LOCALE_SINTLSYMBOL
:
996 return locale_return_string( locale
->sintlsymbol
, type
, buffer
, len
);
998 case LOCALE_SMONDECIMALSEP
:
999 if ((ret
= locale_return_reg_string( &entry_smondecimalsep
, type
, buffer
, len
)) != -1) return ret
;
1000 return locale_return_string( locale
->smondecimalsep
, type
, buffer
, len
);
1002 case LOCALE_SMONTHOUSANDSEP
:
1003 if ((ret
= locale_return_reg_string( &entry_smonthousandsep
, type
, buffer
, len
)) != -1) return ret
;
1004 return locale_return_string( locale
->smonthousandsep
, type
, buffer
, len
);
1006 case LOCALE_SMONGROUPING
:
1007 if ((ret
= locale_return_reg_string( &entry_smongrouping
, type
, buffer
, len
)) != -1) return ret
;
1008 return locale_return_grouping( locale
->smongrouping
, type
, buffer
, len
);
1010 case LOCALE_ICURRDIGITS
:
1011 case LOCALE_IINTLCURRDIGITS
:
1012 if ((ret
= locale_return_reg_number( &entry_icurrdigits
, type
, buffer
, len
)) != -1) return ret
;
1013 return locale_return_number( locale
->icurrdigits
, type
, buffer
, len
);
1015 case LOCALE_ICURRENCY
:
1016 if ((ret
= locale_return_reg_number( &entry_icurrency
, type
, buffer
, len
)) != -1) return ret
;
1017 return locale_return_number( locale
->icurrency
, type
, buffer
, len
);
1019 case LOCALE_INEGCURR
:
1020 if ((ret
= locale_return_reg_number( &entry_inegcurr
, type
, buffer
, len
)) != -1) return ret
;
1021 return locale_return_number( locale
->inegcurr
, type
, buffer
, len
);
1024 if (!get_locale_info( locale
, lcid
, LOCALE_SSHORTDATE
| (type
& LOCALE_NOUSEROVERRIDE
),
1025 tmp
, ARRAY_SIZE( tmp
))) break;
1026 if (!(str
= find_format( tmp
, L
"dMy" ))) break;
1027 while (str
[1] == str
[0]) str
++; /* skip repeated chars */
1028 if (!(end
= find_format( ++str
, L
"dMy" ))) break;
1030 return locale_return_data( str
, end
- str
, type
, buffer
, len
);
1033 if (!get_locale_info( locale
, lcid
, LOCALE_STIMEFORMAT
| (type
& LOCALE_NOUSEROVERRIDE
),
1034 tmp
, ARRAY_SIZE( tmp
))) break;
1035 if (!(str
= find_format( tmp
, L
"Hhms" ))) break;
1036 while (str
[1] == str
[0]) str
++; /* skip repeated chars */
1037 if (!(end
= find_format( ++str
, L
"Hhms" ))) break;
1039 return locale_return_data( str
, end
- str
, type
, buffer
, len
);
1041 case LOCALE_SSHORTDATE
:
1042 if ((ret
= locale_return_reg_string( &entry_sshortdate
, type
, buffer
, len
)) != -1) return ret
;
1043 return locale_return_strarray( locale
->sshortdate
, 0, type
, buffer
, len
);
1045 case LOCALE_SLONGDATE
:
1046 if ((ret
= locale_return_reg_string( &entry_slongdate
, type
, buffer
, len
)) != -1) return ret
;
1047 return locale_return_strarray( locale
->slongdate
, 0, type
, buffer
, len
);
1050 if (!get_locale_info( locale
, lcid
, LOCALE_SSHORTDATE
| (type
& LOCALE_NOUSEROVERRIDE
),
1051 tmp
, ARRAY_SIZE( tmp
))) break;
1052 /* if both year and day are found before month, the last one takes precedence */
1053 for (val
= 0, str
= find_format( tmp
, L
"dMy" ); str
; str
= find_format( str
+ 1, L
"dMy" ))
1055 if (*str
== 'M') break;
1056 val
= (*str
== 'y' ? 2 : 1);
1058 return locale_return_number( val
, type
, buffer
, len
);
1061 if (!get_locale_info( locale
, lcid
, LOCALE_SLONGDATE
| (type
& LOCALE_NOUSEROVERRIDE
),
1062 tmp
, ARRAY_SIZE( tmp
))) break;
1063 /* if both year and day are found before month, the last one takes precedence */
1064 for (val
= 0, str
= find_format( tmp
, L
"dMy" ); str
; str
= find_format( str
+ 1, L
"dMy" ))
1066 if (*str
== 'M') break;
1067 val
= (*str
== 'y' ? 2 : 1);
1069 return locale_return_number( val
, type
, buffer
, len
);
1072 if (!get_locale_info( locale
, lcid
, LOCALE_STIMEFORMAT
| (type
& LOCALE_NOUSEROVERRIDE
),
1073 tmp
, ARRAY_SIZE( tmp
))) break;
1074 if (!(str
= find_format( tmp
, L
"Hh" ))) break;
1075 return locale_return_number( *str
== 'H', type
, buffer
, len
);
1077 case LOCALE_ICENTURY
:
1078 if (!get_locale_info( locale
, lcid
, LOCALE_SSHORTDATE
| (type
& LOCALE_NOUSEROVERRIDE
),
1079 tmp
, ARRAY_SIZE( tmp
))) break;
1080 if (!(str
= find_format( tmp
, L
"y" ))) break;
1081 return locale_return_number( !wcsncmp( str
, L
"yyyy", 4 ), type
, buffer
, len
);
1083 case LOCALE_ITLZERO
:
1084 if (!get_locale_info( locale
, lcid
, LOCALE_STIMEFORMAT
| (type
& LOCALE_NOUSEROVERRIDE
),
1085 tmp
, ARRAY_SIZE( tmp
))) break;
1086 if (!(str
= find_format( tmp
, L
"Hh" ))) break;
1087 return locale_return_number( str
[1] == str
[0], type
, buffer
, len
);
1089 case LOCALE_IDAYLZERO
:
1090 if (!get_locale_info( locale
, lcid
, LOCALE_SSHORTDATE
| (type
& LOCALE_NOUSEROVERRIDE
),
1091 tmp
, ARRAY_SIZE( tmp
))) break;
1092 if (!(str
= find_format( tmp
, L
"d" ))) break;
1093 return locale_return_number( str
[1] == 'd', type
, buffer
, len
);
1095 case LOCALE_IMONLZERO
:
1096 if (!get_locale_info( locale
, lcid
, LOCALE_SSHORTDATE
| (type
& LOCALE_NOUSEROVERRIDE
),
1097 tmp
, ARRAY_SIZE( tmp
))) break;
1098 if (!(str
= find_format( tmp
, L
"M" ))) break;
1099 return locale_return_number( str
[1] == 'M', type
, buffer
, len
);
1102 if ((ret
= locale_return_reg_string( &entry_s1159
, type
, buffer
, len
)) != -1) return ret
;
1103 return locale_return_string( locale
->s1159
, type
, buffer
, len
);
1106 if ((ret
= locale_return_reg_string( &entry_s2359
, type
, buffer
, len
)) != -1) return ret
;
1107 return locale_return_string( locale
->s2359
, type
, buffer
, len
);
1109 case LOCALE_SDAYNAME1
:
1110 case LOCALE_SDAYNAME2
:
1111 case LOCALE_SDAYNAME3
:
1112 case LOCALE_SDAYNAME4
:
1113 case LOCALE_SDAYNAME5
:
1114 case LOCALE_SDAYNAME6
:
1115 case LOCALE_SDAYNAME7
:
1116 return locale_return_strarray( locale
->sdayname
,
1117 LOWORD(type
- LOCALE_SDAYNAME1
+ 1) % 7, type
, buffer
, len
);
1119 case LOCALE_SABBREVDAYNAME1
:
1120 case LOCALE_SABBREVDAYNAME2
:
1121 case LOCALE_SABBREVDAYNAME3
:
1122 case LOCALE_SABBREVDAYNAME4
:
1123 case LOCALE_SABBREVDAYNAME5
:
1124 case LOCALE_SABBREVDAYNAME6
:
1125 case LOCALE_SABBREVDAYNAME7
:
1126 return locale_return_strarray( locale
->sabbrevdayname
,
1127 LOWORD(type
- LOCALE_SABBREVDAYNAME1
+ 1) % 7, type
, buffer
, len
);
1129 case LOCALE_SMONTHNAME1
:
1130 case LOCALE_SMONTHNAME2
:
1131 case LOCALE_SMONTHNAME3
:
1132 case LOCALE_SMONTHNAME4
:
1133 case LOCALE_SMONTHNAME5
:
1134 case LOCALE_SMONTHNAME6
:
1135 case LOCALE_SMONTHNAME7
:
1136 case LOCALE_SMONTHNAME8
:
1137 case LOCALE_SMONTHNAME9
:
1138 case LOCALE_SMONTHNAME10
:
1139 case LOCALE_SMONTHNAME11
:
1140 case LOCALE_SMONTHNAME12
:
1141 return locale_return_strarray( ((type
& LOCALE_RETURN_GENITIVE_NAMES
) && locale
->sgenitivemonth
) ?
1142 locale
->sgenitivemonth
: locale
->smonthname
,
1143 type
- LOCALE_SMONTHNAME1
, type
, buffer
, len
);
1145 case LOCALE_SABBREVMONTHNAME1
:
1146 case LOCALE_SABBREVMONTHNAME2
:
1147 case LOCALE_SABBREVMONTHNAME3
:
1148 case LOCALE_SABBREVMONTHNAME4
:
1149 case LOCALE_SABBREVMONTHNAME5
:
1150 case LOCALE_SABBREVMONTHNAME6
:
1151 case LOCALE_SABBREVMONTHNAME7
:
1152 case LOCALE_SABBREVMONTHNAME8
:
1153 case LOCALE_SABBREVMONTHNAME9
:
1154 case LOCALE_SABBREVMONTHNAME10
:
1155 case LOCALE_SABBREVMONTHNAME11
:
1156 case LOCALE_SABBREVMONTHNAME12
:
1157 return locale_return_strarray( ((type
& LOCALE_RETURN_GENITIVE_NAMES
) && locale
->sabbrevgenitivemonth
) ?
1158 locale
->sabbrevgenitivemonth
: locale
->sabbrevmonthname
,
1159 type
- LOCALE_SABBREVMONTHNAME1
, type
, buffer
, len
);
1161 case LOCALE_SPOSITIVESIGN
:
1162 if ((ret
= locale_return_reg_string( &entry_spositivesign
, type
, buffer
, len
)) != -1) return ret
;
1163 return locale_return_string( locale
->spositivesign
, type
, buffer
, len
);
1165 case LOCALE_SNEGATIVESIGN
:
1166 if ((ret
= locale_return_reg_string( &entry_snegativesign
, type
, buffer
, len
)) != -1) return ret
;
1167 return locale_return_string( locale
->snegativesign
, type
, buffer
, len
);
1169 case LOCALE_IPOSSIGNPOSN
:
1170 if (!get_locale_info( locale
, lcid
,
1171 LOCALE_INEGCURR
| LOCALE_RETURN_NUMBER
| (type
& LOCALE_NOUSEROVERRIDE
),
1172 (WCHAR
*)&val
, sizeof(val
)/sizeof(WCHAR
) )) break;
1173 return locale_return_number( ipossignposn
[val
], type
, buffer
, len
);
1175 case LOCALE_INEGSIGNPOSN
:
1176 if (!get_locale_info( locale
, lcid
,
1177 LOCALE_INEGCURR
| LOCALE_RETURN_NUMBER
| (type
& LOCALE_NOUSEROVERRIDE
),
1178 (WCHAR
*)&val
, sizeof(val
)/sizeof(WCHAR
) )) break;
1179 return locale_return_number( inegsignposn
[val
], type
, buffer
, len
);
1181 case LOCALE_IPOSSYMPRECEDES
:
1182 if (!get_locale_info( locale
, lcid
,
1183 LOCALE_ICURRENCY
| LOCALE_RETURN_NUMBER
| (type
& LOCALE_NOUSEROVERRIDE
),
1184 (WCHAR
*)&val
, sizeof(val
)/sizeof(WCHAR
) )) break;
1185 return locale_return_number( !(val
& 1), type
, buffer
, len
);
1187 case LOCALE_IPOSSEPBYSPACE
:
1188 if (!get_locale_info( locale
, lcid
,
1189 LOCALE_ICURRENCY
| LOCALE_RETURN_NUMBER
| (type
& LOCALE_NOUSEROVERRIDE
),
1190 (WCHAR
*)&val
, sizeof(val
)/sizeof(WCHAR
) )) break;
1191 return locale_return_number( !!(val
& 2), type
, buffer
, len
);
1193 case LOCALE_INEGSYMPRECEDES
:
1194 if (!get_locale_info( locale
, lcid
,
1195 LOCALE_INEGCURR
| LOCALE_RETURN_NUMBER
| (type
& LOCALE_NOUSEROVERRIDE
),
1196 (WCHAR
*)&val
, sizeof(val
)/sizeof(WCHAR
) )) break;
1197 return locale_return_number( inegsymprecedes
[val
], type
, buffer
, len
);
1199 case LOCALE_INEGSEPBYSPACE
:
1200 if (!get_locale_info( locale
, lcid
,
1201 LOCALE_INEGCURR
| LOCALE_RETURN_NUMBER
| (type
& LOCALE_NOUSEROVERRIDE
),
1202 (WCHAR
*)&val
, sizeof(val
)/sizeof(WCHAR
) )) break;
1203 return locale_return_number( (val
>= 8), type
, buffer
, len
);
1205 case LOCALE_FONTSIGNATURE
:
1206 return locale_return_data( locale_strings
+ locale
->fontsignature
+ 1,
1207 locale_strings
[locale
->fontsignature
], type
, buffer
, len
);
1209 case LOCALE_SISO639LANGNAME
:
1210 return locale_return_string( locale
->siso639langname
, type
, buffer
, len
);
1212 case LOCALE_SISO3166CTRYNAME
:
1213 return locale_return_string( locale
->siso3166ctryname
, type
, buffer
, len
);
1216 return locale_return_number( locale
->igeoid
, type
, buffer
, len
);
1219 if (SORTIDFROMLCID(lcid
)) /* custom sort locale */
1221 const NLS_LOCALE_LCID_INDEX
*entry
= find_lcid_entry( lcid
& ~0x80000000 );
1222 if (entry
) return locale_return_string( entry
->name
, type
, buffer
, len
);
1224 return locale_return_string( locale
->sname
, type
, buffer
, len
);
1226 case LOCALE_SDURATION
:
1227 return locale_return_strarray( locale
->sduration
, 0, type
, buffer
, len
);
1229 case LOCALE_SKEYBOARDSTOINSTALL
:
1230 return locale_return_string( locale
->skeyboardstoinstall
, type
, buffer
, len
);
1232 case LOCALE_SSHORTESTDAYNAME1
:
1233 case LOCALE_SSHORTESTDAYNAME2
:
1234 case LOCALE_SSHORTESTDAYNAME3
:
1235 case LOCALE_SSHORTESTDAYNAME4
:
1236 case LOCALE_SSHORTESTDAYNAME5
:
1237 case LOCALE_SSHORTESTDAYNAME6
:
1238 case LOCALE_SSHORTESTDAYNAME7
:
1239 return locale_return_strarray( locale
->sshortestdayname
,
1240 LOWORD(type
- LOCALE_SSHORTESTDAYNAME1
+ 1) % 7, type
, buffer
, len
);
1242 case LOCALE_SISO639LANGNAME2
:
1243 return locale_return_string( locale
->siso639langname2
, type
, buffer
, len
);
1245 case LOCALE_SISO3166CTRYNAME2
:
1246 return locale_return_string( locale
->siso3166ctryname2
, type
, buffer
, len
);
1249 return locale_return_string( locale
->snan
, type
, buffer
, len
);
1251 case LOCALE_SPOSINFINITY
:
1252 return locale_return_string( locale
->sposinfinity
, type
, buffer
, len
);
1254 case LOCALE_SNEGINFINITY
:
1255 return locale_return_string( locale
->sneginfinity
, type
, buffer
, len
);
1257 case LOCALE_SSCRIPTS
:
1258 return locale_return_string( locale
->sscripts
, type
, buffer
, len
);
1260 case LOCALE_SPARENT
:
1261 return locale_return_string( locale
->sparent
, type
, buffer
, len
);
1263 case LOCALE_SCONSOLEFALLBACKNAME
:
1264 return locale_return_string( locale
->sconsolefallbackname
, type
, buffer
, len
);
1266 case LOCALE_SLOCALIZEDLANGUAGENAME
:
1267 /* FIXME: localization */
1268 return locale_return_string( locale
->senglanguage
, type
, buffer
, len
);
1270 case LOCALE_IREADINGLAYOUT
:
1271 return locale_return_number( locale
->ireadinglayout
, type
, buffer
, len
);
1273 case LOCALE_INEUTRAL
:
1274 return locale_return_number( !locale
->inotneutral
, type
, buffer
, len
);
1276 case LOCALE_SENGLISHDISPLAYNAME
:
1277 return locale_return_string( locale
->sengdisplayname
, type
, buffer
, len
);
1279 case LOCALE_SNATIVEDISPLAYNAME
:
1280 return locale_return_string( locale
->snativedisplayname
, type
, buffer
, len
);
1282 case LOCALE_INEGATIVEPERCENT
:
1283 return locale_return_number( locale
->inegativepercent
, type
, buffer
, len
);
1285 case LOCALE_IPOSITIVEPERCENT
:
1286 return locale_return_number( locale
->ipositivepercent
, type
, buffer
, len
);
1288 case LOCALE_SPERCENT
:
1289 return locale_return_string( locale
->spercent
, type
, buffer
, len
);
1291 case LOCALE_SPERMILLE
:
1292 return locale_return_data( spermille
, ARRAY_SIZE(spermille
), type
, buffer
, len
);
1294 case LOCALE_SMONTHDAY
:
1295 return locale_return_strarray( locale
->smonthday
, 0, type
, buffer
, len
);
1297 case LOCALE_SSHORTTIME
:
1298 if ((ret
= locale_return_reg_string( &entry_sshorttime
, type
, buffer
, len
)) != -1) return ret
;
1299 return locale_return_strarray( locale
->sshorttime
, 0, type
, buffer
, len
);
1301 case LOCALE_SOPENTYPELANGUAGETAG
:
1302 return locale_return_string( locale
->sopentypelanguagetag
, type
, buffer
, len
);
1304 case LOCALE_SSORTLOCALE
:
1305 if (SORTIDFROMLCID(lcid
)) /* custom sort locale */
1307 const NLS_LOCALE_LCID_INDEX
*entry
= find_lcid_entry( lcid
& ~0x80000000 );
1308 if (entry
) return locale_return_string( entry
->name
, type
, buffer
, len
);
1310 return locale_return_string( locale
->ssortlocale
, type
, buffer
, len
);
1312 case LOCALE_SRELATIVELONGDATE
:
1313 return locale_return_string( locale
->srelativelongdate
, type
, buffer
, len
);
1315 case 0x007d: /* undocumented */
1316 return locale_return_number( 0, type
, buffer
, len
);
1318 case LOCALE_SSHORTESTAM
:
1319 return locale_return_string( locale
->sshortestam
, type
, buffer
, len
);
1321 case LOCALE_SSHORTESTPM
:
1322 return locale_return_string( locale
->sshortestpm
, type
, buffer
, len
);
1324 case LOCALE_SENGLANGUAGE
:
1325 return locale_return_string( locale
->senglanguage
, type
, buffer
, len
);
1327 case LOCALE_SENGCOUNTRY
:
1328 return locale_return_string( locale
->sengcountry
, type
, buffer
, len
);
1330 case LOCALE_STIMEFORMAT
:
1331 if ((ret
= locale_return_reg_string( &entry_stimeformat
, type
, buffer
, len
)) != -1) return ret
;
1332 return locale_return_strarray( locale
->stimeformat
, 0, type
, buffer
, len
);
1334 case LOCALE_IDEFAULTANSICODEPAGE
:
1335 val
= locale
->idefaultansicodepage
== CP_UTF8
? CP_ACP
: locale
->idefaultansicodepage
;
1336 return locale_return_number( val
, type
, buffer
, len
);
1338 case LOCALE_ITIMEMARKPOSN
:
1339 if (!get_locale_info( locale
, lcid
, LOCALE_STIMEFORMAT
| (type
& LOCALE_NOUSEROVERRIDE
),
1340 tmp
, ARRAY_SIZE( tmp
))) break;
1341 if (!(str
= find_format( tmp
, L
"Hhmst" ))) break;
1342 return locale_return_number( *str
== 't', type
, buffer
, len
);
1344 case LOCALE_SYEARMONTH
:
1345 if ((ret
= locale_return_reg_string( &entry_syearmonth
, type
, buffer
, len
)) != -1) return ret
;
1346 return locale_return_strarray( locale
->syearmonth
, 0, type
, buffer
, len
);
1348 case LOCALE_SENGCURRNAME
:
1349 return locale_return_string( locale
->sengcurrname
, type
, buffer
, len
);
1351 case LOCALE_SNATIVECURRNAME
:
1352 return locale_return_string( locale
->snativecurrname
, type
, buffer
, len
);
1354 case LOCALE_ICALENDARTYPE
:
1355 if ((ret
= locale_return_reg_number( &entry_icalendartype
, type
, buffer
, len
)) != -1) return ret
;
1356 return locale_return_number( locale_strings
[locale
->scalendartype
+ 1], type
, buffer
, len
);
1358 case LOCALE_IPAPERSIZE
:
1359 if ((ret
= locale_return_reg_number( &entry_ipapersize
, type
, buffer
, len
)) != -1) return ret
;
1360 return locale_return_number( locale
->ipapersize
, type
, buffer
, len
);
1362 case LOCALE_IOPTIONALCALENDAR
:
1363 return locale_return_number( locale_strings
[locale
->scalendartype
+ 2], type
, buffer
, len
);
1365 case LOCALE_IFIRSTDAYOFWEEK
:
1366 if ((ret
= locale_return_reg_number( &entry_ifirstdayofweek
, type
, buffer
, len
)) != -1) return ret
;
1367 return locale_return_number( (locale
->ifirstdayofweek
+ 6) % 7, type
, buffer
, len
);
1369 case LOCALE_IFIRSTWEEKOFYEAR
:
1370 if ((ret
= locale_return_reg_number( &entry_ifirstweekofyear
, type
, buffer
, len
)) != -1) return ret
;
1371 return locale_return_number( locale
->ifirstweekofyear
, type
, buffer
, len
);
1373 case LOCALE_SMONTHNAME13
:
1374 return locale_return_strarray( ((type
& LOCALE_RETURN_GENITIVE_NAMES
) && locale
->sgenitivemonth
) ?
1375 locale
->sgenitivemonth
: locale
->smonthname
,
1376 12, type
, buffer
, len
);
1378 case LOCALE_SABBREVMONTHNAME13
:
1379 return locale_return_strarray( ((type
& LOCALE_RETURN_GENITIVE_NAMES
) && locale
->sabbrevgenitivemonth
) ?
1380 locale
->sabbrevgenitivemonth
: locale
->sabbrevmonthname
,
1381 12, type
, buffer
, len
);
1383 case LOCALE_INEGNUMBER
:
1384 if ((ret
= locale_return_reg_number( &entry_inegnumber
, type
, buffer
, len
)) != -1) return ret
;
1385 return locale_return_number( locale
->inegnumber
, type
, buffer
, len
);
1387 case LOCALE_IDEFAULTMACCODEPAGE
:
1388 val
= locale
->idefaultmaccodepage
== CP_UTF8
? CP_MACCP
: locale
->idefaultmaccodepage
;
1389 return locale_return_number( val
, type
, buffer
, len
);
1391 case LOCALE_IDEFAULTEBCDICCODEPAGE
:
1392 return locale_return_number( locale
->idefaultebcdiccodepage
, type
, buffer
, len
);
1394 case LOCALE_SSORTNAME
:
1395 sort
= get_locale_sortname( lcid
);
1396 return locale_return_data( sort
, wcslen(sort
) + 1, type
, buffer
, len
);
1398 case LOCALE_IDIGITSUBSTITUTION
:
1399 if ((ret
= locale_return_reg_number( &entry_idigitsubstitution
, type
, buffer
, len
)) != -1) return ret
;
1400 return locale_return_number( locale
->idigitsubstitution
, type
, buffer
, len
);
1402 SetLastError( ERROR_INVALID_FLAGS
);
1407 /* get geo information from the locale.nls file */
1408 static int get_geo_info( const struct geo_id
*geo
, enum SYSGEOTYPE type
,
1409 WCHAR
*buffer
, int len
, LANGID lang
)
1412 const WCHAR
*str
= tmp
;
1418 if (geo
->class != GEOCLASS_NATION
) return 0;
1421 swprintf( tmp
, ARRAY_SIZE(tmp
), L
"%u", geo
->id
);
1423 case GEO_ISO_UN_NUMBER
:
1424 swprintf( tmp
, ARRAY_SIZE(tmp
), L
"%03u", geo
->uncode
);
1427 swprintf( tmp
, ARRAY_SIZE(tmp
), L
"%u", geo
->parent
);
1429 case GEO_DIALINGCODE
:
1430 swprintf( tmp
, ARRAY_SIZE(tmp
), L
"%u", geo
->dialcode
);
1439 str
= geo
->latitude
;
1442 str
= geo
->longitude
;
1444 case GEO_CURRENCYCODE
:
1445 str
= geo
->currcode
;
1447 case GEO_CURRENCYSYMBOL
:
1448 str
= geo
->currsymbol
;
1452 case GEO_FRIENDLYNAME
:
1453 case GEO_OFFICIALNAME
:
1455 case GEO_OFFICIALLANGUAGES
:
1457 FIXME( "type %u is not supported\n", type
);
1458 SetLastError( ERROR_CALL_NOT_IMPLEMENTED
);
1461 SetLastError( ERROR_INVALID_FLAGS
);
1465 ret
= lstrlenW(str
) + 1;
1466 if (!buffer
|| !len
) return ret
;
1468 memcpy( buffer
, str
, min( ret
, len
) * sizeof(WCHAR
) );
1469 if (len
< ret
) SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1470 return len
< ret
? 0 : ret
;
1474 /* update a registry value based on the current user locale info */
1475 static void update_registry_value( UINT type
, const WCHAR
*value
)
1478 UINT len
= get_locale_info( user_locale
, user_lcid
, type
, buffer
, ARRAY_SIZE(buffer
) );
1479 if (len
) RegSetValueExW( intl_key
, value
, 0, REG_SZ
, (BYTE
*)buffer
, len
* sizeof(WCHAR
) );
1483 /* update all registry values upon user locale change */
1484 static void update_locale_registry(void)
1489 len
= swprintf( buffer
, ARRAY_SIZE(buffer
), L
"%08x", GetUserDefaultLCID() );
1490 RegSetValueExW( intl_key
, L
"Locale", 0, REG_SZ
, (BYTE
*)buffer
, (len
+ 1) * sizeof(WCHAR
) );
1492 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ICALENDARTYPE
, entry_icalendartype
.value
);
1493 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ICOUNTRY
, entry_icountry
.value
);
1494 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ICURRDIGITS
, entry_icurrdigits
.value
);
1495 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ICURRENCY
, entry_icurrency
.value
);
1496 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_IDIGITS
, entry_idigits
.value
);
1497 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_IDIGITSUBSTITUTION
, entry_idigitsubstitution
.value
);
1498 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_IFIRSTDAYOFWEEK
, entry_ifirstdayofweek
.value
);
1499 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_IFIRSTWEEKOFYEAR
, entry_ifirstweekofyear
.value
);
1500 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ILZERO
, entry_ilzero
.value
);
1501 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_IMEASURE
, entry_imeasure
.value
);
1502 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_INEGCURR
, entry_inegcurr
.value
);
1503 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_INEGNUMBER
, entry_inegnumber
.value
);
1504 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_IPAPERSIZE
, entry_ipapersize
.value
);
1505 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_S1159
, entry_s1159
.value
);
1506 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_S2359
, entry_s2359
.value
);
1507 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SCURRENCY
, entry_scurrency
.value
);
1508 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SDECIMAL
, entry_sdecimal
.value
);
1509 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SGROUPING
, entry_sgrouping
.value
);
1510 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SLIST
, entry_slist
.value
);
1511 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SLONGDATE
, entry_slongdate
.value
);
1512 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SMONDECIMALSEP
, entry_smondecimalsep
.value
);
1513 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SMONGROUPING
, entry_smongrouping
.value
);
1514 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SMONTHOUSANDSEP
, entry_smonthousandsep
.value
);
1515 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SNATIVEDIGITS
, entry_snativedigits
.value
);
1516 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SNEGATIVESIGN
, entry_snegativesign
.value
);
1517 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SPOSITIVESIGN
, entry_spositivesign
.value
);
1518 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SSHORTDATE
, entry_sshortdate
.value
);
1519 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SSHORTTIME
, entry_sshorttime
.value
);
1520 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_STHOUSAND
, entry_sthousand
.value
);
1521 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_STIMEFORMAT
, entry_stimeformat
.value
);
1522 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SYEARMONTH
, entry_syearmonth
.value
);
1523 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_IDATE
, L
"iDate" );
1524 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ITIME
, L
"iTime" );
1525 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ITIMEMARKPOSN
, L
"iTimePrefix" );
1526 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_ITLZERO
, L
"iTLZero" );
1527 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SDATE
, L
"sDate" );
1528 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_STIME
, L
"sTime" );
1529 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SABBREVLANGNAME
, L
"sLanguage" );
1530 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SCOUNTRY
, L
"sCountry" );
1531 update_registry_value( LOCALE_NOUSEROVERRIDE
| LOCALE_SNAME
, L
"LocaleName" );
1532 SetUserGeoID( user_locale
->igeoid
);
1536 /***********************************************************************
1539 void init_locale( HMODULE module
)
1541 UINT ansi_cp
= 0, oem_cp
= 0;
1542 USHORT
*ansi_ptr
, *oem_ptr
;
1544 WCHAR bufferW
[LOCALE_NAME_MAX_LENGTH
];
1545 DYNAMIC_TIME_ZONE_INFORMATION timezone
;
1551 NtQueryDefaultLocale( FALSE
, &system_lcid
);
1552 NtQueryDefaultLocale( FALSE
, &user_lcid
);
1553 system_locale
= get_locale_by_id( &system_lcid
, 0 );
1554 user_locale
= get_locale_by_id( &user_lcid
, 0 );
1555 kernelbase_handle
= module
;
1557 if (GetEnvironmentVariableW( L
"WINEUNIXCP", bufferW
, ARRAY_SIZE(bufferW
) ))
1558 unix_cp
= wcstoul( bufferW
, NULL
, 10 );
1560 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT
, LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
1561 (WCHAR
*)&ansi_cp
, sizeof(ansi_cp
)/sizeof(WCHAR
) );
1562 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT
, LOCALE_IDEFAULTMACCODEPAGE
| LOCALE_RETURN_NUMBER
,
1563 (WCHAR
*)&mac_cp
, sizeof(mac_cp
)/sizeof(WCHAR
) );
1564 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT
, LOCALE_IDEFAULTCODEPAGE
| LOCALE_RETURN_NUMBER
,
1565 (WCHAR
*)&oem_cp
, sizeof(oem_cp
)/sizeof(WCHAR
) );
1567 NtGetNlsSectionPtr( 9, 0, NULL
, &sort_ptr
, &size
);
1568 NtGetNlsSectionPtr( 12, NormalizationC
, NULL
, (void **)&norm_info
, &size
);
1569 init_sortkeys( sort_ptr
);
1571 if (!ansi_cp
|| NtGetNlsSectionPtr( 11, ansi_cp
, NULL
, (void **)&ansi_ptr
, &size
))
1572 NtGetNlsSectionPtr( 11, 1252, NULL
, (void **)&ansi_ptr
, &size
);
1573 if (!oem_cp
|| NtGetNlsSectionPtr( 11, oem_cp
, 0, (void **)&oem_ptr
, &size
))
1574 NtGetNlsSectionPtr( 11, 437, NULL
, (void **)&oem_ptr
, &size
);
1575 NtCurrentTeb()->Peb
->AnsiCodePageData
= ansi_ptr
;
1576 NtCurrentTeb()->Peb
->OemCodePageData
= oem_ptr
;
1577 NtCurrentTeb()->Peb
->UnicodeCaseTableData
= sort
.casemap
;
1578 RtlInitNlsTables( ansi_ptr
, oem_ptr
, sort
.casemap
, &nls_info
);
1579 RtlResetRtlTranslations( &nls_info
);
1581 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, L
"System\\CurrentControlSet\\Control\\Nls",
1582 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &nls_key
, NULL
);
1583 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1584 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &tz_key
, NULL
);
1585 RegCreateKeyExW( HKEY_CURRENT_USER
, L
"Control Panel\\International",
1586 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &intl_key
, NULL
);
1588 current_locale_sort
= get_language_sort( LOCALE_NAME_USER_DEFAULT
);
1590 if (GetDynamicTimeZoneInformation( &timezone
) != TIME_ZONE_ID_INVALID
&&
1591 !RegCreateKeyExW( HKEY_LOCAL_MACHINE
, L
"System\\CurrentControlSet\\Control\\TimeZoneInformation",
1592 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
))
1594 RegSetValueExW( hkey
, L
"StandardName", 0, REG_SZ
, (BYTE
*)timezone
.StandardName
,
1595 (lstrlenW(timezone
.StandardName
) + 1) * sizeof(WCHAR
) );
1596 RegSetValueExW( hkey
, L
"TimeZoneKeyName", 0, REG_SZ
, (BYTE
*)timezone
.TimeZoneKeyName
,
1597 (lstrlenW(timezone
.TimeZoneKeyName
) + 1) * sizeof(WCHAR
) );
1598 RegCloseKey( hkey
);
1601 /* Update registry contents if the user locale has changed.
1602 * This simulates the action of the Windows control panel. */
1604 count
= sizeof(bufferW
);
1605 if (!RegQueryValueExW( intl_key
, L
"Locale", NULL
, NULL
, (BYTE
*)bufferW
, &count
))
1607 if (wcstoul( bufferW
, NULL
, 16 ) == user_lcid
) return; /* already set correctly */
1608 TRACE( "updating registry, locale changed %s -> %08lx\n", debugstr_w(bufferW
), user_lcid
);
1610 else TRACE( "updating registry, locale changed none -> %08lx\n", user_lcid
);
1612 update_locale_registry();
1614 if (!RegCreateKeyExW( nls_key
, L
"Codepage",
1615 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
))
1617 count
= swprintf( bufferW
, ARRAY_SIZE(bufferW
), L
"%03d", ansi_cp
);
1618 RegSetValueExW( hkey
, L
"ACP", 0, REG_SZ
, (BYTE
*)bufferW
, (count
+ 1) * sizeof(WCHAR
) );
1619 count
= swprintf( bufferW
, ARRAY_SIZE(bufferW
), L
"%03d", oem_cp
);
1620 RegSetValueExW( hkey
, L
"OEMCP", 0, REG_SZ
, (BYTE
*)bufferW
, (count
+ 1) * sizeof(WCHAR
) );
1621 count
= swprintf( bufferW
, ARRAY_SIZE(bufferW
), L
"%03d", mac_cp
);
1622 RegSetValueExW( hkey
, L
"MACCP", 0, REG_SZ
, (BYTE
*)bufferW
, (count
+ 1) * sizeof(WCHAR
) );
1623 RegCloseKey( hkey
);
1628 static inline USHORT
get_table_entry( const USHORT
*table
, WCHAR ch
)
1630 return table
[table
[table
[ch
>> 8] + ((ch
>> 4) & 0x0f)] + (ch
& 0xf)];
1634 static inline WCHAR
casemap( const USHORT
*table
, WCHAR ch
)
1636 return ch
+ table
[table
[table
[ch
>> 8] + ((ch
>> 4) & 0x0f)] + (ch
& 0x0f)];
1640 static inline WORD
get_char_type( DWORD type
, WCHAR ch
)
1642 const BYTE
*ptr
= sort
.ctype_idx
+ ((const WORD
*)sort
.ctype_idx
)[ch
>> 8];
1643 ptr
= sort
.ctype_idx
+ ((const WORD
*)ptr
)[(ch
>> 4) & 0x0f] + (ch
& 0x0f);
1644 return sort
.ctypes
[*ptr
* 3 + type
/ 2];
1648 static BYTE
rol( BYTE val
, BYTE count
)
1650 return (val
<< count
) | (val
>> (8 - count
));
1654 static BYTE
get_char_props( const struct norm_table
*info
, unsigned int ch
)
1656 const BYTE
*level1
= (const BYTE
*)((const USHORT
*)info
+ info
->props_level1
);
1657 const BYTE
*level2
= (const BYTE
*)((const USHORT
*)info
+ info
->props_level2
);
1658 BYTE off
= level1
[ch
/ 128];
1660 if (!off
|| off
>= 0xfb) return rol( off
, 5 );
1661 return level2
[(off
- 1) * 128 + ch
% 128];
1665 static const WCHAR
*get_decomposition( WCHAR ch
, unsigned int *ret_len
)
1667 const struct pair
{ WCHAR src
; USHORT dst
; } *pairs
;
1668 const USHORT
*hash_table
= (const USHORT
*)norm_info
+ norm_info
->decomp_hash
;
1670 unsigned int i
, pos
, end
, len
, hash
;
1673 hash
= ch
% norm_info
->decomp_size
;
1674 pos
= hash_table
[hash
];
1677 if (get_char_props( norm_info
, ch
) != 0xbf) return NULL
;
1678 ret
= (const USHORT
*)norm_info
+ norm_info
->decomp_seq
+ (pos
& 0x1fff);
1683 pairs
= (const struct pair
*)((const USHORT
*)norm_info
+ norm_info
->decomp_map
);
1685 /* find the end of the hash bucket */
1686 for (i
= hash
+ 1; i
< norm_info
->decomp_size
; i
++) if (!(hash_table
[i
] >> 13)) break;
1687 if (i
< norm_info
->decomp_size
) end
= hash_table
[i
];
1688 else for (end
= pos
; pairs
[end
].src
; end
++) ;
1690 for ( ; pos
< end
; pos
++)
1692 if (pairs
[pos
].src
!= (WCHAR
)ch
) continue;
1693 ret
= (const USHORT
*)norm_info
+ norm_info
->decomp_seq
+ (pairs
[pos
].dst
& 0x1fff);
1694 len
= pairs
[pos
].dst
>> 13;
1697 if (pos
>= end
) return NULL
;
1700 if (len
== 7) while (ret
[len
]) len
++;
1701 if (!ret
[0]) len
= 0; /* ignored char */
1707 static WCHAR
compose_chars( WCHAR ch1
, WCHAR ch2
)
1709 const USHORT
*table
= (const USHORT
*)norm_info
+ norm_info
->comp_hash
;
1710 const WCHAR
*chars
= (const USHORT
*)norm_info
+ norm_info
->comp_seq
;
1711 unsigned int hash
, start
, end
, i
;
1714 hash
= (ch1
+ 95 * ch2
) % norm_info
->comp_size
;
1715 start
= table
[hash
];
1716 end
= table
[hash
+ 1];
1719 for (i
= 0; i
< 3; i
++, start
++)
1721 ch
[i
] = chars
[start
];
1722 if (IS_HIGH_SURROGATE( ch
[i
] )) start
++;
1724 if (ch
[0] == ch1
&& ch
[1] == ch2
) return ch
[2];
1730 static UINT
get_lcid_codepage( LCID lcid
, ULONG flags
)
1732 UINT ret
= GetACP();
1734 if (!(flags
& LOCALE_USE_CP_ACP
) && lcid
!= GetSystemDefaultLCID())
1735 GetLocaleInfoW( lcid
, LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
1736 (WCHAR
*)&ret
, sizeof(ret
)/sizeof(WCHAR
) );
1741 static const CPTABLEINFO
*get_codepage_table( UINT codepage
)
1750 return &nls_info
.AnsiTableInfo
;
1752 return &nls_info
.OemTableInfo
;
1757 if (NtCurrentTeb()->CurrentLocale
== GetUserDefaultLCID()) return &nls_info
.AnsiTableInfo
;
1758 codepage
= get_lcid_codepage( NtCurrentTeb()->CurrentLocale
, 0 );
1759 if (!codepage
) return &nls_info
.AnsiTableInfo
;
1762 if (codepage
== nls_info
.AnsiTableInfo
.CodePage
) return &nls_info
.AnsiTableInfo
;
1763 if (codepage
== nls_info
.OemTableInfo
.CodePage
) return &nls_info
.OemTableInfo
;
1767 RtlEnterCriticalSection( &locale_section
);
1769 for (i
= 0; i
< nb_codepages
; i
++) if (codepages
[i
].CodePage
== codepage
) goto done
;
1771 if (i
== ARRAY_SIZE( codepages
))
1773 RtlLeaveCriticalSection( &locale_section
);
1774 ERR( "too many codepages\n" );
1777 if (NtGetNlsSectionPtr( 11, codepage
, NULL
, (void **)&ptr
, &size
))
1779 RtlLeaveCriticalSection( &locale_section
);
1780 SetLastError( ERROR_INVALID_PARAMETER
);
1783 RtlInitCodePageTable( ptr
, &codepages
[i
] );
1786 RtlLeaveCriticalSection( &locale_section
);
1787 return &codepages
[i
];
1791 static const WCHAR
*get_ligature( WCHAR wc
)
1793 int low
= 0, high
= ARRAY_SIZE( ligatures
) -1;
1796 int pos
= (low
+ high
) / 2;
1797 if (ligatures
[pos
][0] < wc
) low
= pos
+ 1;
1798 else if (ligatures
[pos
][0] > wc
) high
= pos
- 1;
1799 else return ligatures
[pos
] + 1;
1805 static NTSTATUS
expand_ligatures( const WCHAR
*src
, int srclen
, WCHAR
*dst
, int *dstlen
)
1807 int i
, len
, pos
= 0;
1808 NTSTATUS ret
= STATUS_SUCCESS
;
1809 const WCHAR
*expand
;
1811 for (i
= 0; i
< srclen
; i
++)
1813 if (!(expand
= get_ligature( src
[i
] )))
1818 else len
= lstrlenW( expand
);
1820 if (*dstlen
&& ret
== STATUS_SUCCESS
)
1822 if (pos
+ len
<= *dstlen
) memcpy( dst
+ pos
, expand
, len
* sizeof(WCHAR
) );
1823 else ret
= STATUS_BUFFER_TOO_SMALL
;
1832 static NTSTATUS
fold_digits( const WCHAR
*src
, int srclen
, WCHAR
*dst
, int *dstlen
)
1834 extern const WCHAR wine_digitmap
[] DECLSPEC_HIDDEN
;
1835 int i
, len
= *dstlen
;
1838 if (!len
) return STATUS_SUCCESS
;
1839 if (srclen
> len
) return STATUS_BUFFER_TOO_SMALL
;
1840 for (i
= 0; i
< srclen
; i
++)
1842 WCHAR digit
= get_table_entry( wine_digitmap
, src
[i
] );
1843 dst
[i
] = digit
? digit
: src
[i
];
1845 return STATUS_SUCCESS
;
1849 static NTSTATUS
fold_string( DWORD flags
, const WCHAR
*src
, int srclen
, WCHAR
*dst
, int *dstlen
)
1856 case MAP_PRECOMPOSED
:
1857 return RtlNormalizeString( NormalizationC
, src
, srclen
, dst
, dstlen
);
1859 case MAP_PRECOMPOSED
| MAP_FOLDCZONE
:
1860 return RtlNormalizeString( NormalizationKC
, src
, srclen
, dst
, dstlen
);
1862 return RtlNormalizeString( NormalizationD
, src
, srclen
, dst
, dstlen
);
1863 case MAP_COMPOSITE
| MAP_FOLDCZONE
:
1864 return RtlNormalizeString( NormalizationKD
, src
, srclen
, dst
, dstlen
);
1865 case MAP_FOLDDIGITS
:
1866 return fold_digits( src
, srclen
, dst
, dstlen
);
1867 case MAP_EXPAND_LIGATURES
:
1868 case MAP_EXPAND_LIGATURES
| MAP_FOLDCZONE
:
1869 return expand_ligatures( src
, srclen
, dst
, dstlen
);
1870 case MAP_FOLDDIGITS
| MAP_PRECOMPOSED
:
1871 if (!(tmp
= RtlAllocateHeap( GetProcessHeap(), 0, srclen
* sizeof(WCHAR
) )))
1872 return STATUS_NO_MEMORY
;
1873 fold_digits( src
, srclen
, tmp
, &srclen
);
1874 ret
= RtlNormalizeString( NormalizationC
, tmp
, srclen
, dst
, dstlen
);
1876 case MAP_FOLDDIGITS
| MAP_FOLDCZONE
:
1877 case MAP_FOLDDIGITS
| MAP_PRECOMPOSED
| MAP_FOLDCZONE
:
1878 if (!(tmp
= RtlAllocateHeap( GetProcessHeap(), 0, srclen
* sizeof(WCHAR
) )))
1879 return STATUS_NO_MEMORY
;
1880 fold_digits( src
, srclen
, tmp
, &srclen
);
1881 ret
= RtlNormalizeString( NormalizationKC
, tmp
, srclen
, dst
, dstlen
);
1883 case MAP_FOLDDIGITS
| MAP_COMPOSITE
:
1884 if (!(tmp
= RtlAllocateHeap( GetProcessHeap(), 0, srclen
* sizeof(WCHAR
) )))
1885 return STATUS_NO_MEMORY
;
1886 fold_digits( src
, srclen
, tmp
, &srclen
);
1887 ret
= RtlNormalizeString( NormalizationD
, tmp
, srclen
, dst
, dstlen
);
1889 case MAP_FOLDDIGITS
| MAP_COMPOSITE
| MAP_FOLDCZONE
:
1890 if (!(tmp
= RtlAllocateHeap( GetProcessHeap(), 0, srclen
* sizeof(WCHAR
) )))
1891 return STATUS_NO_MEMORY
;
1892 fold_digits( src
, srclen
, tmp
, &srclen
);
1893 ret
= RtlNormalizeString( NormalizationKD
, tmp
, srclen
, dst
, dstlen
);
1895 case MAP_EXPAND_LIGATURES
| MAP_FOLDDIGITS
:
1896 case MAP_EXPAND_LIGATURES
| MAP_FOLDDIGITS
| MAP_FOLDCZONE
:
1897 if (!(tmp
= RtlAllocateHeap( GetProcessHeap(), 0, srclen
* sizeof(WCHAR
) )))
1898 return STATUS_NO_MEMORY
;
1899 fold_digits( src
, srclen
, tmp
, &srclen
);
1900 ret
= expand_ligatures( tmp
, srclen
, dst
, dstlen
);
1903 return STATUS_INVALID_PARAMETER_1
;
1905 RtlFreeHeap( GetProcessHeap(), 0, tmp
);
1910 static int mbstowcs_cpsymbol( DWORD flags
, const char *src
, int srclen
, WCHAR
*dst
, int dstlen
)
1916 SetLastError( ERROR_INVALID_FLAGS
);
1919 if (!dstlen
) return srclen
;
1920 len
= min( srclen
, dstlen
);
1921 for (i
= 0; i
< len
; i
++)
1923 unsigned char c
= src
[i
];
1924 dst
[i
] = (c
< 0x20) ? c
: c
+ 0xf000;
1928 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1935 static int mbstowcs_utf7( DWORD flags
, const char *src
, int srclen
, WCHAR
*dst
, int dstlen
)
1937 static const signed char base64_decoding_table
[] =
1939 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1940 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1941 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1942 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1943 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1944 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1945 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1946 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1949 const char *source_end
= src
+ srclen
;
1950 int offset
= 0, pos
= 0;
1951 DWORD byte_pair
= 0;
1955 SetLastError( ERROR_INVALID_FLAGS
);
1958 #define OUTPUT(ch) \
1962 if (pos >= dstlen) goto overflow; \
1968 while (src
< source_end
)
1973 if (src
>= source_end
) break;
1976 /* just a plus sign escaped as +- */
1984 signed char sextet
= *src
;
1987 /* skip over the dash and end base64 decoding
1988 * the current, unfinished byte pair is discarded */
1995 /* the next character of src is < 0 and therefore not part of a base64 sequence
1996 * the current, unfinished byte pair is NOT discarded in this case
1997 * this is probably a bug in Windows */
2000 sextet
= base64_decoding_table
[sextet
];
2003 /* -1 means that the next character of src is not part of a base64 sequence
2004 * in other words, all sextets in this base64 sequence have been processed
2005 * the current, unfinished byte pair is discarded */
2010 byte_pair
= (byte_pair
<< 6) | sextet
;
2014 /* this byte pair is done */
2015 OUTPUT( byte_pair
>> (offset
- 16) );
2020 while (src
< source_end
);
2024 OUTPUT( (unsigned char)*src
);
2031 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2037 static int mbstowcs_utf8( DWORD flags
, const char *src
, int srclen
, WCHAR
*dst
, int dstlen
)
2042 if (flags
& ~(MB_PRECOMPOSED
| MB_COMPOSITE
| MB_USEGLYPHCHARS
| MB_ERR_INVALID_CHARS
))
2044 SetLastError( ERROR_INVALID_FLAGS
);
2047 if (!dstlen
) dst
= NULL
;
2048 status
= RtlUTF8ToUnicodeN( dst
, dstlen
* sizeof(WCHAR
), &reslen
, src
, srclen
);
2049 if (status
== STATUS_SOME_NOT_MAPPED
)
2051 if (flags
& MB_ERR_INVALID_CHARS
)
2053 SetLastError( ERROR_NO_UNICODE_TRANSLATION
);
2057 else if (!set_ntstatus( status
)) reslen
= 0;
2059 return reslen
/ sizeof(WCHAR
);
2063 static inline int is_private_use_area_char( WCHAR code
)
2065 return (code
>= 0xe000 && code
<= 0xf8ff);
2069 static int check_invalid_chars( const CPTABLEINFO
*info
, const unsigned char *src
, int srclen
)
2071 if (info
->DBCSOffsets
)
2073 for ( ; srclen
; src
++, srclen
-- )
2075 USHORT off
= info
->DBCSOffsets
[*src
];
2078 if (srclen
== 1) break; /* partial char, error */
2079 if (info
->DBCSOffsets
[off
+ src
[1]] == info
->UniDefaultChar
&&
2080 ((src
[0] << 8) | src
[1]) != info
->TransUniDefaultChar
) break;
2085 if (info
->MultiByteTable
[*src
] == info
->UniDefaultChar
&& *src
!= info
->TransUniDefaultChar
)
2087 if (is_private_use_area_char( info
->MultiByteTable
[*src
] )) break;
2092 for ( ; srclen
; src
++, srclen
-- )
2094 if (info
->MultiByteTable
[*src
] == info
->UniDefaultChar
&& *src
!= info
->TransUniDefaultChar
)
2096 if (is_private_use_area_char( info
->MultiByteTable
[*src
] )) break;
2104 static int mbstowcs_decompose( const CPTABLEINFO
*info
, const unsigned char *src
, int srclen
,
2105 WCHAR
*dst
, int dstlen
)
2110 const WCHAR
*decomp
;
2111 unsigned int decomp_len
;
2113 if (info
->DBCSOffsets
)
2115 if (!dstlen
) /* compute length */
2117 for (len
= 0; srclen
; srclen
--, src
++, len
+= decomp_len
)
2119 if ((off
= info
->DBCSOffsets
[*src
]))
2121 if (srclen
> 1 && src
[1])
2125 ch
= info
->DBCSOffsets
[off
+ *src
];
2127 else ch
= info
->UniDefaultChar
;
2129 else ch
= info
->MultiByteTable
[*src
];
2130 get_decomposition( ch
, &decomp_len
);
2135 for (len
= dstlen
; srclen
&& len
; srclen
--, src
++, dst
+= decomp_len
, len
-= decomp_len
)
2137 if ((off
= info
->DBCSOffsets
[*src
]))
2139 if (srclen
> 1 && src
[1])
2143 ch
= info
->DBCSOffsets
[off
+ *src
];
2145 else ch
= info
->UniDefaultChar
;
2147 else ch
= info
->MultiByteTable
[*src
];
2149 if ((decomp
= get_decomposition( ch
, &decomp_len
)))
2151 if (len
< decomp_len
) break;
2152 memcpy( dst
, decomp
, decomp_len
* sizeof(WCHAR
) );
2159 if (!dstlen
) /* compute length */
2161 for (len
= 0; srclen
; srclen
--, src
++, len
+= decomp_len
)
2162 get_decomposition( info
->MultiByteTable
[*src
], &decomp_len
);
2166 for (len
= dstlen
; srclen
&& len
; srclen
--, src
++, dst
+= decomp_len
, len
-= decomp_len
)
2168 ch
= info
->MultiByteTable
[*src
];
2169 if ((decomp
= get_decomposition( ch
, &decomp_len
)))
2171 if (len
< decomp_len
) break;
2172 memcpy( dst
, decomp
, decomp_len
* sizeof(WCHAR
) );
2180 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2183 return dstlen
- len
;
2187 static int mbstowcs_sbcs( const CPTABLEINFO
*info
, const unsigned char *src
, int srclen
,
2188 WCHAR
*dst
, int dstlen
)
2190 const USHORT
*table
= info
->MultiByteTable
;
2193 if (!dstlen
) return srclen
;
2195 if (dstlen
< srclen
) /* buffer too small: fill it up to dstlen and return error */
2198 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2202 while (srclen
>= 16)
2204 dst
[0] = table
[src
[0]];
2205 dst
[1] = table
[src
[1]];
2206 dst
[2] = table
[src
[2]];
2207 dst
[3] = table
[src
[3]];
2208 dst
[4] = table
[src
[4]];
2209 dst
[5] = table
[src
[5]];
2210 dst
[6] = table
[src
[6]];
2211 dst
[7] = table
[src
[7]];
2212 dst
[8] = table
[src
[8]];
2213 dst
[9] = table
[src
[9]];
2214 dst
[10] = table
[src
[10]];
2215 dst
[11] = table
[src
[11]];
2216 dst
[12] = table
[src
[12]];
2217 dst
[13] = table
[src
[13]];
2218 dst
[14] = table
[src
[14]];
2219 dst
[15] = table
[src
[15]];
2225 /* now handle the remaining characters */
2230 case 15: dst
[-15] = table
[src
[-15]];
2231 case 14: dst
[-14] = table
[src
[-14]];
2232 case 13: dst
[-13] = table
[src
[-13]];
2233 case 12: dst
[-12] = table
[src
[-12]];
2234 case 11: dst
[-11] = table
[src
[-11]];
2235 case 10: dst
[-10] = table
[src
[-10]];
2236 case 9: dst
[-9] = table
[src
[-9]];
2237 case 8: dst
[-8] = table
[src
[-8]];
2238 case 7: dst
[-7] = table
[src
[-7]];
2239 case 6: dst
[-6] = table
[src
[-6]];
2240 case 5: dst
[-5] = table
[src
[-5]];
2241 case 4: dst
[-4] = table
[src
[-4]];
2242 case 3: dst
[-3] = table
[src
[-3]];
2243 case 2: dst
[-2] = table
[src
[-2]];
2244 case 1: dst
[-1] = table
[src
[-1]];
2251 static int mbstowcs_dbcs( const CPTABLEINFO
*info
, const unsigned char *src
, int srclen
,
2252 WCHAR
*dst
, int dstlen
)
2259 for (i
= 0; srclen
; i
++, src
++, srclen
--)
2260 if (info
->DBCSOffsets
[*src
] && srclen
> 1 && src
[1]) { src
++; srclen
--; }
2264 for (i
= dstlen
; srclen
&& i
; i
--, srclen
--, src
++, dst
++)
2266 if ((off
= info
->DBCSOffsets
[*src
]))
2268 if (srclen
> 1 && src
[1])
2272 *dst
= info
->DBCSOffsets
[off
+ *src
];
2274 else *dst
= info
->UniDefaultChar
;
2276 else *dst
= info
->MultiByteTable
[*src
];
2280 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2287 static int mbstowcs_codepage( UINT codepage
, DWORD flags
, const char *src
, int srclen
,
2288 WCHAR
*dst
, int dstlen
)
2290 CPTABLEINFO local_info
;
2291 const CPTABLEINFO
*info
= get_codepage_table( codepage
);
2292 const unsigned char *str
= (const unsigned char *)src
;
2296 SetLastError( ERROR_INVALID_PARAMETER
);
2299 if (flags
& ~(MB_PRECOMPOSED
| MB_COMPOSITE
| MB_USEGLYPHCHARS
| MB_ERR_INVALID_CHARS
))
2301 SetLastError( ERROR_INVALID_FLAGS
);
2305 if ((flags
& MB_USEGLYPHCHARS
) && info
->MultiByteTable
[256] == 256)
2308 local_info
.MultiByteTable
+= 257;
2311 if ((flags
& MB_ERR_INVALID_CHARS
) && check_invalid_chars( info
, str
, srclen
))
2313 SetLastError( ERROR_NO_UNICODE_TRANSLATION
);
2317 if (flags
& MB_COMPOSITE
) return mbstowcs_decompose( info
, str
, srclen
, dst
, dstlen
);
2319 if (info
->DBCSOffsets
)
2320 return mbstowcs_dbcs( info
, str
, srclen
, dst
, dstlen
);
2322 return mbstowcs_sbcs( info
, str
, srclen
, dst
, dstlen
);
2326 static int wcstombs_cpsymbol( DWORD flags
, const WCHAR
*src
, int srclen
, char *dst
, int dstlen
,
2327 const char *defchar
, BOOL
*used
)
2333 SetLastError( ERROR_INVALID_FLAGS
);
2336 if (defchar
|| used
)
2338 SetLastError( ERROR_INVALID_PARAMETER
);
2341 if (!dstlen
) return srclen
;
2342 len
= min( srclen
, dstlen
);
2343 for (i
= 0; i
< len
; i
++)
2345 if (src
[i
] < 0x20) dst
[i
] = src
[i
];
2346 else if (src
[i
] >= 0xf020 && src
[i
] < 0xf100) dst
[i
] = src
[i
] - 0xf000;
2349 SetLastError( ERROR_NO_UNICODE_TRANSLATION
);
2355 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2362 static int wcstombs_utf7( DWORD flags
, const WCHAR
*src
, int srclen
, char *dst
, int dstlen
,
2363 const char *defchar
, BOOL
*used
)
2365 static const char directly_encodable
[] =
2367 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
2368 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
2369 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
2370 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
2371 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
2372 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
2373 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
2374 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
2376 #define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
2378 static const char base64_encoding_table
[] =
2379 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2381 const WCHAR
*source_end
= src
+ srclen
;
2384 if (defchar
|| used
)
2386 SetLastError( ERROR_INVALID_PARAMETER
);
2391 SetLastError( ERROR_INVALID_FLAGS
);
2395 #define OUTPUT(ch) \
2399 if (pos >= dstlen) goto overflow; \
2405 while (src
< source_end
)
2413 else if (ENCODABLE(*src
))
2420 unsigned int offset
= 0, byte_pair
= 0;
2423 while (src
< source_end
&& !ENCODABLE(*src
))
2425 byte_pair
= (byte_pair
<< 16) | *src
;
2430 OUTPUT( base64_encoding_table
[(byte_pair
>> offset
) & 0x3f] );
2436 /* Windows won't create a padded base64 character if there's no room for the - sign
2437 * as well ; this is probably a bug in Windows */
2438 if (dstlen
> 0 && pos
+ 1 >= dstlen
) goto overflow
;
2439 byte_pair
<<= (6 - offset
);
2440 OUTPUT( base64_encoding_table
[byte_pair
& 0x3f] );
2442 /* Windows always explicitly terminates the base64 sequence
2443 even though RFC 2152 (page 3, rule 2) does not require this */
2450 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2457 static int wcstombs_utf8( DWORD flags
, const WCHAR
*src
, int srclen
, char *dst
, int dstlen
,
2458 const char *defchar
, BOOL
*used
)
2463 if (defchar
|| used
)
2465 SetLastError( ERROR_INVALID_PARAMETER
);
2468 if (flags
& ~(WC_DISCARDNS
| WC_SEPCHARS
| WC_DEFAULTCHAR
| WC_ERR_INVALID_CHARS
|
2469 WC_COMPOSITECHECK
| WC_NO_BEST_FIT_CHARS
))
2471 SetLastError( ERROR_INVALID_FLAGS
);
2474 if (!dstlen
) dst
= NULL
;
2475 status
= RtlUnicodeToUTF8N( dst
, dstlen
, &reslen
, src
, srclen
* sizeof(WCHAR
) );
2476 if (status
== STATUS_SOME_NOT_MAPPED
)
2478 if (flags
& WC_ERR_INVALID_CHARS
)
2480 SetLastError( ERROR_NO_UNICODE_TRANSLATION
);
2484 else if (!set_ntstatus( status
)) reslen
= 0;
2489 static int wcstombs_sbcs( const CPTABLEINFO
*info
, const WCHAR
*src
, unsigned int srclen
,
2490 char *dst
, unsigned int dstlen
)
2492 const char *table
= info
->WideCharTable
;
2495 if (!dstlen
) return srclen
;
2497 if (dstlen
< srclen
)
2499 /* buffer too small: fill it up to dstlen and return error */
2501 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2505 while (srclen
>= 16)
2507 dst
[0] = table
[src
[0]];
2508 dst
[1] = table
[src
[1]];
2509 dst
[2] = table
[src
[2]];
2510 dst
[3] = table
[src
[3]];
2511 dst
[4] = table
[src
[4]];
2512 dst
[5] = table
[src
[5]];
2513 dst
[6] = table
[src
[6]];
2514 dst
[7] = table
[src
[7]];
2515 dst
[8] = table
[src
[8]];
2516 dst
[9] = table
[src
[9]];
2517 dst
[10] = table
[src
[10]];
2518 dst
[11] = table
[src
[11]];
2519 dst
[12] = table
[src
[12]];
2520 dst
[13] = table
[src
[13]];
2521 dst
[14] = table
[src
[14]];
2522 dst
[15] = table
[src
[15]];
2528 /* now handle remaining characters */
2533 case 15: dst
[-15] = table
[src
[-15]];
2534 case 14: dst
[-14] = table
[src
[-14]];
2535 case 13: dst
[-13] = table
[src
[-13]];
2536 case 12: dst
[-12] = table
[src
[-12]];
2537 case 11: dst
[-11] = table
[src
[-11]];
2538 case 10: dst
[-10] = table
[src
[-10]];
2539 case 9: dst
[-9] = table
[src
[-9]];
2540 case 8: dst
[-8] = table
[src
[-8]];
2541 case 7: dst
[-7] = table
[src
[-7]];
2542 case 6: dst
[-6] = table
[src
[-6]];
2543 case 5: dst
[-5] = table
[src
[-5]];
2544 case 4: dst
[-4] = table
[src
[-4]];
2545 case 3: dst
[-3] = table
[src
[-3]];
2546 case 2: dst
[-2] = table
[src
[-2]];
2547 case 1: dst
[-1] = table
[src
[-1]];
2554 static int wcstombs_dbcs( const CPTABLEINFO
*info
, const WCHAR
*src
, unsigned int srclen
,
2555 char *dst
, unsigned int dstlen
)
2557 const USHORT
*table
= info
->WideCharTable
;
2562 for (i
= 0; srclen
; src
++, srclen
--, i
++) if (table
[*src
] & 0xff00) i
++;
2566 for (i
= dstlen
; srclen
&& i
; i
--, srclen
--, src
++)
2568 if (table
[*src
] & 0xff00)
2570 if (i
== 1) break; /* do not output a partial char */
2572 *dst
++ = table
[*src
] >> 8;
2574 *dst
++ = (char)table
[*src
];
2578 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2585 static inline int is_valid_sbcs_mapping( const CPTABLEINFO
*info
, DWORD flags
, unsigned int wch
)
2587 const unsigned char *table
= info
->WideCharTable
;
2589 if (wch
>= 0x10000) return 0;
2590 if ((flags
& WC_NO_BEST_FIT_CHARS
) || table
[wch
] == info
->DefaultChar
)
2591 return (info
->MultiByteTable
[table
[wch
]] == wch
);
2596 static inline int is_valid_dbcs_mapping( const CPTABLEINFO
*info
, DWORD flags
, unsigned int wch
)
2598 const unsigned short *table
= info
->WideCharTable
;
2601 if (wch
>= 0x10000) return 0;
2603 if ((flags
& WC_NO_BEST_FIT_CHARS
) || ch
== info
->DefaultChar
)
2605 if (ch
>> 8) return info
->DBCSOffsets
[info
->DBCSOffsets
[ch
>> 8] + (ch
& 0xff)] == wch
;
2606 return info
->MultiByteTable
[ch
] == wch
;
2612 static int wcstombs_sbcs_slow( const CPTABLEINFO
*info
, DWORD flags
, const WCHAR
*src
, unsigned int srclen
,
2613 char *dst
, unsigned int dstlen
, const char *defchar
, BOOL
*used
)
2615 const char *table
= info
->WideCharTable
;
2616 const char def
= defchar
? *defchar
: (char)info
->DefaultChar
;
2620 unsigned int composed
;
2622 if (!used
) used
= &tmp
; /* avoid checking on every char */
2627 for (i
= 0; srclen
; i
++, src
++, srclen
--)
2630 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= compose_chars( src
[0], src
[1] )))
2632 /* now check if we can use the composed char */
2633 if (is_valid_sbcs_mapping( info
, flags
, composed
))
2635 /* we have a good mapping, use it */
2640 /* no mapping for the composed char, check the other flags */
2641 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
2644 src
++; /* skip the non-spacing char */
2648 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
2653 /* WC_SEPCHARS is the default */
2655 if (!*used
) *used
= !is_valid_sbcs_mapping( info
, flags
, wch
);
2660 for (i
= dstlen
; srclen
&& i
; dst
++, i
--, src
++, srclen
--)
2663 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= compose_chars( src
[0], src
[1] )))
2665 /* now check if we can use the composed char */
2666 if (is_valid_sbcs_mapping( info
, flags
, composed
))
2668 /* we have a good mapping, use it */
2669 *dst
= table
[composed
];
2674 /* no mapping for the composed char, check the other flags */
2675 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
2679 src
++; /* skip the non-spacing char */
2683 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
2688 /* WC_SEPCHARS is the default */
2692 if (!is_valid_sbcs_mapping( info
, flags
, wch
))
2700 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2707 static int wcstombs_dbcs_slow( const CPTABLEINFO
*info
, DWORD flags
, const WCHAR
*src
, unsigned int srclen
,
2708 char *dst
, unsigned int dstlen
, const char *defchar
, BOOL
*used
)
2710 const USHORT
*table
= info
->WideCharTable
;
2711 WCHAR wch
, defchar_value
;
2712 unsigned int composed
;
2717 if (!defchar
[1]) defchar_value
= (unsigned char)defchar
[0];
2718 else defchar_value
= ((unsigned char)defchar
[0] << 8) | (unsigned char)defchar
[1];
2720 if (!used
) used
= &tmp
; /* avoid checking on every char */
2725 if (!defchar
&& !used
&& !(flags
& WC_COMPOSITECHECK
))
2727 for (i
= 0; srclen
; srclen
--, src
++, i
++) if (table
[*src
] & 0xff00) i
++;
2730 for (i
= 0; srclen
; srclen
--, src
++, i
++)
2733 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= compose_chars( src
[0], src
[1] )))
2735 /* now check if we can use the composed char */
2736 if (is_valid_dbcs_mapping( info
, flags
, composed
))
2738 /* we have a good mapping for the composed char, use it */
2739 res
= table
[composed
];
2740 if (res
& 0xff00) i
++;
2745 /* no mapping for the composed char, check the other flags */
2746 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
2748 if (defchar_value
& 0xff00) i
++;
2750 src
++; /* skip the non-spacing char */
2754 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
2759 /* WC_SEPCHARS is the default */
2763 if (!is_valid_dbcs_mapping( info
, flags
, wch
))
2765 res
= defchar_value
;
2768 if (res
& 0xff00) i
++;
2774 for (i
= dstlen
; srclen
&& i
; i
--, srclen
--, src
++)
2777 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= compose_chars( src
[0], src
[1] )))
2779 /* now check if we can use the composed char */
2780 if (is_valid_dbcs_mapping( info
, flags
, composed
))
2782 /* we have a good mapping for the composed char, use it */
2783 res
= table
[composed
];
2788 /* no mapping for the composed char, check the other flags */
2789 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
2791 res
= defchar_value
;
2793 src
++; /* skip the non-spacing char */
2797 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
2802 /* WC_SEPCHARS is the default */
2806 if (!is_valid_dbcs_mapping( info
, flags
, wch
))
2808 res
= defchar_value
;
2815 if (i
== 1) break; /* do not output a partial char */
2823 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2830 static int wcstombs_codepage( UINT codepage
, DWORD flags
, const WCHAR
*src
, int srclen
,
2831 char *dst
, int dstlen
, const char *defchar
, BOOL
*used
)
2833 const CPTABLEINFO
*info
= get_codepage_table( codepage
);
2837 SetLastError( ERROR_INVALID_PARAMETER
);
2840 if (flags
& ~(WC_DISCARDNS
| WC_SEPCHARS
| WC_DEFAULTCHAR
| WC_ERR_INVALID_CHARS
|
2841 WC_COMPOSITECHECK
| WC_NO_BEST_FIT_CHARS
))
2843 SetLastError( ERROR_INVALID_FLAGS
);
2846 if (flags
|| defchar
|| used
)
2848 if (!defchar
) defchar
= (const char *)&info
->DefaultChar
;
2849 if (info
->DBCSOffsets
)
2850 return wcstombs_dbcs_slow( info
, flags
, src
, srclen
, dst
, dstlen
, defchar
, used
);
2852 return wcstombs_sbcs_slow( info
, flags
, src
, srclen
, dst
, dstlen
, defchar
, used
);
2854 if (info
->DBCSOffsets
)
2855 return wcstombs_dbcs( info
, src
, srclen
, dst
, dstlen
);
2857 return wcstombs_sbcs( info
, src
, srclen
, dst
, dstlen
);
2861 static int get_sortkey( DWORD flags
, const WCHAR
*src
, int srclen
, char *dst
, int dstlen
)
2863 WCHAR dummy
[4]; /* no decomposition is larger than 4 chars */
2866 const WCHAR
*src_save
= src
;
2867 int srclen_save
= srclen
;
2869 key_len
[0] = key_len
[1] = key_len
[2] = key_len
[3] = 0;
2870 for (; srclen
; srclen
--, src
++)
2872 unsigned int i
, decomposed_len
= 1;/*wine_decompose(*src, dummy, 4);*/
2876 for (i
= 0; i
< decomposed_len
; i
++)
2878 WCHAR wch
= dummy
[i
];
2881 if ((flags
& NORM_IGNORESYMBOLS
) &&
2882 (get_char_type( CT_CTYPE1
, wch
) & (C1_PUNCT
| C1_SPACE
)))
2885 if (flags
& NORM_IGNORECASE
) wch
= casemap( nls_info
.LowerCaseTable
, wch
);
2887 ce
= collation_table
[collation_table
[collation_table
[wch
>> 8] + ((wch
>> 4) & 0x0f)] + (wch
& 0xf)];
2888 if (ce
!= (unsigned int)-1)
2890 if (ce
>> 16) key_len
[0] += 2;
2891 if ((ce
>> 8) & 0xff) key_len
[1]++;
2892 if ((ce
>> 4) & 0x0f) key_len
[2]++;
2895 if (wch
>> 8) key_len
[3]++;
2902 if (wch
>> 8) key_len
[0]++;
2903 if (wch
& 0xff) key_len
[0]++;
2909 if (!dstlen
) /* compute length */
2910 /* 4 * '\1' + key length */
2911 return key_len
[0] + key_len
[1] + key_len
[2] + key_len
[3] + 4;
2913 if (dstlen
< key_len
[0] + key_len
[1] + key_len
[2] + key_len
[3] + 4 + 1)
2914 return 0; /* overflow */
2917 srclen
= srclen_save
;
2920 key_ptr
[1] = key_ptr
[0] + key_len
[0] + 1;
2921 key_ptr
[2] = key_ptr
[1] + key_len
[1] + 1;
2922 key_ptr
[3] = key_ptr
[2] + key_len
[2] + 1;
2924 for (; srclen
; srclen
--, src
++)
2926 unsigned int i
, decomposed_len
= 1;/*wine_decompose(*src, dummy, 4);*/
2930 for (i
= 0; i
< decomposed_len
; i
++)
2932 WCHAR wch
= dummy
[i
];
2935 if ((flags
& NORM_IGNORESYMBOLS
) &&
2936 (get_char_type( CT_CTYPE1
, wch
) & (C1_PUNCT
| C1_SPACE
)))
2939 if (flags
& NORM_IGNORECASE
) wch
= casemap( nls_info
.LowerCaseTable
, wch
);
2941 ce
= collation_table
[collation_table
[collation_table
[wch
>> 8] + ((wch
>> 4) & 0x0f)] + (wch
& 0xf)];
2942 if (ce
!= (unsigned int)-1)
2945 if ((key
= ce
>> 16))
2947 *key_ptr
[0]++ = key
>> 8;
2948 *key_ptr
[0]++ = key
& 0xff;
2950 /* make key 1 start from 2 */
2951 if ((key
= (ce
>> 8) & 0xff)) *key_ptr
[1]++ = key
+ 1;
2952 /* make key 2 start from 2 */
2953 if ((key
= (ce
>> 4) & 0x0f)) *key_ptr
[2]++ = key
+ 1;
2954 /* key 3 is always a character code */
2957 if (wch
>> 8) *key_ptr
[3]++ = wch
>> 8;
2958 if (wch
& 0xff) *key_ptr
[3]++ = wch
& 0xff;
2963 *key_ptr
[0]++ = 0xff;
2964 *key_ptr
[0]++ = 0xfe;
2965 if (wch
>> 8) *key_ptr
[0]++ = wch
>> 8;
2966 if (wch
& 0xff) *key_ptr
[0]++ = wch
& 0xff;
2977 return key_ptr
[3] - dst
;
2981 /* compose a full-width katakana. return consumed source characters. */
2982 static int compose_katakana( const WCHAR
*src
, int srclen
, WCHAR
*dst
)
2984 static const BYTE katakana_map
[] =
2986 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
2987 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
2988 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
2989 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
2990 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
2991 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
2992 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
2993 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
2998 if (!dst
) dst
= &dummy
;
3012 shift
= *src
- 0xff61;
3013 if (shift
< 0 || shift
>= ARRAY_SIZE( katakana_map
)) return 0;
3014 *dst
= katakana_map
[shift
] | 0x3000;
3018 if (srclen
<= 1) return 1;
3022 case 0xff9e: /* datakuten (voiced sound) */
3023 if ((*src
>= 0xff76 && *src
<= 0xff84) || (*src
>= 0xff8a && *src
<= 0xff8e) || *src
== 0x30fd)
3025 else if (*src
== 0xff73)
3026 *dst
= 0x30f4; /* KATAKANA LETTER VU */
3027 else if (*src
== 0xff9c)
3028 *dst
= 0x30f7; /* KATAKANA LETTER VA */
3029 else if (*src
== 0x30f0)
3030 *dst
= 0x30f8; /* KATAKANA LETTER VI */
3031 else if (*src
== 0x30f1)
3032 *dst
= 0x30f9; /* KATAKANA LETTER VE */
3033 else if (*src
== 0xff66)
3034 *dst
= 0x30fa; /* KATAKANA LETTER VO */
3038 case 0xff9f: /* handakuten (semi-voiced sound) */
3039 if (*src
>= 0xff8a && *src
<= 0xff8e)
3050 /* map one or two half-width characters to one full-width character */
3051 static int map_to_fullwidth( const WCHAR
*src
, int srclen
, WCHAR
*dst
)
3055 if (*src
<= '~' && *src
> ' ' && *src
!= '\\')
3056 *dst
= *src
- 0x20 + 0xff00;
3057 else if (*src
== ' ')
3059 else if (*src
<= 0x00af && *src
>= 0x00a2)
3061 static const BYTE misc_symbols_table
[] =
3063 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3064 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3066 if (misc_symbols_table
[*src
- 0x00a2])
3067 *dst
= misc_symbols_table
[*src
- 0x00a2] | 0xff00;
3071 else if (*src
== 0x20a9) /* WON SIGN */
3073 else if ((n
= compose_katakana(src
, srclen
, dst
)) > 0)
3075 else if (*src
>= 0xffa0 && *src
<= 0xffdc)
3077 static const BYTE hangul_mapping_table
[] =
3079 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3080 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3081 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3082 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3083 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3084 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3085 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3086 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3089 if (hangul_mapping_table
[*src
- 0xffa0])
3090 *dst
= hangul_mapping_table
[*src
- 0xffa0] | 0x3100;
3100 /* decompose a full-width katakana character into one or two half-width characters. */
3101 static int decompose_katakana( WCHAR c
, WCHAR
*dst
, int dstlen
)
3103 static const BYTE katakana_map
[] =
3105 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3106 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3107 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3108 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3109 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3110 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3111 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3112 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3113 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3114 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3115 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3116 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3117 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3119 int len
= 0, shift
= c
- 0x3099;
3122 if (shift
< 0 || shift
>= ARRAY_SIZE( katakana_map
)) return 0;
3124 k
= katakana_map
[shift
];
3127 if (dstlen
> 0) *dst
= c
;
3132 if (dstlen
> 0) *dst
= k
| 0xff00;
3139 dst
[0] = (k
> 0x50) ? (c
- (k
& 0xf)) : (katakana_map
[shift
- k
] | 0xff00);
3140 dst
[1] = (k
== 2) ? 0xff9f : 0xff9e;
3147 /* map single full-width character to single or double half-width characters. */
3148 static int map_to_halfwidth( WCHAR c
, WCHAR
*dst
, int dstlen
)
3150 int n
= decompose_katakana( c
, dst
, dstlen
);
3151 if (n
> 0) return n
;
3155 else if (c
== 0x3001)
3157 else if (c
== 0x3002)
3159 else if (c
== 0x300c || c
== 0x300d)
3160 *dst
= (c
- 0x300c) + 0xff62;
3161 else if (c
>= 0x3131 && c
<= 0x3163)
3163 *dst
= c
- 0x3131 + 0xffa1;
3164 if (*dst
>= 0xffbf) *dst
+= 3;
3165 if (*dst
>= 0xffc8) *dst
+= 2;
3166 if (*dst
>= 0xffd0) *dst
+= 2;
3167 if (*dst
>= 0xffd8) *dst
+= 2;
3169 else if (c
== 0x3164)
3171 else if (c
== 0x2019)
3173 else if (c
== 0x201d)
3175 else if (c
> 0xff00 && c
< 0xff5f && c
!= 0xff3c)
3176 *dst
= c
- 0xff00 + 0x20;
3177 else if (c
>= 0xffe0 && c
<= 0xffe6)
3179 static const WCHAR misc_symbol_map
[] = { 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9 };
3180 *dst
= misc_symbol_map
[c
- 0xffe0];
3189 /* 32-bit collation element table format:
3190 * unicode weight - high 16 bit, diacritic weight - high 8 bit of low 16 bit,
3191 * case weight - high 4 bit of low 8 bit.
3194 enum weight
{ UNICODE_WEIGHT
, DIACRITIC_WEIGHT
, CASE_WEIGHT
};
3196 static unsigned int get_weight( WCHAR ch
, enum weight type
)
3200 ret
= collation_table
[collation_table
[collation_table
[ch
>> 8] + ((ch
>> 4) & 0x0f)] + (ch
& 0xf)];
3201 if (ret
== ~0u) return ch
;
3205 case UNICODE_WEIGHT
: return ret
>> 16;
3206 case DIACRITIC_WEIGHT
: return (ret
>> 8) & 0xff;
3207 case CASE_WEIGHT
: return (ret
>> 4) & 0x0f;
3213 static void inc_str_pos( const WCHAR
**str
, int *len
, unsigned int *dpos
, unsigned int *dlen
)
3225 static int compare_weights(int flags
, const WCHAR
*str1
, int len1
,
3226 const WCHAR
*str2
, int len2
, enum weight type
)
3228 unsigned int ce1
, ce2
, dpos1
= 0, dpos2
= 0, dlen1
= 0, dlen2
= 0;
3229 const WCHAR
*dstr1
= NULL
, *dstr2
= NULL
;
3231 while (len1
> 0 && len2
> 0)
3233 if (!dlen1
&& !(dstr1
= get_decomposition( *str1
, &dlen1
))) dstr1
= str1
;
3234 if (!dlen2
&& !(dstr2
= get_decomposition( *str2
, &dlen2
))) dstr2
= str2
;
3236 if (flags
& NORM_IGNORESYMBOLS
)
3239 /* FIXME: not tested */
3240 if (get_char_type( CT_CTYPE1
, dstr1
[dpos1
] ) & (C1_PUNCT
| C1_SPACE
))
3242 inc_str_pos( &str1
, &len1
, &dpos1
, &dlen1
);
3245 if (get_char_type( CT_CTYPE1
, dstr2
[dpos2
] ) & (C1_PUNCT
| C1_SPACE
))
3247 inc_str_pos( &str2
, &len2
, &dpos2
, &dlen2
);
3253 /* hyphen and apostrophe are treated differently depending on
3254 * whether SORT_STRINGSORT specified or not
3256 if (type
== UNICODE_WEIGHT
&& !(flags
& SORT_STRINGSORT
))
3258 if (dstr1
[dpos1
] == '-' || dstr1
[dpos1
] == '\'')
3260 if (dstr2
[dpos2
] != '-' && dstr2
[dpos2
] != '\'')
3262 inc_str_pos( &str1
, &len1
, &dpos1
, &dlen1
);
3266 else if (dstr2
[dpos2
] == '-' || dstr2
[dpos2
] == '\'')
3268 inc_str_pos( &str2
, &len2
, &dpos2
, &dlen2
);
3273 ce1
= get_weight( dstr1
[dpos1
], type
);
3276 inc_str_pos( &str1
, &len1
, &dpos1
, &dlen1
);
3279 ce2
= get_weight( dstr2
[dpos2
], type
);
3282 inc_str_pos( &str2
, &len2
, &dpos2
, &dlen2
);
3286 if (ce1
- ce2
) return ce1
- ce2
;
3288 inc_str_pos( &str1
, &len1
, &dpos1
, &dlen1
);
3289 inc_str_pos( &str2
, &len2
, &dpos2
, &dlen2
);
3293 if (!dlen1
&& !(dstr1
= get_decomposition( *str1
, &dlen1
))) dstr1
= str1
;
3294 ce1
= get_weight( dstr1
[dpos1
], type
);
3296 inc_str_pos( &str1
, &len1
, &dpos1
, &dlen1
);
3300 if (!dlen2
&& !(dstr2
= get_decomposition( *str2
, &dlen2
))) dstr2
= str2
;
3301 ce2
= get_weight( dstr2
[dpos2
], type
);
3303 inc_str_pos( &str2
, &len2
, &dpos2
, &dlen2
);
3309 static int compare_tzdate( const TIME_FIELDS
*tf
, const SYSTEMTIME
*compare
)
3311 static const int month_lengths
[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
3312 int first
, last
, limit
, dayinsecs
;
3314 if (tf
->Month
< compare
->wMonth
) return -1; /* We are in a month before the date limit. */
3315 if (tf
->Month
> compare
->wMonth
) return 1; /* We are in a month after the date limit. */
3317 /* if year is 0 then date is in day-of-week format, otherwise
3318 * it's absolute date.
3320 if (!compare
->wYear
)
3322 /* wDay is interpreted as number of the week in the month
3323 * 5 means: the last week in the month */
3324 /* calculate the day of the first DayOfWeek in the month */
3325 first
= (6 + compare
->wDayOfWeek
- tf
->Weekday
+ tf
->Day
) % 7 + 1;
3326 /* check needed for the 5th weekday of the month */
3327 last
= month_lengths
[tf
->Month
- 1] +
3328 (tf
->Month
== 2 && (!(tf
->Year
% 4) && (tf
->Year
% 100 || !(tf
->Year
% 400))));
3329 limit
= first
+ 7 * (compare
->wDay
- 1);
3330 if (limit
> last
) limit
-= 7;
3332 else limit
= compare
->wDay
;
3334 limit
= ((limit
* 24 + compare
->wHour
) * 60 + compare
->wMinute
) * 60;
3335 dayinsecs
= ((tf
->Day
* 24 + tf
->Hour
) * 60 + tf
->Minute
) * 60 + tf
->Second
;
3336 return dayinsecs
- limit
;
3340 static DWORD
get_timezone_id( const TIME_ZONE_INFORMATION
*info
, LARGE_INTEGER time
, BOOL is_local
)
3343 BOOL before_standard_date
, after_daylight_date
;
3347 if (!info
->DaylightDate
.wMonth
) return TIME_ZONE_ID_UNKNOWN
;
3349 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
3350 if (info
->StandardDate
.wMonth
== 0 ||
3351 (info
->StandardDate
.wYear
== 0 &&
3352 (info
->StandardDate
.wDay
< 1 || info
->StandardDate
.wDay
> 5 ||
3353 info
->DaylightDate
.wDay
< 1 || info
->DaylightDate
.wDay
> 5)))
3355 SetLastError( ERROR_INVALID_PARAMETER
);
3356 return TIME_ZONE_ID_INVALID
;
3359 if (!is_local
) time
.QuadPart
-= info
->Bias
* (LONGLONG
)600000000;
3360 RtlTimeToTimeFields( &time
, &tf
);
3364 t2
.QuadPart
= time
.QuadPart
- info
->DaylightBias
* (LONGLONG
)600000000;
3365 RtlTimeToTimeFields( &t2
, &tf
);
3367 if (tf
.Year
== year
)
3368 before_standard_date
= compare_tzdate( &tf
, &info
->StandardDate
) < 0;
3370 before_standard_date
= tf
.Year
< year
;
3374 t2
.QuadPart
= time
.QuadPart
- info
->StandardBias
* (LONGLONG
)600000000;
3375 RtlTimeToTimeFields( &t2
, &tf
);
3377 if (tf
.Year
== year
)
3378 after_daylight_date
= compare_tzdate( &tf
, &info
->DaylightDate
) >= 0;
3380 after_daylight_date
= tf
.Year
> year
;
3382 if (info
->DaylightDate
.wMonth
< info
->StandardDate
.wMonth
) /* Northern hemisphere */
3384 if (before_standard_date
&& after_daylight_date
) return TIME_ZONE_ID_DAYLIGHT
;
3386 else /* Down south */
3388 if (before_standard_date
|| after_daylight_date
) return TIME_ZONE_ID_DAYLIGHT
;
3390 return TIME_ZONE_ID_STANDARD
;
3394 /* Note: the Internal_ functions are not documented. The number of parameters
3395 * should be correct, but their exact meaning may not.
3398 /******************************************************************************
3399 * Internal_EnumCalendarInfo (kernelbase.@)
3401 BOOL WINAPI DECLSPEC_HOTPATCH
Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc
, LCID lcid
, CALID id
,
3402 CALTYPE type
, BOOL unicode
, BOOL ex
,
3403 BOOL exex
, LPARAM lparam
)
3406 CALID calendars
[2] = { id
};
3411 SetLastError( ERROR_INVALID_PARAMETER
);
3415 if (id
== ENUM_ALL_CALENDARS
)
3417 if (!GetLocaleInfoW( lcid
, LOCALE_ICALENDARTYPE
| LOCALE_RETURN_NUMBER
,
3418 (WCHAR
*)&calendars
[0], sizeof(calendars
[0]) / sizeof(WCHAR
) )) return FALSE
;
3419 if (!GetLocaleInfoW( lcid
, LOCALE_IOPTIONALCALENDAR
| LOCALE_RETURN_NUMBER
,
3420 (WCHAR
*)&calendars
[1], sizeof(calendars
[1]) / sizeof(WCHAR
) )) calendars
[1] = 0;
3423 for (i
= 0; i
< ARRAY_SIZE(calendars
) && calendars
[i
]; i
++)
3426 if (type
& CAL_RETURN_NUMBER
)
3427 ret
= GetCalendarInfoW( lcid
, id
, type
, NULL
, 0, (LPDWORD
)buffer
);
3429 ret
= GetCalendarInfoW( lcid
, id
, type
, buffer
, ARRAY_SIZE(buffer
), NULL
);
3433 ret
= GetCalendarInfoW( lcid
, id
, type
, bufW
, ARRAY_SIZE(bufW
), NULL
);
3434 if (ret
) WideCharToMultiByte( CP_ACP
, 0, bufW
, -1, (char *)buffer
, sizeof(buffer
), NULL
, NULL
);
3439 if (exex
) ret
= ((CALINFO_ENUMPROCEXEX
)proc
)( buffer
, id
, NULL
, lparam
);
3440 else if (ex
) ret
= ((CALINFO_ENUMPROCEXW
)proc
)( buffer
, id
);
3441 else ret
= proc( buffer
);
3449 /**************************************************************************
3450 * Internal_EnumDateFormats (kernelbase.@)
3452 BOOL WINAPI DECLSPEC_HOTPATCH
Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
,
3453 BOOL unicode
, BOOL ex
, BOOL exex
, LPARAM lparam
)
3462 SetLastError( ERROR_INVALID_PARAMETER
);
3465 if (!GetLocaleInfoW( lcid
, LOCALE_ICALENDARTYPE
|LOCALE_RETURN_NUMBER
,
3466 (LPWSTR
)&cal_id
, sizeof(cal_id
)/sizeof(WCHAR
) ))
3469 switch (flags
& ~LOCALE_USE_CP_ACP
)
3472 case DATE_SHORTDATE
:
3473 lctype
= LOCALE_SSHORTDATE
;
3476 lctype
= LOCALE_SLONGDATE
;
3478 case DATE_YEARMONTH
:
3479 lctype
= LOCALE_SYEARMONTH
;
3482 FIXME( "unknown date format 0x%08lx\n", flags
);
3483 SetLastError( ERROR_INVALID_PARAMETER
);
3487 lctype
|= flags
& LOCALE_USE_CP_ACP
;
3489 ret
= GetLocaleInfoW( lcid
, lctype
, buffer
, ARRAY_SIZE(buffer
) );
3491 ret
= GetLocaleInfoA( lcid
, lctype
, (char *)buffer
, sizeof(buffer
) );
3495 if (exex
) ((DATEFMT_ENUMPROCEXEX
)proc
)( buffer
, cal_id
, lparam
);
3496 else if (ex
) ((DATEFMT_ENUMPROCEXW
)proc
)( buffer
, cal_id
);
3497 else proc( buffer
);
3503 /******************************************************************************
3504 * Internal_EnumLanguageGroupLocales (kernelbase.@)
3506 BOOL WINAPI DECLSPEC_HOTPATCH
Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc
, LGRPID id
,
3507 DWORD flags
, LONG_PTR param
, BOOL unicode
)
3509 WCHAR name
[10], value
[10];
3510 DWORD name_len
, value_len
, type
, index
= 0, alt
= 0;
3514 if (!proc
|| id
< LGRPID_WESTERN_EUROPE
|| id
> LGRPID_ARMENIAN
)
3516 SetLastError( ERROR_INVALID_PARAMETER
);
3520 if (RegOpenKeyExW( nls_key
, L
"Locale", 0, KEY_READ
, &key
)) return FALSE
;
3521 if (RegOpenKeyExW( key
, L
"Alternate Sorts", 0, KEY_READ
, &altkey
)) altkey
= 0;
3525 name_len
= ARRAY_SIZE(name
);
3526 value_len
= sizeof(value
);
3527 if (RegEnumValueW( alt
? altkey
: key
, index
++, name
, &name_len
, NULL
,
3528 &type
, (BYTE
*)value
, &value_len
))
3534 if (type
!= REG_SZ
) continue;
3535 if (id
!= wcstoul( value
, NULL
, 16 )) continue;
3536 lcid
= wcstoul( name
, NULL
, 16 );
3540 WideCharToMultiByte( CP_ACP
, 0, name
, -1, nameA
, sizeof(nameA
), NULL
, NULL
);
3541 if (!((LANGGROUPLOCALE_ENUMPROCA
)proc
)( id
, lcid
, nameA
, param
)) break;
3543 else if (!proc( id
, lcid
, name
, param
)) break;
3545 RegCloseKey( altkey
);
3551 /***********************************************************************
3552 * Internal_EnumSystemCodePages (kernelbase.@)
3554 BOOL WINAPI DECLSPEC_HOTPATCH
Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc
, DWORD flags
,
3558 DWORD name_len
, type
, index
= 0;
3561 if (RegOpenKeyExW( nls_key
, L
"Codepage", 0, KEY_READ
, &key
)) return FALSE
;
3565 name_len
= ARRAY_SIZE(name
);
3566 if (RegEnumValueW( key
, index
++, name
, &name_len
, NULL
, &type
, NULL
, NULL
)) break;
3567 if (type
!= REG_SZ
) continue;
3568 if (!wcstoul( name
, NULL
, 10 )) continue;
3572 WideCharToMultiByte( CP_ACP
, 0, name
, -1, nameA
, sizeof(nameA
), NULL
, NULL
);
3573 if (!((CODEPAGE_ENUMPROCA
)proc
)( nameA
)) break;
3575 else if (!proc( name
)) break;
3582 /******************************************************************************
3583 * Internal_EnumSystemLanguageGroups (kernelbase.@)
3585 BOOL WINAPI DECLSPEC_HOTPATCH
Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc
,
3586 DWORD flags
, LONG_PTR param
, BOOL unicode
)
3588 WCHAR name
[10], value
[10], descr
[80];
3589 DWORD name_len
, value_len
, type
, index
= 0;
3595 SetLastError( ERROR_INVALID_PARAMETER
);
3602 flags
= LGRPID_INSTALLED
;
3604 case LGRPID_INSTALLED
:
3605 case LGRPID_SUPPORTED
:
3608 SetLastError( ERROR_INVALID_FLAGS
);
3612 if (RegOpenKeyExW( nls_key
, L
"Language Groups", 0, KEY_READ
, &key
)) return FALSE
;
3616 name_len
= ARRAY_SIZE(name
);
3617 value_len
= sizeof(value
);
3618 if (RegEnumValueW( key
, index
++, name
, &name_len
, NULL
, &type
, (BYTE
*)value
, &value_len
)) break;
3619 if (type
!= REG_SZ
) continue;
3621 id
= wcstoul( name
, NULL
, 16 );
3623 if (!(flags
& LGRPID_SUPPORTED
) && !wcstoul( value
, NULL
, 10 )) continue;
3624 if (!LoadStringW( kernelbase_handle
, id
, descr
, ARRAY_SIZE(descr
) )) descr
[0] = 0;
3625 TRACE( "%p: %lu %s %s %lx %Ix\n", proc
, id
, debugstr_w(name
), debugstr_w(descr
), flags
, param
);
3628 char nameA
[10], descrA
[80];
3629 WideCharToMultiByte( CP_ACP
, 0, name
, -1, nameA
, sizeof(nameA
), NULL
, NULL
);
3630 WideCharToMultiByte( CP_ACP
, 0, descr
, -1, descrA
, sizeof(descrA
), NULL
, NULL
);
3631 if (!((LANGUAGEGROUP_ENUMPROCA
)proc
)( id
, nameA
, descrA
, flags
, param
)) break;
3633 else if (!proc( id
, name
, descr
, flags
, param
)) break;
3640 /**************************************************************************
3641 * Internal_EnumTimeFormats (kernelbase.@)
3643 BOOL WINAPI DECLSPEC_HOTPATCH
Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
,
3644 BOOL unicode
, BOOL ex
, LPARAM lparam
)
3652 SetLastError( ERROR_INVALID_PARAMETER
);
3655 switch (flags
& ~LOCALE_USE_CP_ACP
)
3658 lctype
= LOCALE_STIMEFORMAT
;
3660 case TIME_NOSECONDS
:
3661 lctype
= LOCALE_SSHORTTIME
;
3664 FIXME( "Unknown time format %lx\n", flags
);
3665 SetLastError( ERROR_INVALID_PARAMETER
);
3669 lctype
|= flags
& LOCALE_USE_CP_ACP
;
3671 ret
= GetLocaleInfoW( lcid
, lctype
, buffer
, ARRAY_SIZE(buffer
) );
3673 ret
= GetLocaleInfoA( lcid
, lctype
, (char *)buffer
, sizeof(buffer
) );
3677 if (ex
) ((TIMEFMT_ENUMPROCEX
)proc
)( buffer
, lparam
);
3678 else proc( buffer
);
3684 /******************************************************************************
3685 * Internal_EnumUILanguages (kernelbase.@)
3687 BOOL WINAPI DECLSPEC_HOTPATCH
Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc
, DWORD flags
,
3688 LONG_PTR param
, BOOL unicode
)
3690 WCHAR nameW
[LOCALE_NAME_MAX_LENGTH
];
3691 char nameA
[LOCALE_NAME_MAX_LENGTH
];
3696 SetLastError( ERROR_INVALID_PARAMETER
);
3699 if (flags
& ~(MUI_LANGUAGE_ID
| MUI_LANGUAGE_NAME
))
3701 SetLastError( ERROR_INVALID_FLAGS
);
3705 for (i
= 0; i
< locale_table
->nb_lcnames
; i
++)
3707 if (!lcnames_index
[i
].name
) continue; /* skip invariant locale */
3708 if (lcnames_index
[i
].id
& 0x80000000) continue; /* skip aliases */
3709 if (!get_locale_data( lcnames_index
[i
].idx
)->inotneutral
) continue; /* skip neutral locales */
3710 if (!SORTIDFROMLCID( lcnames_index
[i
].id
) != !(flags
& LCID_ALTERNATE_SORTS
))
3711 continue; /* skip alternate sorts */
3712 if (flags
& MUI_LANGUAGE_NAME
)
3714 const WCHAR
*str
= locale_strings
+ lcnames_index
[i
].name
;
3718 memcpy( nameW
, str
+ 1, (*str
+ 1) * sizeof(WCHAR
) );
3719 if (!proc( nameW
, param
)) break;
3723 WideCharToMultiByte( CP_ACP
, 0, str
+ 1, -1, nameA
, sizeof(nameA
), NULL
, NULL
);
3724 if (!((UILANGUAGE_ENUMPROCA
)proc
)( nameA
, param
)) break;
3729 if (lcnames_index
[i
].id
== LOCALE_CUSTOM_UNSPECIFIED
) continue; /* skip locales with no lcid */
3732 swprintf( nameW
, ARRAY_SIZE(nameW
), L
"%04lx", lcnames_index
[i
].id
);
3733 if (!proc( nameW
, param
)) break;
3737 sprintf( nameA
, "%04x", lcnames_index
[i
].id
);
3738 if (!((UILANGUAGE_ENUMPROCA
)proc
)( nameA
, param
)) break;
3746 /******************************************************************************
3747 * CompareStringEx (kernelbase.@)
3749 INT WINAPI
CompareStringEx( const WCHAR
*locale
, DWORD flags
, const WCHAR
*str1
, int len1
,
3750 const WCHAR
*str2
, int len2
, NLSVERSIONINFO
*version
,
3751 void *reserved
, LPARAM handle
)
3753 DWORD supported_flags
= NORM_IGNORECASE
| NORM_IGNORENONSPACE
| NORM_IGNORESYMBOLS
| SORT_STRINGSORT
|
3754 NORM_IGNOREKANATYPE
| NORM_IGNOREWIDTH
| LOCALE_USE_CP_ACP
;
3755 DWORD semistub_flags
= NORM_LINGUISTIC_CASING
| LINGUISTIC_IGNORECASE
| LINGUISTIC_IGNOREDIACRITIC
|
3756 SORT_DIGITSASNUMBERS
| 0x10000000;
3757 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3761 if (version
) FIXME( "unexpected version parameter\n" );
3762 if (reserved
) FIXME( "unexpected reserved value\n" );
3763 if (handle
) FIXME( "unexpected handle\n" );
3767 SetLastError( ERROR_INVALID_PARAMETER
);
3771 if (flags
& ~(supported_flags
| semistub_flags
))
3773 SetLastError( ERROR_INVALID_FLAGS
);
3777 if (flags
& semistub_flags
)
3779 if (!once
++) FIXME( "semi-stub behavior for flag(s) 0x%lx\n", flags
& semistub_flags
);
3782 if (len1
< 0) len1
= lstrlenW(str1
);
3783 if (len2
< 0) len2
= lstrlenW(str2
);
3785 ret
= compare_weights( flags
, str1
, len1
, str2
, len2
, UNICODE_WEIGHT
);
3788 if (!(flags
& NORM_IGNORENONSPACE
))
3789 ret
= compare_weights( flags
, str1
, len1
, str2
, len2
, DIACRITIC_WEIGHT
);
3790 if (!ret
&& !(flags
& NORM_IGNORECASE
))
3791 ret
= compare_weights( flags
, str1
, len1
, str2
, len2
, CASE_WEIGHT
);
3793 if (!ret
) return CSTR_EQUAL
;
3794 return (ret
< 0) ? CSTR_LESS_THAN
: CSTR_GREATER_THAN
;
3798 /******************************************************************************
3799 * CompareStringA (kernelbase.@)
3801 INT WINAPI DECLSPEC_HOTPATCH
CompareStringA( LCID lcid
, DWORD flags
, const char *str1
, int len1
,
3802 const char *str2
, int len2
)
3804 WCHAR
*buf1W
= NtCurrentTeb()->StaticUnicodeBuffer
;
3805 WCHAR
*buf2W
= buf1W
+ 130;
3806 LPWSTR str1W
, str2W
;
3807 INT len1W
= 0, len2W
= 0, ret
;
3808 UINT locale_cp
= CP_ACP
;
3812 SetLastError( ERROR_INVALID_PARAMETER
);
3816 if (flags
& SORT_DIGITSASNUMBERS
)
3818 SetLastError( ERROR_INVALID_FLAGS
);
3822 if (len1
< 0) len1
= strlen(str1
);
3823 if (len2
< 0) len2
= strlen(str2
);
3825 locale_cp
= get_lcid_codepage( lcid
, flags
);
3828 if (len1
<= 130) len1W
= MultiByteToWideChar( locale_cp
, 0, str1
, len1
, buf1W
, 130 );
3829 if (len1W
) str1W
= buf1W
;
3832 len1W
= MultiByteToWideChar( locale_cp
, 0, str1
, len1
, NULL
, 0 );
3833 str1W
= HeapAlloc( GetProcessHeap(), 0, len1W
* sizeof(WCHAR
) );
3836 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
3839 MultiByteToWideChar( locale_cp
, 0, str1
, len1
, str1W
, len1W
);
3850 if (len2
<= 130) len2W
= MultiByteToWideChar( locale_cp
, 0, str2
, len2
, buf2W
, 130 );
3851 if (len2W
) str2W
= buf2W
;
3854 len2W
= MultiByteToWideChar( locale_cp
, 0, str2
, len2
, NULL
, 0 );
3855 str2W
= HeapAlloc( GetProcessHeap(), 0, len2W
* sizeof(WCHAR
) );
3858 if (str1W
!= buf1W
) HeapFree( GetProcessHeap(), 0, str1W
);
3859 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
3862 MultiByteToWideChar( locale_cp
, 0, str2
, len2
, str2W
, len2W
);
3871 ret
= CompareStringW( lcid
, flags
, str1W
, len1W
, str2W
, len2W
);
3873 if (str1W
!= buf1W
) HeapFree( GetProcessHeap(), 0, str1W
);
3874 if (str2W
!= buf2W
) HeapFree( GetProcessHeap(), 0, str2W
);
3879 /******************************************************************************
3880 * CompareStringW (kernelbase.@)
3882 INT WINAPI DECLSPEC_HOTPATCH
CompareStringW( LCID lcid
, DWORD flags
, const WCHAR
*str1
, int len1
,
3883 const WCHAR
*str2
, int len2
)
3885 return CompareStringEx( NULL
, flags
, str1
, len1
, str2
, len2
, NULL
, NULL
, 0 );
3889 /******************************************************************************
3890 * CompareStringOrdinal (kernelbase.@)
3892 INT WINAPI DECLSPEC_HOTPATCH
CompareStringOrdinal( const WCHAR
*str1
, INT len1
,
3893 const WCHAR
*str2
, INT len2
, BOOL ignore_case
)
3899 SetLastError( ERROR_INVALID_PARAMETER
);
3902 if (len1
< 0) len1
= lstrlenW( str1
);
3903 if (len2
< 0) len2
= lstrlenW( str2
);
3905 ret
= RtlCompareUnicodeStrings( str1
, len1
, str2
, len2
, ignore_case
);
3906 if (ret
< 0) return CSTR_LESS_THAN
;
3907 if (ret
> 0) return CSTR_GREATER_THAN
;
3912 /******************************************************************************
3913 * ConvertDefaultLocale (kernelbase.@)
3915 LCID WINAPI DECLSPEC_HOTPATCH
ConvertDefaultLocale( LCID lcid
)
3917 get_locale_by_id( &lcid
, 0 );
3922 /******************************************************************************
3923 * EnumCalendarInfoW (kernelbase.@)
3925 BOOL WINAPI DECLSPEC_HOTPATCH
EnumCalendarInfoW( CALINFO_ENUMPROCW proc
, LCID lcid
,
3926 CALID id
, CALTYPE type
)
3928 return Internal_EnumCalendarInfo( proc
, lcid
, id
, type
, TRUE
, FALSE
, FALSE
, 0 );
3932 /******************************************************************************
3933 * EnumCalendarInfoExW (kernelbase.@)
3935 BOOL WINAPI DECLSPEC_HOTPATCH
EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc
, LCID lcid
,
3936 CALID id
, CALTYPE type
)
3938 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW
)proc
, lcid
, id
, type
, TRUE
, TRUE
, FALSE
, 0 );
3941 /******************************************************************************
3942 * EnumCalendarInfoExEx (kernelbase.@)
3944 BOOL WINAPI DECLSPEC_HOTPATCH
EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc
, LPCWSTR locale
, CALID id
,
3945 LPCWSTR reserved
, CALTYPE type
, LPARAM lparam
)
3947 LCID lcid
= LocaleNameToLCID( locale
, 0 );
3948 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW
)proc
, lcid
, id
, type
, TRUE
, TRUE
, TRUE
, lparam
);
3952 /**************************************************************************
3953 * EnumDateFormatsW (kernelbase.@)
3955 BOOL WINAPI DECLSPEC_HOTPATCH
EnumDateFormatsW( DATEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
3957 return Internal_EnumDateFormats( proc
, lcid
, flags
, TRUE
, FALSE
, FALSE
, 0 );
3961 /**************************************************************************
3962 * EnumDateFormatsExW (kernelbase.@)
3964 BOOL WINAPI DECLSPEC_HOTPATCH
EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc
, LCID lcid
, DWORD flags
)
3966 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW
)proc
, lcid
, flags
, TRUE
, TRUE
, FALSE
, 0 );
3970 /**************************************************************************
3971 * EnumDateFormatsExEx (kernelbase.@)
3973 BOOL WINAPI DECLSPEC_HOTPATCH
EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc
, const WCHAR
*locale
,
3974 DWORD flags
, LPARAM lparam
)
3976 LCID lcid
= LocaleNameToLCID( locale
, 0 );
3977 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW
)proc
, lcid
, flags
, TRUE
, TRUE
, TRUE
, lparam
);
3982 /******************************************************************************
3983 * EnumDynamicTimeZoneInformation (kernelbase.@)
3985 DWORD WINAPI DECLSPEC_HOTPATCH
EnumDynamicTimeZoneInformation( DWORD index
,
3986 DYNAMIC_TIME_ZONE_INFORMATION
*info
)
3988 DYNAMIC_TIME_ZONE_INFORMATION tz
;
3992 if (!info
) return ERROR_INVALID_PARAMETER
;
3994 size
= ARRAY_SIZE(tz
.TimeZoneKeyName
);
3995 ret
= RegEnumKeyExW( tz_key
, index
, tz
.TimeZoneKeyName
, &size
, NULL
, NULL
, NULL
, NULL
);
3996 if (ret
) return ret
;
3998 tz
.DynamicDaylightTimeDisabled
= TRUE
;
3999 if (!GetTimeZoneInformationForYear( 0, &tz
, (TIME_ZONE_INFORMATION
*)info
)) return GetLastError();
4001 lstrcpyW( info
->TimeZoneKeyName
, tz
.TimeZoneKeyName
);
4002 info
->DynamicDaylightTimeDisabled
= FALSE
;
4007 /******************************************************************************
4008 * EnumLanguageGroupLocalesW (kernelbase.@)
4010 BOOL WINAPI DECLSPEC_HOTPATCH
EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc
, LGRPID id
,
4011 DWORD flags
, LONG_PTR param
)
4013 return Internal_EnumLanguageGroupLocales( proc
, id
, flags
, param
, TRUE
);
4017 /******************************************************************************
4018 * EnumUILanguagesW (kernelbase.@)
4020 BOOL WINAPI DECLSPEC_HOTPATCH
EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc
, DWORD flags
, LONG_PTR param
)
4022 return Internal_EnumUILanguages( proc
, flags
, param
, TRUE
);
4026 /***********************************************************************
4027 * EnumSystemCodePagesW (kernelbase.@)
4029 BOOL WINAPI DECLSPEC_HOTPATCH
EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc
, DWORD flags
)
4031 return Internal_EnumSystemCodePages( proc
, flags
, TRUE
);
4035 /******************************************************************************
4036 * EnumSystemGeoID (kernelbase.@)
4038 BOOL WINAPI DECLSPEC_HOTPATCH
EnumSystemGeoID( GEOCLASS
class, GEOID parent
, GEO_ENUMPROC proc
)
4042 TRACE( "(%ld, %ld, %p)\n", class, parent
, proc
);
4046 SetLastError( ERROR_INVALID_PARAMETER
);
4049 if (class != GEOCLASS_NATION
&& class != GEOCLASS_REGION
&& class != GEOCLASS_ALL
)
4051 SetLastError( ERROR_INVALID_FLAGS
);
4055 for (i
= 0; i
< geo_ids_count
; i
++)
4057 if (class != GEOCLASS_ALL
&& geo_ids
[i
].class != class) continue;
4058 if (parent
&& geo_ids
[i
].parent
!= parent
) continue;
4059 if (!proc( geo_ids
[i
].id
)) break;
4065 /******************************************************************************
4066 * EnumSystemLanguageGroupsW (kernelbase.@)
4068 BOOL WINAPI DECLSPEC_HOTPATCH
EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc
,
4069 DWORD flags
, LONG_PTR param
)
4071 return Internal_EnumSystemLanguageGroups( proc
, flags
, param
, TRUE
);
4075 /******************************************************************************
4076 * EnumSystemLocalesA (kernelbase.@)
4078 BOOL WINAPI DECLSPEC_HOTPATCH
EnumSystemLocalesA( LOCALE_ENUMPROCA proc
, DWORD flags
)
4083 for (i
= 0; i
< locale_table
->nb_lcnames
; i
++)
4085 if (!lcnames_index
[i
].name
) continue; /* skip invariant locale */
4086 if (lcnames_index
[i
].id
== LOCALE_CUSTOM_UNSPECIFIED
) continue; /* skip locales with no lcid */
4087 if (lcnames_index
[i
].id
& 0x80000000) continue; /* skip aliases */
4088 if (!get_locale_data( lcnames_index
[i
].idx
)->inotneutral
) continue; /* skip neutral locales */
4089 if (!SORTIDFROMLCID( lcnames_index
[i
].id
) != !(flags
& LCID_ALTERNATE_SORTS
))
4090 continue; /* skip alternate sorts */
4091 sprintf( name
, "%08x", lcnames_index
[i
].id
);
4092 if (!proc( name
)) break;
4098 /******************************************************************************
4099 * EnumSystemLocalesW (kernelbase.@)
4101 BOOL WINAPI DECLSPEC_HOTPATCH
EnumSystemLocalesW( LOCALE_ENUMPROCW proc
, DWORD flags
)
4106 for (i
= 0; i
< locale_table
->nb_lcnames
; i
++)
4108 if (!lcnames_index
[i
].name
) continue; /* skip invariant locale */
4109 if (lcnames_index
[i
].id
== LOCALE_CUSTOM_UNSPECIFIED
) continue; /* skip locales with no lcid */
4110 if (lcnames_index
[i
].id
& 0x80000000) continue; /* skip aliases */
4111 if (!get_locale_data( lcnames_index
[i
].idx
)->inotneutral
) continue; /* skip neutral locales */
4112 if (!SORTIDFROMLCID( lcnames_index
[i
].id
) != !(flags
& LCID_ALTERNATE_SORTS
))
4113 continue; /* skip alternate sorts */
4114 swprintf( name
, ARRAY_SIZE(name
), L
"%08lx", lcnames_index
[i
].id
);
4115 if (!proc( name
)) break;
4121 /******************************************************************************
4122 * EnumSystemLocalesEx (kernelbase.@)
4124 BOOL WINAPI DECLSPEC_HOTPATCH
EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc
, DWORD wanted_flags
,
4125 LPARAM param
, void *reserved
)
4127 WCHAR buffer
[LOCALE_NAME_MAX_LENGTH
];
4132 SetLastError( ERROR_INVALID_PARAMETER
);
4136 for (i
= 0; i
< locale_table
->nb_lcnames
; i
++)
4138 const NLS_LOCALE_DATA
*locale
= get_locale_data( lcnames_index
[i
].idx
);
4139 const WCHAR
*str
= locale_strings
+ lcnames_index
[i
].name
;
4141 if (lcnames_index
[i
].id
& 0x80000000) continue; /* skip aliases */
4142 memcpy( buffer
, str
+ 1, (*str
+ 1) * sizeof(WCHAR
) );
4143 if (SORTIDFROMLCID( lcnames_index
[i
].id
) || wcschr( str
+ 1, '_' ))
4144 flags
= LOCALE_ALTERNATE_SORTS
;
4146 flags
= LOCALE_WINDOWS
| (locale
->inotneutral
? LOCALE_SPECIFICDATA
: LOCALE_NEUTRALDATA
);
4147 if (wanted_flags
&& !(flags
& wanted_flags
)) continue;
4148 if (!proc( buffer
, flags
, param
)) break;
4154 /**************************************************************************
4155 * EnumTimeFormatsW (kernelbase.@)
4157 BOOL WINAPI DECLSPEC_HOTPATCH
EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
4159 return Internal_EnumTimeFormats( proc
, lcid
, flags
, TRUE
, FALSE
, 0 );
4163 /**************************************************************************
4164 * EnumTimeFormatsEx (kernelbase.@)
4166 BOOL WINAPI DECLSPEC_HOTPATCH
EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc
, const WCHAR
*locale
,
4167 DWORD flags
, LPARAM lparam
)
4169 LCID lcid
= LocaleNameToLCID( locale
, 0 );
4170 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW
)proc
, lcid
, flags
, TRUE
, TRUE
, lparam
);
4174 /**************************************************************************
4175 * FindNLSString (kernelbase.@)
4177 INT WINAPI DECLSPEC_HOTPATCH
FindNLSString( LCID lcid
, DWORD flags
, const WCHAR
*src
,
4178 int srclen
, const WCHAR
*value
, int valuelen
, int *found
)
4180 WCHAR locale
[LOCALE_NAME_MAX_LENGTH
];
4182 LCIDToLocaleName( lcid
, locale
, ARRAY_SIZE(locale
), 0 );
4183 return FindNLSStringEx( locale
, flags
, src
, srclen
, value
, valuelen
, found
, NULL
, NULL
, 0 );
4187 /**************************************************************************
4188 * FindNLSStringEx (kernelbase.@)
4190 INT WINAPI DECLSPEC_HOTPATCH
FindNLSStringEx( const WCHAR
*locale
, DWORD flags
, const WCHAR
*src
,
4191 int srclen
, const WCHAR
*value
, int valuelen
, int *found
,
4192 NLSVERSIONINFO
*version
, void *reserved
, LPARAM handle
)
4194 /* FIXME: this function should normalize strings before calling CompareStringEx() */
4196 int offset
, inc
, count
;
4198 TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale
), flags
,
4199 wine_dbgstr_w(src
), srclen
, wine_dbgstr_w(value
), valuelen
, found
,
4200 version
, reserved
, handle
);
4202 if (version
|| reserved
|| handle
|| !IsValidLocaleName(locale
) ||
4203 !src
|| !srclen
|| srclen
< -1 || !value
|| !valuelen
|| valuelen
< -1)
4205 SetLastError( ERROR_INVALID_PARAMETER
);
4208 if (srclen
== -1) srclen
= lstrlenW(src
);
4209 if (valuelen
== -1) valuelen
= lstrlenW(value
);
4212 if (srclen
< 0) return -1;
4214 mask
= flags
& ~(FIND_FROMSTART
| FIND_FROMEND
| FIND_STARTSWITH
| FIND_ENDSWITH
);
4215 count
= flags
& (FIND_FROMSTART
| FIND_FROMEND
) ? srclen
+ 1 : 1;
4216 offset
= flags
& (FIND_FROMSTART
| FIND_STARTSWITH
) ? 0 : srclen
;
4217 inc
= flags
& (FIND_FROMSTART
| FIND_STARTSWITH
) ? 1 : -1;
4220 if (CompareStringEx( locale
, mask
, src
+ offset
, valuelen
,
4221 value
, valuelen
, NULL
, NULL
, 0 ) == CSTR_EQUAL
)
4223 if (found
) *found
= valuelen
;
4232 /******************************************************************************
4233 * FindStringOrdinal (kernelbase.@)
4235 INT WINAPI DECLSPEC_HOTPATCH
FindStringOrdinal( DWORD flag
, const WCHAR
*src
, INT src_size
,
4236 const WCHAR
*val
, INT val_size
, BOOL ignore_case
)
4238 INT offset
, inc
, count
;
4240 TRACE( "%#lx %s %d %s %d %d\n", flag
, wine_dbgstr_w(src
), src_size
,
4241 wine_dbgstr_w(val
), val_size
, ignore_case
);
4245 SetLastError( ERROR_INVALID_PARAMETER
);
4249 if (flag
!= FIND_FROMSTART
&& flag
!= FIND_FROMEND
&& flag
!= FIND_STARTSWITH
&& flag
!= FIND_ENDSWITH
)
4251 SetLastError( ERROR_INVALID_FLAGS
);
4255 if (src_size
== -1) src_size
= lstrlenW( src
);
4256 if (val_size
== -1) val_size
= lstrlenW( val
);
4258 SetLastError( ERROR_SUCCESS
);
4259 src_size
-= val_size
;
4260 if (src_size
< 0) return -1;
4262 count
= flag
& (FIND_FROMSTART
| FIND_FROMEND
) ? src_size
+ 1 : 1;
4263 offset
= flag
& (FIND_FROMSTART
| FIND_STARTSWITH
) ? 0 : src_size
;
4264 inc
= flag
& (FIND_FROMSTART
| FIND_STARTSWITH
) ? 1 : -1;
4267 if (CompareStringOrdinal( src
+ offset
, val_size
, val
, val_size
, ignore_case
) == CSTR_EQUAL
)
4275 /******************************************************************************
4276 * FoldStringW (kernelbase.@)
4278 INT WINAPI DECLSPEC_HOTPATCH
FoldStringW( DWORD flags
, LPCWSTR src
, INT srclen
, LPWSTR dst
, INT dstlen
)
4284 if (!src
|| !srclen
|| dstlen
< 0 || (dstlen
&& !dst
) || src
== dst
)
4286 SetLastError( ERROR_INVALID_PARAMETER
);
4289 if (srclen
== -1) srclen
= lstrlenW(src
) + 1;
4291 if (!dstlen
&& (flags
& (MAP_PRECOMPOSED
| MAP_FOLDCZONE
| MAP_COMPOSITE
)))
4294 if (!(buf
= RtlAllocateHeap( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4296 SetLastError( ERROR_OUTOFMEMORY
);
4303 status
= fold_string( flags
, src
, srclen
, buf
, &len
);
4304 if (buf
!= dst
) RtlFreeHeap( GetProcessHeap(), 0, buf
);
4305 if (status
!= STATUS_BUFFER_TOO_SMALL
) break;
4306 if (!(buf
= RtlAllocateHeap( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4308 SetLastError( ERROR_OUTOFMEMORY
);
4312 if (status
== STATUS_INVALID_PARAMETER_1
)
4314 SetLastError( ERROR_INVALID_FLAGS
);
4317 if (!set_ntstatus( status
)) return 0;
4319 if (dstlen
&& dstlen
< len
) SetLastError( ERROR_INSUFFICIENT_BUFFER
);
4324 static const WCHAR
*get_message( DWORD flags
, const void *src
, UINT id
, UINT lang
,
4325 BOOL ansi
, WCHAR
**buffer
)
4329 if (!(flags
& FORMAT_MESSAGE_FROM_STRING
))
4331 const MESSAGE_RESOURCE_ENTRY
*entry
;
4332 NTSTATUS status
= STATUS_INVALID_PARAMETER
;
4334 if (flags
& FORMAT_MESSAGE_FROM_HMODULE
)
4336 HMODULE module
= (HMODULE
)src
;
4337 if (!module
) module
= GetModuleHandleW( 0 );
4338 status
= RtlFindMessage( module
, RT_MESSAGETABLE
, lang
, id
, &entry
);
4340 if (status
&& (flags
& FORMAT_MESSAGE_FROM_SYSTEM
))
4342 /* Fold win32 hresult to its embedded error code. */
4343 if (HRESULT_SEVERITY(id
) == SEVERITY_ERROR
&& HRESULT_FACILITY(id
) == FACILITY_WIN32
)
4344 id
= HRESULT_CODE( id
);
4345 status
= RtlFindMessage( kernelbase_handle
, RT_MESSAGETABLE
, lang
, id
, &entry
);
4347 if (!set_ntstatus( status
)) return NULL
;
4350 ansi
= !(entry
->Flags
& MESSAGE_RESOURCE_UNICODE
);
4353 if (!ansi
) return src
;
4354 len
= MultiByteToWideChar( CP_ACP
, 0, src
, -1, NULL
, 0 );
4355 if (!(*buffer
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) ))) return NULL
;
4356 MultiByteToWideChar( CP_ACP
, 0, src
, -1, *buffer
, len
);
4361 /***********************************************************************
4362 * FormatMessageA (kernelbase.@)
4364 DWORD WINAPI DECLSPEC_HOTPATCH
FormatMessageA( DWORD flags
, const void *source
, DWORD msgid
, DWORD langid
,
4365 char *buffer
, DWORD size
, va_list *args
)
4368 ULONG len
, retsize
= 0;
4369 ULONG width
= (flags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
);
4371 WCHAR
*result
, *message
= NULL
;
4374 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags
, source
, msgid
, langid
, buffer
, size
, args
);
4376 if (flags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
)
4380 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
4383 *(char **)buffer
= NULL
;
4387 SetLastError( ERROR_INVALID_PARAMETER
);
4391 if (width
== 0xff) width
= ~0u;
4393 if (!(src
= get_message( flags
, source
, msgid
, langid
, TRUE
, &message
))) return 0;
4395 if (!(result
= HeapAlloc( GetProcessHeap(), 0, 65536 )))
4396 status
= STATUS_NO_MEMORY
;
4398 status
= RtlFormatMessage( src
, width
, !!(flags
& FORMAT_MESSAGE_IGNORE_INSERTS
),
4399 TRUE
, !!(flags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
), args
,
4400 result
, 65536, &retsize
);
4402 HeapFree( GetProcessHeap(), 0, message
);
4404 if (status
== STATUS_BUFFER_OVERFLOW
)
4406 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
4409 if (!set_ntstatus( status
)) goto done
;
4411 len
= WideCharToMultiByte( CP_ACP
, 0, result
, retsize
/ sizeof(WCHAR
), NULL
, 0, NULL
, NULL
);
4414 SetLastError( ERROR_NO_WORK_DONE
);
4418 if (flags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
)
4420 char *buf
= LocalAlloc( LMEM_ZEROINIT
, max( size
, len
));
4423 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
4426 *(char **)buffer
= buf
;
4427 WideCharToMultiByte( CP_ACP
, 0, result
, retsize
/ sizeof(WCHAR
), buf
, max( size
, len
), NULL
, NULL
);
4429 else if (len
> size
)
4431 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
4434 else WideCharToMultiByte( CP_ACP
, 0, result
, retsize
/ sizeof(WCHAR
), buffer
, size
, NULL
, NULL
);
4439 HeapFree( GetProcessHeap(), 0, result
);
4444 /***********************************************************************
4445 * FormatMessageW (kernelbase.@)
4447 DWORD WINAPI DECLSPEC_HOTPATCH
FormatMessageW( DWORD flags
, const void *source
, DWORD msgid
, DWORD langid
,
4448 WCHAR
*buffer
, DWORD size
, va_list *args
)
4451 ULONG width
= (flags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
);
4453 WCHAR
*message
= NULL
;
4456 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags
, source
, msgid
, langid
, buffer
, size
, args
);
4460 SetLastError( ERROR_INVALID_PARAMETER
);
4464 if (width
== 0xff) width
= ~0u;
4466 if (flags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) *(LPWSTR
*)buffer
= NULL
;
4468 if (!(src
= get_message( flags
, source
, msgid
, langid
, FALSE
, &message
))) return 0;
4470 if (flags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
)
4473 ULONG alloc
= max( size
* sizeof(WCHAR
), 65536 );
4477 if (!(result
= HeapAlloc( GetProcessHeap(), 0, alloc
)))
4479 status
= STATUS_NO_MEMORY
;
4482 status
= RtlFormatMessage( src
, width
, !!(flags
& FORMAT_MESSAGE_IGNORE_INSERTS
),
4483 FALSE
, !!(flags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
), args
,
4484 result
, alloc
, &retsize
);
4487 if (retsize
<= sizeof(WCHAR
)) HeapFree( GetProcessHeap(), 0, result
);
4488 else *(WCHAR
**)buffer
= HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY
,
4489 result
, max( retsize
, size
* sizeof(WCHAR
) ));
4492 HeapFree( GetProcessHeap(), 0, result
);
4493 if (status
!= STATUS_BUFFER_OVERFLOW
) break;
4497 else status
= RtlFormatMessage( src
, width
, !!(flags
& FORMAT_MESSAGE_IGNORE_INSERTS
),
4498 FALSE
, !!(flags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
), args
,
4499 buffer
, size
* sizeof(WCHAR
), &retsize
);
4501 HeapFree( GetProcessHeap(), 0, message
);
4503 if (status
== STATUS_BUFFER_OVERFLOW
)
4505 if (size
) buffer
[size
- 1] = 0;
4506 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
4509 if (!set_ntstatus( status
)) return 0;
4510 if (retsize
<= sizeof(WCHAR
)) SetLastError( ERROR_NO_WORK_DONE
);
4511 return retsize
/ sizeof(WCHAR
) - 1;
4515 /******************************************************************************
4516 * GetACP (kernelbase.@)
4518 UINT WINAPI
GetACP(void)
4520 return nls_info
.AnsiTableInfo
.CodePage
;
4524 /***********************************************************************
4525 * GetCPInfo (kernelbase.@)
4527 BOOL WINAPI DECLSPEC_HOTPATCH
GetCPInfo( UINT codepage
, CPINFO
*cpinfo
)
4529 const CPTABLEINFO
*table
;
4533 SetLastError( ERROR_INVALID_PARAMETER
);
4540 cpinfo
->DefaultChar
[0] = 0x3f;
4541 cpinfo
->DefaultChar
[1] = 0;
4542 memset( cpinfo
->LeadByte
, 0, sizeof(cpinfo
->LeadByte
) );
4543 cpinfo
->MaxCharSize
= (codepage
== CP_UTF7
) ? 5 : 4;
4546 if (!(table
= get_codepage_table( codepage
))) return FALSE
;
4547 cpinfo
->MaxCharSize
= table
->MaximumCharacterSize
;
4548 memcpy( cpinfo
->DefaultChar
, &table
->DefaultChar
, sizeof(cpinfo
->DefaultChar
) );
4549 memcpy( cpinfo
->LeadByte
, table
->LeadByte
, sizeof(cpinfo
->LeadByte
) );
4556 /***********************************************************************
4557 * GetCPInfoExW (kernelbase.@)
4559 BOOL WINAPI
GetCPInfoExW( UINT codepage
, DWORD flags
, CPINFOEXW
*cpinfo
)
4561 const CPTABLEINFO
*table
;
4566 SetLastError( ERROR_INVALID_PARAMETER
);
4572 cpinfo
->DefaultChar
[0] = 0x3f;
4573 cpinfo
->DefaultChar
[1] = 0;
4574 cpinfo
->LeadByte
[0] = cpinfo
->LeadByte
[1] = 0;
4575 cpinfo
->MaxCharSize
= 5;
4576 cpinfo
->CodePage
= CP_UTF7
;
4577 cpinfo
->UnicodeDefaultChar
= 0x3f;
4580 cpinfo
->DefaultChar
[0] = 0x3f;
4581 cpinfo
->DefaultChar
[1] = 0;
4582 cpinfo
->LeadByte
[0] = cpinfo
->LeadByte
[1] = 0;
4583 cpinfo
->MaxCharSize
= 4;
4584 cpinfo
->CodePage
= CP_UTF8
;
4585 cpinfo
->UnicodeDefaultChar
= 0x3f;
4588 if (!(table
= get_codepage_table( codepage
))) return FALSE
;
4589 cpinfo
->MaxCharSize
= table
->MaximumCharacterSize
;
4590 memcpy( cpinfo
->DefaultChar
, &table
->DefaultChar
, sizeof(cpinfo
->DefaultChar
) );
4591 memcpy( cpinfo
->LeadByte
, table
->LeadByte
, sizeof(cpinfo
->LeadByte
) );
4592 cpinfo
->CodePage
= table
->CodePage
;
4593 cpinfo
->UnicodeDefaultChar
= table
->UniDefaultChar
;
4598 max
= ARRAY_SIZE(codepage_names
) - 1;
4599 cpinfo
->CodePageName
[0] = 0;
4602 pos
= (min
+ max
) / 2;
4603 if (codepage_names
[pos
].cp
< cpinfo
->CodePage
) min
= pos
+ 1;
4604 else if (codepage_names
[pos
].cp
> cpinfo
->CodePage
) max
= pos
- 1;
4607 wcscpy( cpinfo
->CodePageName
, codepage_names
[pos
].name
);
4615 /***********************************************************************
4616 * GetCalendarInfoW (kernelbase.@)
4618 INT WINAPI DECLSPEC_HOTPATCH
GetCalendarInfoW( LCID lcid
, CALID calendar
, CALTYPE type
,
4619 WCHAR
*data
, INT count
, DWORD
*value
)
4621 static const LCTYPE lctype_map
[] =
4624 0, /* CAL_ICALINTVALUE */
4625 0, /* CAL_SCALNAME */
4626 0, /* CAL_IYEAROFFSETRANGE */
4627 0, /* CAL_SERASTRING */
4637 LOCALE_SABBREVDAYNAME1
,
4638 LOCALE_SABBREVDAYNAME2
,
4639 LOCALE_SABBREVDAYNAME3
,
4640 LOCALE_SABBREVDAYNAME4
,
4641 LOCALE_SABBREVDAYNAME5
,
4642 LOCALE_SABBREVDAYNAME6
,
4643 LOCALE_SABBREVDAYNAME7
,
4653 LOCALE_SMONTHNAME10
,
4654 LOCALE_SMONTHNAME11
,
4655 LOCALE_SMONTHNAME12
,
4656 LOCALE_SMONTHNAME13
,
4657 LOCALE_SABBREVMONTHNAME1
,
4658 LOCALE_SABBREVMONTHNAME2
,
4659 LOCALE_SABBREVMONTHNAME3
,
4660 LOCALE_SABBREVMONTHNAME4
,
4661 LOCALE_SABBREVMONTHNAME5
,
4662 LOCALE_SABBREVMONTHNAME6
,
4663 LOCALE_SABBREVMONTHNAME7
,
4664 LOCALE_SABBREVMONTHNAME8
,
4665 LOCALE_SABBREVMONTHNAME9
,
4666 LOCALE_SABBREVMONTHNAME10
,
4667 LOCALE_SABBREVMONTHNAME11
,
4668 LOCALE_SABBREVMONTHNAME12
,
4669 LOCALE_SABBREVMONTHNAME13
,
4671 0, /* CAL_ITWODIGITYEARMAX */
4672 LOCALE_SSHORTESTDAYNAME1
,
4673 LOCALE_SSHORTESTDAYNAME2
,
4674 LOCALE_SSHORTESTDAYNAME3
,
4675 LOCALE_SSHORTESTDAYNAME4
,
4676 LOCALE_SSHORTESTDAYNAME5
,
4677 LOCALE_SSHORTESTDAYNAME6
,
4678 LOCALE_SSHORTESTDAYNAME7
,
4680 0, /* CAL_SABBREVERASTRING */
4683 CALTYPE calinfo
= type
& 0xffff;
4685 if (type
& CAL_NOUSEROVERRIDE
) FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
4686 if (type
& CAL_USE_CP_ACP
) FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
4688 if ((type
& CAL_RETURN_NUMBER
) && !value
)
4690 SetLastError( ERROR_INVALID_PARAMETER
);
4694 if (type
& CAL_RETURN_GENITIVE_NAMES
) flags
|= LOCALE_RETURN_GENITIVE_NAMES
;
4698 case CAL_ICALINTVALUE
:
4699 if (type
& CAL_RETURN_NUMBER
)
4700 return GetLocaleInfoW( lcid
, LOCALE_RETURN_NUMBER
| LOCALE_ICALENDARTYPE
,
4701 (WCHAR
*)value
, sizeof(*value
) / sizeof(WCHAR
) );
4702 return GetLocaleInfoW( lcid
, LOCALE_ICALENDARTYPE
, data
, count
);
4705 FIXME( "Unimplemented caltype %ld\n", calinfo
);
4706 if (data
) *data
= 0;
4709 case CAL_IYEAROFFSETRANGE
:
4710 case CAL_SERASTRING
:
4711 case CAL_SABBREVERASTRING
:
4712 FIXME( "Unimplemented caltype %ld\n", calinfo
);
4715 case CAL_SSHORTDATE
:
4724 case CAL_SABBREVDAYNAME1
:
4725 case CAL_SABBREVDAYNAME2
:
4726 case CAL_SABBREVDAYNAME3
:
4727 case CAL_SABBREVDAYNAME4
:
4728 case CAL_SABBREVDAYNAME5
:
4729 case CAL_SABBREVDAYNAME6
:
4730 case CAL_SABBREVDAYNAME7
:
4731 case CAL_SMONTHNAME1
:
4732 case CAL_SMONTHNAME2
:
4733 case CAL_SMONTHNAME3
:
4734 case CAL_SMONTHNAME4
:
4735 case CAL_SMONTHNAME5
:
4736 case CAL_SMONTHNAME6
:
4737 case CAL_SMONTHNAME7
:
4738 case CAL_SMONTHNAME8
:
4739 case CAL_SMONTHNAME9
:
4740 case CAL_SMONTHNAME10
:
4741 case CAL_SMONTHNAME11
:
4742 case CAL_SMONTHNAME12
:
4743 case CAL_SMONTHNAME13
:
4744 case CAL_SABBREVMONTHNAME1
:
4745 case CAL_SABBREVMONTHNAME2
:
4746 case CAL_SABBREVMONTHNAME3
:
4747 case CAL_SABBREVMONTHNAME4
:
4748 case CAL_SABBREVMONTHNAME5
:
4749 case CAL_SABBREVMONTHNAME6
:
4750 case CAL_SABBREVMONTHNAME7
:
4751 case CAL_SABBREVMONTHNAME8
:
4752 case CAL_SABBREVMONTHNAME9
:
4753 case CAL_SABBREVMONTHNAME10
:
4754 case CAL_SABBREVMONTHNAME11
:
4755 case CAL_SABBREVMONTHNAME12
:
4756 case CAL_SABBREVMONTHNAME13
:
4758 case CAL_SYEARMONTH
:
4759 case CAL_SSHORTESTDAYNAME1
:
4760 case CAL_SSHORTESTDAYNAME2
:
4761 case CAL_SSHORTESTDAYNAME3
:
4762 case CAL_SSHORTESTDAYNAME4
:
4763 case CAL_SSHORTESTDAYNAME5
:
4764 case CAL_SSHORTESTDAYNAME6
:
4765 case CAL_SSHORTESTDAYNAME7
:
4766 return GetLocaleInfoW( lcid
, lctype_map
[calinfo
] | flags
, data
, count
);
4768 case CAL_ITWODIGITYEARMAX
:
4769 if (type
& CAL_RETURN_NUMBER
)
4771 *value
= CALINFO_MAX_YEAR
;
4772 return sizeof(DWORD
) / sizeof(WCHAR
);
4777 int ret
= swprintf( buffer
, ARRAY_SIZE(buffer
), L
"%u", CALINFO_MAX_YEAR
) + 1;
4778 if (!data
) return ret
;
4781 lstrcpyW( data
, buffer
);
4784 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
4789 FIXME( "Unknown caltype %ld\n", calinfo
);
4790 SetLastError( ERROR_INVALID_FLAGS
);
4797 /***********************************************************************
4798 * GetCalendarInfoEx (kernelbase.@)
4800 INT WINAPI DECLSPEC_HOTPATCH
GetCalendarInfoEx( const WCHAR
*locale
, CALID calendar
, const WCHAR
*reserved
,
4801 CALTYPE type
, WCHAR
*data
, INT count
, DWORD
*value
)
4803 LCID lcid
= LocaleNameToLCID( locale
, 0 );
4804 return GetCalendarInfoW( lcid
, calendar
, type
, data
, count
, value
);
4807 static CRITICAL_SECTION tzname_section
;
4808 static CRITICAL_SECTION_DEBUG tzname_section_debug
=
4810 0, 0, &tzname_section
,
4811 { &tzname_section_debug
.ProcessLocksList
, &tzname_section_debug
.ProcessLocksList
},
4812 0, 0, { (DWORD_PTR
)(__FILE__
": tzname_section") }
4814 static CRITICAL_SECTION tzname_section
= { &tzname_section_debug
, -1, 0, 0, 0, 0 };
4817 WCHAR key_name
[128];
4818 WCHAR standard_name
[32];
4819 WCHAR daylight_name
[32];
4822 /***********************************************************************
4823 * GetDynamicTimeZoneInformation (kernelbase.@)
4825 DWORD WINAPI DECLSPEC_HOTPATCH
GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION
*info
)
4830 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION
*)info
)))
4831 return TIME_ZONE_ID_INVALID
;
4833 RtlEnterCriticalSection( &tzname_section
);
4834 if (cached_tzname
.lcid
== GetThreadLocale() &&
4835 !wcscmp( info
->TimeZoneKeyName
, cached_tzname
.key_name
))
4837 wcscpy( info
->StandardName
, cached_tzname
.standard_name
);
4838 wcscpy( info
->DaylightName
, cached_tzname
.daylight_name
);
4839 RtlLeaveCriticalSection( &tzname_section
);
4843 RtlLeaveCriticalSection( &tzname_section
);
4844 if (!RegOpenKeyExW( tz_key
, info
->TimeZoneKeyName
, 0, KEY_ALL_ACCESS
, &key
))
4846 RegLoadMUIStringW( key
, L
"MUI_Std", info
->StandardName
,
4847 sizeof(info
->StandardName
), NULL
, 0, system_dir
);
4848 RegLoadMUIStringW( key
, L
"MUI_Dlt", info
->DaylightName
,
4849 sizeof(info
->DaylightName
), NULL
, 0, system_dir
);
4852 else return TIME_ZONE_ID_INVALID
;
4854 RtlEnterCriticalSection( &tzname_section
);
4855 cached_tzname
.lcid
= GetThreadLocale();
4856 wcscpy( cached_tzname
.key_name
, info
->TimeZoneKeyName
);
4857 wcscpy( cached_tzname
.standard_name
, info
->StandardName
);
4858 wcscpy( cached_tzname
.daylight_name
, info
->DaylightName
);
4859 RtlLeaveCriticalSection( &tzname_section
);
4862 NtQuerySystemTime( &now
);
4863 return get_timezone_id( (TIME_ZONE_INFORMATION
*)info
, now
, FALSE
);
4867 /******************************************************************************
4868 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
4870 DWORD WINAPI DECLSPEC_HOTPATCH
GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION
*info
,
4871 DWORD
*first
, DWORD
*last
)
4873 HKEY key
, dst_key
= 0;
4874 DWORD type
, count
, ret
= ERROR_FILE_NOT_FOUND
;
4876 if (RegOpenKeyExW( tz_key
, info
->TimeZoneKeyName
, 0, KEY_ALL_ACCESS
, &key
)) return ret
;
4878 if (RegOpenKeyExW( key
, L
"Dynamic DST", 0, KEY_ALL_ACCESS
, &dst_key
)) goto done
;
4879 count
= sizeof(DWORD
);
4880 if (RegQueryValueExW( dst_key
, L
"FirstEntry", NULL
, &type
, (BYTE
*)first
, &count
)) goto done
;
4881 if (type
!= REG_DWORD
) goto done
;
4882 count
= sizeof(DWORD
);
4883 if (RegQueryValueExW( dst_key
, L
"LastEntry", NULL
, &type
, (BYTE
*)last
, &count
)) goto done
;
4884 if (type
!= REG_DWORD
) goto done
;
4888 RegCloseKey( dst_key
);
4894 /******************************************************************************
4895 * GetFileMUIInfo (kernelbase.@)
4897 BOOL WINAPI
/* DECLSPEC_HOTPATCH */ GetFileMUIInfo( DWORD flags
, const WCHAR
*path
,
4898 FILEMUIINFO
*info
, DWORD
*size
)
4900 FIXME( "stub: %lu, %s, %p, %p\n", flags
, debugstr_w(path
), info
, size
);
4901 SetLastError( ERROR_CALL_NOT_IMPLEMENTED
);
4906 /******************************************************************************
4907 * GetFileMUIPath (kernelbase.@)
4909 BOOL WINAPI
/* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags
, const WCHAR
*filepath
,
4910 WCHAR
*language
, ULONG
*languagelen
,
4911 WCHAR
*muipath
, ULONG
*muipathlen
,
4912 ULONGLONG
*enumerator
)
4914 FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags
, debugstr_w(filepath
),
4915 debugstr_w(language
), languagelen
, muipath
, muipathlen
, enumerator
);
4916 SetLastError( ERROR_CALL_NOT_IMPLEMENTED
);
4921 /******************************************************************************
4922 * GetGeoInfoW (kernelbase.@)
4924 INT WINAPI DECLSPEC_HOTPATCH
GetGeoInfoW( GEOID id
, GEOTYPE type
, WCHAR
*data
, int count
, LANGID lang
)
4926 const struct geo_id
*ptr
= find_geo_id_entry( id
);
4928 TRACE( "%ld %ld %p %d %d\n", id
, type
, data
, count
, lang
);
4932 SetLastError( ERROR_INVALID_PARAMETER
);
4935 return get_geo_info( ptr
, type
, data
, count
, lang
);
4939 /******************************************************************************
4940 * GetLocaleInfoA (kernelbase.@)
4942 INT WINAPI DECLSPEC_HOTPATCH
GetLocaleInfoA( LCID lcid
, LCTYPE lctype
, char *buffer
, INT len
)
4947 TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid
, lctype
, buffer
, len
);
4949 if (len
< 0 || (len
&& !buffer
))
4951 SetLastError( ERROR_INVALID_PARAMETER
);
4954 if (LOWORD(lctype
) == LOCALE_SSHORTTIME
|| (lctype
& LOCALE_RETURN_GENITIVE_NAMES
))
4956 SetLastError( ERROR_INVALID_FLAGS
);
4960 if (LOWORD(lctype
) == LOCALE_FONTSIGNATURE
|| (lctype
& LOCALE_RETURN_NUMBER
))
4961 return GetLocaleInfoW( lcid
, lctype
, (WCHAR
*)buffer
, len
/ sizeof(WCHAR
) ) * sizeof(WCHAR
);
4963 if (!(lenW
= GetLocaleInfoW( lcid
, lctype
, NULL
, 0 ))) return 0;
4965 if (!(bufferW
= RtlAllocateHeap( GetProcessHeap(), 0, lenW
* sizeof(WCHAR
) )))
4967 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
4970 ret
= GetLocaleInfoW( lcid
, lctype
, bufferW
, lenW
);
4971 if (ret
) ret
= WideCharToMultiByte( get_lcid_codepage( lcid
, lctype
), 0,
4972 bufferW
, ret
, buffer
, len
, NULL
, NULL
);
4973 RtlFreeHeap( GetProcessHeap(), 0, bufferW
);
4978 /******************************************************************************
4979 * GetLocaleInfoW (kernelbase.@)
4981 INT WINAPI DECLSPEC_HOTPATCH
GetLocaleInfoW( LCID lcid
, LCTYPE lctype
, WCHAR
*buffer
, INT len
)
4983 const NLS_LOCALE_DATA
*locale
;
4985 if (len
< 0 || (len
&& !buffer
))
4987 SetLastError( ERROR_INVALID_PARAMETER
);
4991 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid
, lctype
, buffer
, len
);
4993 if (!(locale
= get_locale_by_id( &lcid
, 0 )))
4995 SetLastError( ERROR_INVALID_PARAMETER
);
4998 return get_locale_info( locale
, lcid
, lctype
, buffer
, len
);
5002 /******************************************************************************
5003 * GetLocaleInfoEx (kernelbase.@)
5005 INT WINAPI DECLSPEC_HOTPATCH
GetLocaleInfoEx( const WCHAR
*name
, LCTYPE info
, WCHAR
*buffer
, INT len
)
5008 const NLS_LOCALE_DATA
*locale
= get_locale_by_name( name
, &lcid
);
5010 TRACE( "%s 0x%lx %p %d\n", debugstr_w(name
), info
, buffer
, len
);
5014 SetLastError( ERROR_INVALID_PARAMETER
);
5017 return get_locale_info( locale
, lcid
, info
, buffer
, len
);
5021 /******************************************************************************
5022 * GetNLSVersion (kernelbase.@)
5024 BOOL WINAPI DECLSPEC_HOTPATCH
GetNLSVersion( NLS_FUNCTION func
, LCID lcid
, NLSVERSIONINFO
*info
)
5026 WCHAR locale
[LOCALE_NAME_MAX_LENGTH
];
5028 if (info
->dwNLSVersionInfoSize
< offsetof( NLSVERSIONINFO
, dwEffectiveId
))
5030 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
5033 if (!LCIDToLocaleName( lcid
, locale
, LOCALE_NAME_MAX_LENGTH
, LOCALE_ALLOW_NEUTRAL_NAMES
))
5035 SetLastError( ERROR_INVALID_PARAMETER
);
5038 return GetNLSVersionEx( func
, locale
, (NLSVERSIONINFOEX
*)info
);
5042 /******************************************************************************
5043 * GetNLSVersionEx (kernelbase.@)
5045 BOOL WINAPI DECLSPEC_HOTPATCH
GetNLSVersionEx( NLS_FUNCTION func
, const WCHAR
*locale
,
5046 NLSVERSIONINFOEX
*info
)
5050 if (func
!= COMPARE_STRING
)
5052 SetLastError( ERROR_INVALID_FLAGS
);
5055 if (info
->dwNLSVersionInfoSize
< sizeof(*info
) &&
5056 (info
->dwNLSVersionInfoSize
!= offsetof( NLSVERSIONINFO
, dwEffectiveId
)))
5058 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
5062 if (!(lcid
= LocaleNameToLCID( locale
, 0 ))) return FALSE
;
5064 info
->dwNLSVersion
= info
->dwDefinedVersion
= sort
.version
;
5065 if (info
->dwNLSVersionInfoSize
>= sizeof(*info
))
5067 const struct sortguid
*sortid
= get_language_sort( locale
);
5068 info
->dwEffectiveId
= lcid
;
5069 info
->guidCustomVersion
= sortid
? sortid
->id
: default_sort_guid
;
5075 /******************************************************************************
5076 * GetOEMCP (kernelbase.@)
5078 UINT WINAPI
GetOEMCP(void)
5080 return nls_info
.OemTableInfo
.CodePage
;
5084 /***********************************************************************
5085 * GetProcessPreferredUILanguages (kernelbase.@)
5087 BOOL WINAPI DECLSPEC_HOTPATCH
GetProcessPreferredUILanguages( DWORD flags
, ULONG
*count
,
5088 WCHAR
*buffer
, ULONG
*size
)
5090 return set_ntstatus( RtlGetProcessPreferredUILanguages( flags
, count
, buffer
, size
));
5094 /***********************************************************************
5095 * GetStringTypeA (kernelbase.@)
5097 BOOL WINAPI DECLSPEC_HOTPATCH
GetStringTypeA( LCID locale
, DWORD type
, const char *src
, int count
,
5105 if (count
== -1) count
= strlen(src
) + 1;
5107 cp
= get_lcid_codepage( locale
, 0 );
5108 countW
= MultiByteToWideChar(cp
, 0, src
, count
, NULL
, 0);
5109 if((srcW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
5111 MultiByteToWideChar(cp
, 0, src
, count
, srcW
, countW
);
5113 * NOTE: the target buffer has 1 word for each CHARACTER in the source
5114 * string, with multibyte characters there maybe be more bytes in count
5115 * than character space in the buffer!
5117 ret
= GetStringTypeW(type
, srcW
, countW
, chartype
);
5118 HeapFree(GetProcessHeap(), 0, srcW
);
5124 /***********************************************************************
5125 * GetStringTypeW (kernelbase.@)
5127 BOOL WINAPI DECLSPEC_HOTPATCH
GetStringTypeW( DWORD type
, const WCHAR
*src
, INT count
, WORD
*chartype
)
5131 SetLastError( ERROR_INVALID_PARAMETER
);
5134 if (type
!= CT_CTYPE1
&& type
!= CT_CTYPE2
&& type
!= CT_CTYPE3
)
5136 SetLastError( ERROR_INVALID_PARAMETER
);
5140 if (count
== -1) count
= lstrlenW(src
) + 1;
5142 while (count
--) *chartype
++ = get_char_type( type
, *src
++ );
5148 /***********************************************************************
5149 * GetStringTypeExW (kernelbase.@)
5151 BOOL WINAPI DECLSPEC_HOTPATCH
GetStringTypeExW( LCID locale
, DWORD type
, const WCHAR
*src
, int count
,
5154 /* locale is ignored for Unicode */
5155 return GetStringTypeW( type
, src
, count
, chartype
);
5159 /***********************************************************************
5160 * GetSystemDefaultLCID (kernelbase.@)
5162 LCID WINAPI DECLSPEC_HOTPATCH
GetSystemDefaultLCID(void)
5168 /***********************************************************************
5169 * GetSystemDefaultLangID (kernelbase.@)
5171 LANGID WINAPI DECLSPEC_HOTPATCH
GetSystemDefaultLangID(void)
5173 return LANGIDFROMLCID( GetSystemDefaultLCID() );
5177 /***********************************************************************
5178 * GetSystemDefaultLocaleName (kernelbase.@)
5180 INT WINAPI DECLSPEC_HOTPATCH
GetSystemDefaultLocaleName( LPWSTR name
, INT count
)
5182 return get_locale_info( system_locale
, system_lcid
, LOCALE_SNAME
, name
, count
);
5186 /***********************************************************************
5187 * GetSystemDefaultUILanguage (kernelbase.@)
5189 LANGID WINAPI DECLSPEC_HOTPATCH
GetSystemDefaultUILanguage(void)
5192 NtQueryInstallUILanguage( &lang
);
5197 /***********************************************************************
5198 * GetSystemPreferredUILanguages (kernelbase.@)
5200 BOOL WINAPI DECLSPEC_HOTPATCH
GetSystemPreferredUILanguages( DWORD flags
, ULONG
*count
,
5201 WCHAR
*buffer
, ULONG
*size
)
5203 return set_ntstatus( RtlGetSystemPreferredUILanguages( flags
, 0, count
, buffer
, size
));
5207 /***********************************************************************
5208 * GetThreadPreferredUILanguages (kernelbase.@)
5210 BOOL WINAPI DECLSPEC_HOTPATCH
GetThreadPreferredUILanguages( DWORD flags
, ULONG
*count
,
5211 WCHAR
*buffer
, ULONG
*size
)
5213 return set_ntstatus( RtlGetThreadPreferredUILanguages( flags
, count
, buffer
, size
));
5217 /***********************************************************************
5218 * GetTimeZoneInformation (kernelbase.@)
5220 DWORD WINAPI DECLSPEC_HOTPATCH
GetTimeZoneInformation( TIME_ZONE_INFORMATION
*info
)
5222 DYNAMIC_TIME_ZONE_INFORMATION tzinfo
;
5223 DWORD ret
= GetDynamicTimeZoneInformation( &tzinfo
);
5225 memcpy( info
, &tzinfo
, sizeof(*info
) );
5230 /***********************************************************************
5231 * GetTimeZoneInformationForYear (kernelbase.@)
5233 BOOL WINAPI DECLSPEC_HOTPATCH
GetTimeZoneInformationForYear( USHORT year
,
5234 DYNAMIC_TIME_ZONE_INFORMATION
*dynamic
,
5235 TIME_ZONE_INFORMATION
*info
)
5237 DYNAMIC_TIME_ZONE_INFORMATION local_info
;
5238 HKEY key
= 0, dst_key
;
5246 SYSTEMTIME std_date
;
5247 SYSTEMTIME dlt_date
;
5250 TRACE( "(%u,%p)\n", year
, info
);
5254 if (GetDynamicTimeZoneInformation( &local_info
) == TIME_ZONE_ID_INVALID
) return FALSE
;
5255 dynamic
= &local_info
;
5258 if ((ret
= RegOpenKeyExW( tz_key
, dynamic
->TimeZoneKeyName
, 0, KEY_ALL_ACCESS
, &key
))) goto done
;
5259 if (RegLoadMUIStringW( key
, L
"MUI_Std", info
->StandardName
,
5260 sizeof(info
->StandardName
), NULL
, 0, system_dir
))
5262 count
= sizeof(info
->StandardName
);
5263 if ((ret
= RegQueryValueExW( key
, L
"Std", NULL
, NULL
, (BYTE
*)info
->StandardName
, &count
)))
5266 if (RegLoadMUIStringW( key
, L
"MUI_Dlt", info
->DaylightName
,
5267 sizeof(info
->DaylightName
), NULL
, 0, system_dir
))
5269 count
= sizeof(info
->DaylightName
);
5270 if ((ret
= RegQueryValueExW( key
, L
"Dlt", NULL
, NULL
, (BYTE
*)info
->DaylightName
, &count
)))
5274 ret
= ERROR_FILE_NOT_FOUND
;
5275 if (!dynamic
->DynamicDaylightTimeDisabled
&&
5276 !RegOpenKeyExW( key
, L
"Dynamic DST", 0, KEY_ALL_ACCESS
, &dst_key
))
5279 swprintf( yearW
, ARRAY_SIZE(yearW
), L
"%u", year
);
5280 count
= sizeof(data
);
5281 ret
= RegQueryValueExW( dst_key
, yearW
, NULL
, NULL
, (BYTE
*)&data
, &count
);
5282 RegCloseKey( dst_key
);
5286 count
= sizeof(data
);
5287 ret
= RegQueryValueExW( key
, L
"TZI", NULL
, NULL
, (BYTE
*)&data
, &count
);
5292 info
->Bias
= data
.bias
;
5293 info
->StandardBias
= data
.std_bias
;
5294 info
->DaylightBias
= data
.dlt_bias
;
5295 info
->StandardDate
= data
.std_date
;
5296 info
->DaylightDate
= data
.dlt_date
;
5301 if (ret
) SetLastError( ret
);
5306 /***********************************************************************
5307 * GetUserDefaultLCID (kernelbase.@)
5309 LCID WINAPI DECLSPEC_HOTPATCH
GetUserDefaultLCID(void)
5315 /***********************************************************************
5316 * GetUserDefaultLangID (kernelbase.@)
5318 LANGID WINAPI DECLSPEC_HOTPATCH
GetUserDefaultLangID(void)
5320 return LANGIDFROMLCID( GetUserDefaultLCID() );
5324 /***********************************************************************
5325 * GetUserDefaultLocaleName (kernelbase.@)
5327 INT WINAPI DECLSPEC_HOTPATCH
GetUserDefaultLocaleName( LPWSTR name
, INT len
)
5329 return get_locale_info( user_locale
, user_lcid
, LOCALE_SNAME
, name
, len
);
5333 /***********************************************************************
5334 * GetUserDefaultUILanguage (kernelbase.@)
5336 LANGID WINAPI DECLSPEC_HOTPATCH
GetUserDefaultUILanguage(void)
5339 NtQueryDefaultUILanguage( &lang
);
5344 /******************************************************************************
5345 * GetUserGeoID (kernelbase.@)
5347 GEOID WINAPI DECLSPEC_HOTPATCH
GetUserGeoID( GEOCLASS geoclass
)
5356 case GEOCLASS_NATION
:
5359 case GEOCLASS_REGION
:
5363 WARN("Unknown geoclass %ld\n", geoclass
);
5364 return GEOID_NOT_AVAILABLE
;
5366 if (!RegOpenKeyExW( intl_key
, L
"Geo", 0, KEY_ALL_ACCESS
, &hkey
))
5368 DWORD count
= sizeof(bufferW
);
5369 if (!RegQueryValueExW( hkey
, name
, NULL
, NULL
, (BYTE
*)bufferW
, &count
))
5370 ret
= wcstol( bufferW
, NULL
, 10 );
5371 RegCloseKey( hkey
);
5377 /******************************************************************************
5378 * GetUserPreferredUILanguages (kernelbase.@)
5380 BOOL WINAPI DECLSPEC_HOTPATCH
GetUserPreferredUILanguages( DWORD flags
, ULONG
*count
,
5381 WCHAR
*buffer
, ULONG
*size
)
5383 return set_ntstatus( RtlGetUserPreferredUILanguages( flags
, 0, count
, buffer
, size
));
5387 /******************************************************************************
5388 * IdnToAscii (kernelbase.@)
5390 INT WINAPI DECLSPEC_HOTPATCH
IdnToAscii( DWORD flags
, const WCHAR
*src
, INT srclen
,
5391 WCHAR
*dst
, INT dstlen
)
5393 NTSTATUS status
= RtlIdnToAscii( flags
, src
, srclen
, dst
, &dstlen
);
5394 if (!set_ntstatus( status
)) return 0;
5399 /******************************************************************************
5400 * IdnToNameprepUnicode (kernelbase.@)
5402 INT WINAPI DECLSPEC_HOTPATCH
IdnToNameprepUnicode( DWORD flags
, const WCHAR
*src
, INT srclen
,
5403 WCHAR
*dst
, INT dstlen
)
5405 NTSTATUS status
= RtlIdnToNameprepUnicode( flags
, src
, srclen
, dst
, &dstlen
);
5406 if (!set_ntstatus( status
)) return 0;
5411 /******************************************************************************
5412 * IdnToUnicode (kernelbase.@)
5414 INT WINAPI DECLSPEC_HOTPATCH
IdnToUnicode( DWORD flags
, const WCHAR
*src
, INT srclen
,
5415 WCHAR
*dst
, INT dstlen
)
5417 NTSTATUS status
= RtlIdnToUnicode( flags
, src
, srclen
, dst
, &dstlen
);
5418 if (!set_ntstatus( status
)) return 0;
5423 /******************************************************************************
5424 * IsCharAlphaA (kernelbase.@)
5426 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharAlphaA( CHAR c
)
5428 WCHAR wc
= nls_info
.AnsiTableInfo
.MultiByteTable
[(unsigned char)c
];
5429 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_ALPHA
);
5433 /******************************************************************************
5434 * IsCharAlphaW (kernelbase.@)
5436 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharAlphaW( WCHAR wc
)
5438 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_ALPHA
);
5442 /******************************************************************************
5443 * IsCharAlphaNumericA (kernelbase.@)
5445 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharAlphaNumericA( CHAR c
)
5447 WCHAR wc
= nls_info
.AnsiTableInfo
.MultiByteTable
[(unsigned char)c
];
5448 return !!(get_char_type( CT_CTYPE1
, wc
) & (C1_ALPHA
| C1_DIGIT
));
5452 /******************************************************************************
5453 * IsCharAlphaNumericW (kernelbase.@)
5455 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharAlphaNumericW( WCHAR wc
)
5457 return !!(get_char_type( CT_CTYPE1
, wc
) & (C1_ALPHA
| C1_DIGIT
));
5461 /******************************************************************************
5462 * IsCharBlankW (kernelbase.@)
5464 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharBlankW( WCHAR wc
)
5466 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_BLANK
);
5470 /******************************************************************************
5471 * IsCharCntrlW (kernelbase.@)
5473 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharCntrlW( WCHAR wc
)
5475 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_CNTRL
);
5479 /******************************************************************************
5480 * IsCharDigitW (kernelbase.@)
5482 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharDigitW( WCHAR wc
)
5484 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_DIGIT
);
5488 /******************************************************************************
5489 * IsCharLowerA (kernelbase.@)
5491 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharLowerA( CHAR c
)
5493 WCHAR wc
= nls_info
.AnsiTableInfo
.MultiByteTable
[(unsigned char)c
];
5494 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_LOWER
);
5498 /******************************************************************************
5499 * IsCharLowerW (kernelbase.@)
5501 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharLowerW( WCHAR wc
)
5503 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_LOWER
);
5507 /******************************************************************************
5508 * IsCharPunctW (kernelbase.@)
5510 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharPunctW( WCHAR wc
)
5512 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_PUNCT
);
5516 /******************************************************************************
5517 * IsCharSpaceA (kernelbase.@)
5519 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharSpaceA( CHAR c
)
5521 WCHAR wc
= nls_info
.AnsiTableInfo
.MultiByteTable
[(unsigned char)c
];
5522 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_SPACE
);
5526 /******************************************************************************
5527 * IsCharSpaceW (kernelbase.@)
5529 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharSpaceW( WCHAR wc
)
5531 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_SPACE
);
5535 /******************************************************************************
5536 * IsCharUpperA (kernelbase.@)
5538 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharUpperA( CHAR c
)
5540 WCHAR wc
= nls_info
.AnsiTableInfo
.MultiByteTable
[(unsigned char)c
];
5541 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_UPPER
);
5545 /******************************************************************************
5546 * IsCharUpperW (kernelbase.@)
5548 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharUpperW( WCHAR wc
)
5550 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_UPPER
);
5554 /******************************************************************************
5555 * IsCharXDigitW (kernelbase.@)
5557 BOOL WINAPI DECLSPEC_HOTPATCH
IsCharXDigitW( WCHAR wc
)
5559 return !!(get_char_type( CT_CTYPE1
, wc
) & C1_XDIGIT
);
5563 /******************************************************************************
5564 * IsDBCSLeadByte (kernelbase.@)
5566 BOOL WINAPI DECLSPEC_HOTPATCH
IsDBCSLeadByte( BYTE testchar
)
5568 return nls_info
.AnsiTableInfo
.DBCSCodePage
&& nls_info
.AnsiTableInfo
.DBCSOffsets
[testchar
];
5572 /******************************************************************************
5573 * IsDBCSLeadByteEx (kernelbase.@)
5575 BOOL WINAPI DECLSPEC_HOTPATCH
IsDBCSLeadByteEx( UINT codepage
, BYTE testchar
)
5577 const CPTABLEINFO
*table
= get_codepage_table( codepage
);
5578 return table
&& table
->DBCSCodePage
&& table
->DBCSOffsets
[testchar
];
5582 /******************************************************************************
5583 * IsNormalizedString (kernelbase.@)
5585 BOOL WINAPI DECLSPEC_HOTPATCH
IsNormalizedString( NORM_FORM form
, const WCHAR
*str
, INT len
)
5588 if (!set_ntstatus( RtlIsNormalizedString( form
, str
, len
, &res
))) res
= FALSE
;
5593 /******************************************************************************
5594 * IsValidCodePage (kernelbase.@)
5596 BOOL WINAPI DECLSPEC_HOTPATCH
IsValidCodePage( UINT codepage
)
5609 return get_codepage_table( codepage
) != NULL
;
5614 /******************************************************************************
5615 * IsValidLanguageGroup (kernelbase.@)
5617 BOOL WINAPI DECLSPEC_HOTPATCH
IsValidLanguageGroup( LGRPID id
, DWORD flags
)
5619 WCHAR name
[10], value
[10];
5620 DWORD type
, value_len
= sizeof(value
);
5624 if (RegOpenKeyExW( nls_key
, L
"Language Groups", 0, KEY_READ
, &key
)) return FALSE
;
5626 swprintf( name
, ARRAY_SIZE(name
), L
"%x", id
);
5627 if (!RegQueryValueExW( key
, name
, NULL
, &type
, (BYTE
*)value
, &value_len
) && type
== REG_SZ
)
5628 ret
= (flags
& LGRPID_SUPPORTED
) || wcstoul( value
, NULL
, 10 );
5635 /******************************************************************************
5636 * IsValidLocale (kernelbase.@)
5638 BOOL WINAPI DECLSPEC_HOTPATCH
IsValidLocale( LCID lcid
, DWORD flags
)
5640 return !!get_locale_by_id( &lcid
, LOCALE_ALLOW_NEUTRAL_NAMES
);
5644 /******************************************************************************
5645 * IsValidLocaleName (kernelbase.@)
5647 BOOL WINAPI DECLSPEC_HOTPATCH
IsValidLocaleName( const WCHAR
*locale
)
5649 if (locale
== LOCALE_NAME_USER_DEFAULT
) return FALSE
;
5650 return !!find_lcname_entry( locale
);
5654 /******************************************************************************
5655 * IsValidNLSVersion (kernelbase.@)
5657 DWORD WINAPI DECLSPEC_HOTPATCH
IsValidNLSVersion( NLS_FUNCTION func
, const WCHAR
*locale
,
5658 NLSVERSIONINFOEX
*info
)
5660 static const GUID GUID_NULL
;
5661 NLSVERSIONINFOEX infoex
;
5664 if (func
!= COMPARE_STRING
)
5666 SetLastError( ERROR_INVALID_PARAMETER
);
5669 if (info
->dwNLSVersionInfoSize
< sizeof(*info
) &&
5670 (info
->dwNLSVersionInfoSize
!= offsetof( NLSVERSIONINFO
, dwEffectiveId
)))
5672 SetLastError( ERROR_INVALID_PARAMETER
);
5675 infoex
.dwNLSVersionInfoSize
= sizeof(infoex
);
5676 if (!GetNLSVersionEx( func
, locale
, &infoex
)) return FALSE
;
5678 ret
= (infoex
.dwNLSVersion
& ~0xff) == (info
->dwNLSVersion
& ~0xff);
5679 if (ret
&& !IsEqualGUID( &info
->guidCustomVersion
, &GUID_NULL
))
5680 ret
= find_sortguid( &info
->guidCustomVersion
) != NULL
;
5682 if (!ret
) SetLastError( ERROR_SUCCESS
);
5687 /***********************************************************************
5688 * LCIDToLocaleName (kernelbase.@)
5690 INT WINAPI DECLSPEC_HOTPATCH
LCIDToLocaleName( LCID lcid
, WCHAR
*name
, INT count
, DWORD flags
)
5692 const NLS_LOCALE_DATA
*locale
= get_locale_by_id( &lcid
, flags
);
5696 SetLastError( ERROR_INVALID_PARAMETER
);
5699 return get_locale_info( locale
, lcid
, LOCALE_SNAME
, name
, count
);
5703 /***********************************************************************
5704 * LCMapStringEx (kernelbase.@)
5706 INT WINAPI DECLSPEC_HOTPATCH
LCMapStringEx( const WCHAR
*locale
, DWORD flags
, const WCHAR
*src
, int srclen
,
5707 WCHAR
*dst
, int dstlen
, NLSVERSIONINFO
*version
,
5708 void *reserved
, LPARAM handle
)
5710 const struct sortguid
*sortid
= NULL
;
5714 if (version
) FIXME( "unsupported version structure %p\n", version
);
5715 if (reserved
) FIXME( "unsupported reserved pointer %p\n", reserved
);
5719 if (!once
++) FIXME( "unsupported lparam %Ix\n", handle
);
5722 if (!src
|| !srclen
|| dstlen
< 0)
5724 SetLastError( ERROR_INVALID_PARAMETER
);
5728 /* mutually exclusive flags */
5729 if ((flags
& (LCMAP_LOWERCASE
| LCMAP_UPPERCASE
)) == (LCMAP_LOWERCASE
| LCMAP_UPPERCASE
) ||
5730 (flags
& (LCMAP_HIRAGANA
| LCMAP_KATAKANA
)) == (LCMAP_HIRAGANA
| LCMAP_KATAKANA
) ||
5731 (flags
& (LCMAP_HALFWIDTH
| LCMAP_FULLWIDTH
)) == (LCMAP_HALFWIDTH
| LCMAP_FULLWIDTH
) ||
5732 (flags
& (LCMAP_TRADITIONAL_CHINESE
| LCMAP_SIMPLIFIED_CHINESE
)) == (LCMAP_TRADITIONAL_CHINESE
| LCMAP_SIMPLIFIED_CHINESE
) ||
5735 SetLastError( ERROR_INVALID_FLAGS
);
5739 if (!dstlen
) dst
= NULL
;
5741 if (flags
& LCMAP_LINGUISTIC_CASING
&& !(sortid
= get_language_sort( locale
)))
5743 FIXME( "unknown locale %s\n", debugstr_w(locale
) );
5744 SetLastError( ERROR_INVALID_PARAMETER
);
5748 if (flags
& LCMAP_SORTKEY
)
5754 SetLastError( ERROR_INVALID_FLAGS
);
5757 if (srclen
< 0) srclen
= lstrlenW(src
);
5759 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5760 debugstr_w(locale
), flags
, debugstr_wn(src
, srclen
), srclen
, dst
, dstlen
);
5762 if ((ret
= get_sortkey( flags
, src
, srclen
, (char *)dst
, dstlen
))) ret
++;
5763 else SetLastError( ERROR_INSUFFICIENT_BUFFER
);
5767 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
5768 if (flags
& SORT_STRINGSORT
)
5770 SetLastError( ERROR_INVALID_FLAGS
);
5773 if (((flags
& (NORM_IGNORENONSPACE
| NORM_IGNORESYMBOLS
)) &&
5774 (flags
& ~(NORM_IGNORENONSPACE
| NORM_IGNORESYMBOLS
))) ||
5775 ((flags
& (LCMAP_HIRAGANA
| LCMAP_KATAKANA
| LCMAP_HALFWIDTH
| LCMAP_FULLWIDTH
)) &&
5776 (flags
& (LCMAP_SIMPLIFIED_CHINESE
| LCMAP_TRADITIONAL_CHINESE
))))
5778 SetLastError( ERROR_INVALID_FLAGS
);
5782 if (srclen
< 0) srclen
= lstrlenW(src
) + 1;
5784 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5785 debugstr_w(locale
), flags
, debugstr_wn(src
, srclen
), srclen
, dst
, dstlen
);
5787 if (!dst
) /* return required string length */
5789 if (flags
& NORM_IGNORESYMBOLS
)
5791 for (len
= 0; srclen
; src
++, srclen
--)
5792 if (!(get_char_type( CT_CTYPE1
, *src
) & (C1_PUNCT
| C1_SPACE
))) len
++;
5794 else if (flags
& LCMAP_FULLWIDTH
)
5796 for (len
= 0; srclen
; src
++, srclen
--, len
++)
5798 if (compose_katakana( src
, srclen
, NULL
) == 2)
5805 else if (flags
& LCMAP_HALFWIDTH
)
5807 for (len
= 0; srclen
; src
++, srclen
--, len
++)
5810 /* map Hiragana to Katakana before decomposition if needed */
5811 if ((flags
& LCMAP_KATAKANA
) &&
5812 ((wch
>= 0x3041 && wch
<= 0x3096) || wch
== 0x309D || wch
== 0x309E))
5815 if (decompose_katakana( wch
, NULL
, 0 ) == 2) len
++;
5822 if (src
== dst
&& (flags
& ~(LCMAP_LOWERCASE
| LCMAP_UPPERCASE
)))
5824 SetLastError( ERROR_INVALID_FLAGS
);
5828 if (flags
& (NORM_IGNORENONSPACE
| NORM_IGNORESYMBOLS
))
5830 for (len
= dstlen
, dst_ptr
= dst
; srclen
&& len
; src
++, srclen
--)
5832 if ((flags
& NORM_IGNORESYMBOLS
) && (get_char_type( CT_CTYPE1
, *src
) & (C1_PUNCT
| C1_SPACE
)))
5840 if (flags
& (LCMAP_FULLWIDTH
| LCMAP_HALFWIDTH
| LCMAP_HIRAGANA
| LCMAP_KATAKANA
))
5842 for (len
= dstlen
, dst_ptr
= dst
; len
&& srclen
; src
++, srclen
--, len
--, dst_ptr
++)
5845 if (flags
& LCMAP_FULLWIDTH
)
5847 /* map half-width character to full-width one,
5848 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
5849 if (map_to_fullwidth( src
, srclen
, &wch
) == 2)
5857 if (flags
& LCMAP_KATAKANA
)
5859 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
5860 we can't use C3_HIRAGANA as some characters can't map to katakana */
5861 if ((wch
>= 0x3041 && wch
<= 0x3096) || wch
== 0x309D || wch
== 0x309E) wch
+= 0x60;
5863 else if (flags
& LCMAP_HIRAGANA
)
5865 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
5866 we can't use C3_KATAKANA as some characters can't map to hiragana */
5867 if ((wch
>= 0x30A1 && wch
<= 0x30F6) || wch
== 0x30FD || wch
== 0x30FE) wch
-= 0x60;
5870 if (flags
& LCMAP_HALFWIDTH
)
5872 /* map full-width character to half-width one,
5873 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
5874 if (map_to_halfwidth(wch
, dst_ptr
, len
) == 2)
5881 else *dst_ptr
= wch
;
5883 if (!(flags
& (LCMAP_UPPERCASE
| LCMAP_LOWERCASE
)) || srclen
) goto done
;
5885 srclen
= dst_ptr
- dst
;
5889 if (flags
& (LCMAP_UPPERCASE
| LCMAP_LOWERCASE
))
5891 const USHORT
*table
= sort
.casemap
+ (flags
& LCMAP_LINGUISTIC_CASING
? sortid
->casemap
: 0);
5892 table
= table
+ 2 + (flags
& LCMAP_LOWERCASE
? table
[1] : 0);
5893 for (len
= dstlen
, dst_ptr
= dst
; srclen
&& len
; src
++, srclen
--, len
--)
5894 *dst_ptr
++ = casemap( table
, *src
);
5898 len
= min( srclen
, dstlen
);
5899 memcpy( dst
, src
, len
* sizeof(WCHAR
) );
5900 dst_ptr
= dst
+ len
;
5907 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
5911 return dst_ptr
- dst
;
5915 /***********************************************************************
5916 * LCMapStringA (kernelbase.@)
5918 INT WINAPI DECLSPEC_HOTPATCH
LCMapStringA( LCID lcid
, DWORD flags
, const char *src
, int srclen
,
5919 char *dst
, int dstlen
)
5921 WCHAR
*bufW
= NtCurrentTeb()->StaticUnicodeBuffer
;
5923 INT ret
= 0, srclenW
, dstlenW
;
5924 UINT locale_cp
= CP_ACP
;
5926 if (!src
|| !srclen
|| dstlen
< 0)
5928 SetLastError( ERROR_INVALID_PARAMETER
);
5932 locale_cp
= get_lcid_codepage( lcid
, flags
);
5934 srclenW
= MultiByteToWideChar( locale_cp
, 0, src
, srclen
, bufW
, 260 );
5935 if (srclenW
) srcW
= bufW
;
5938 srclenW
= MultiByteToWideChar( locale_cp
, 0, src
, srclen
, NULL
, 0 );
5939 srcW
= HeapAlloc( GetProcessHeap(), 0, srclenW
* sizeof(WCHAR
) );
5942 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
5945 MultiByteToWideChar( locale_cp
, 0, src
, srclen
, srcW
, srclenW
);
5948 if (flags
& LCMAP_SORTKEY
)
5952 SetLastError( ERROR_INVALID_FLAGS
);
5955 ret
= LCMapStringEx( NULL
, flags
, srcW
, srclenW
, (WCHAR
*)dst
, dstlen
, NULL
, NULL
, 0 );
5959 if (flags
& SORT_STRINGSORT
)
5961 SetLastError( ERROR_INVALID_FLAGS
);
5965 dstlenW
= LCMapStringEx( NULL
, flags
, srcW
, srclenW
, NULL
, 0, NULL
, NULL
, 0 );
5966 if (!dstlenW
) goto done
;
5968 dstW
= HeapAlloc( GetProcessHeap(), 0, dstlenW
* sizeof(WCHAR
) );
5971 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
5974 LCMapStringEx( NULL
, flags
, srcW
, srclenW
, dstW
, dstlenW
, NULL
, NULL
, 0 );
5975 ret
= WideCharToMultiByte( locale_cp
, 0, dstW
, dstlenW
, dst
, dstlen
, NULL
, NULL
);
5976 HeapFree( GetProcessHeap(), 0, dstW
);
5979 if (srcW
!= bufW
) HeapFree( GetProcessHeap(), 0, srcW
);
5984 /***********************************************************************
5985 * LCMapStringW (kernelbase.@)
5987 INT WINAPI DECLSPEC_HOTPATCH
LCMapStringW( LCID lcid
, DWORD flags
, const WCHAR
*src
, int srclen
,
5988 WCHAR
*dst
, int dstlen
)
5990 return LCMapStringEx( NULL
, flags
, src
, srclen
, dst
, dstlen
, NULL
, NULL
, 0 );
5994 /***********************************************************************
5995 * LocaleNameToLCID (kernelbase.@)
5997 LCID WINAPI DECLSPEC_HOTPATCH
LocaleNameToLCID( const WCHAR
*name
, DWORD flags
)
6000 const NLS_LOCALE_DATA
*locale
= get_locale_by_name( name
, &lcid
);
6004 SetLastError( ERROR_INVALID_PARAMETER
);
6007 if (!(flags
& LOCALE_ALLOW_NEUTRAL_NAMES
) && !locale
->inotneutral
)
6008 lcid
= locale
->idefaultlanguage
;
6013 /******************************************************************************
6014 * MultiByteToWideChar (kernelbase.@)
6016 INT WINAPI DECLSPEC_HOTPATCH
MultiByteToWideChar( UINT codepage
, DWORD flags
, const char *src
, INT srclen
,
6017 WCHAR
*dst
, INT dstlen
)
6021 if (!src
|| !srclen
|| (!dst
&& dstlen
) || dstlen
< 0)
6023 SetLastError( ERROR_INVALID_PARAMETER
);
6026 if (srclen
< 0) srclen
= strlen(src
) + 1;
6031 ret
= mbstowcs_cpsymbol( flags
, src
, srclen
, dst
, dstlen
);
6034 ret
= mbstowcs_utf7( flags
, src
, srclen
, dst
, dstlen
);
6037 ret
= mbstowcs_utf8( flags
, src
, srclen
, dst
, dstlen
);
6040 if (unix_cp
== CP_UTF8
)
6042 ret
= mbstowcs_utf8( flags
, src
, srclen
, dst
, dstlen
);
6048 ret
= mbstowcs_codepage( codepage
, flags
, src
, srclen
, dst
, dstlen
);
6051 TRACE( "cp %d %s -> %s, ret = %d\n", codepage
, debugstr_an(src
, srclen
), debugstr_wn(dst
, ret
), ret
);
6056 /******************************************************************************
6057 * NormalizeString (kernelbase.@)
6059 INT WINAPI DECLSPEC_HOTPATCH
NormalizeString(NORM_FORM form
, const WCHAR
*src
, INT src_len
,
6060 WCHAR
*dst
, INT dst_len
)
6062 NTSTATUS status
= RtlNormalizeString( form
, src
, src_len
, dst
, &dst_len
);
6066 case STATUS_OBJECT_NAME_NOT_FOUND
:
6067 status
= STATUS_INVALID_PARAMETER
;
6069 case STATUS_BUFFER_TOO_SMALL
:
6070 case STATUS_NO_UNICODE_TRANSLATION
:
6074 SetLastError( RtlNtStatusToDosError( status
));
6079 /******************************************************************************
6080 * ResolveLocaleName (kernelbase.@)
6082 INT WINAPI DECLSPEC_HOTPATCH
ResolveLocaleName( LPCWSTR name
, LPWSTR buffer
, INT len
)
6084 FIXME( "stub: %s, %p, %d\n", wine_dbgstr_w(name
), buffer
, len
);
6086 SetLastError( ERROR_CALL_NOT_IMPLEMENTED
);
6091 /******************************************************************************
6092 * SetLocaleInfoW (kernelbase.@)
6094 BOOL WINAPI DECLSPEC_HOTPATCH
SetLocaleInfoW( LCID lcid
, LCTYPE lctype
, const WCHAR
*data
)
6096 WCHAR
*str
, tmp
[80];
6100 SetLastError( ERROR_INVALID_PARAMETER
);
6104 switch (LOWORD(lctype
))
6106 case LOCALE_ICALENDARTYPE
: return set_registry_entry( &entry_icalendartype
, data
);
6107 case LOCALE_ICURRDIGITS
: return set_registry_entry( &entry_icurrdigits
, data
);
6108 case LOCALE_ICURRENCY
: return set_registry_entry( &entry_icurrency
, data
);
6109 case LOCALE_IDIGITS
: return set_registry_entry( &entry_idigits
, data
);
6110 case LOCALE_IDIGITSUBSTITUTION
: return set_registry_entry( &entry_idigitsubstitution
, data
);
6111 case LOCALE_IFIRSTDAYOFWEEK
: return set_registry_entry( &entry_ifirstdayofweek
, data
);
6112 case LOCALE_IFIRSTWEEKOFYEAR
: return set_registry_entry( &entry_ifirstweekofyear
, data
);
6113 case LOCALE_ILZERO
: return set_registry_entry( &entry_ilzero
, data
);
6114 case LOCALE_IMEASURE
: return set_registry_entry( &entry_imeasure
, data
);
6115 case LOCALE_INEGCURR
: return set_registry_entry( &entry_inegcurr
, data
);
6116 case LOCALE_INEGNUMBER
: return set_registry_entry( &entry_inegnumber
, data
);
6117 case LOCALE_IPAPERSIZE
: return set_registry_entry( &entry_ipapersize
, data
);
6118 case LOCALE_S1159
: return set_registry_entry( &entry_s1159
, data
);
6119 case LOCALE_S2359
: return set_registry_entry( &entry_s2359
, data
);
6120 case LOCALE_SCURRENCY
: return set_registry_entry( &entry_scurrency
, data
);
6121 case LOCALE_SDECIMAL
: return set_registry_entry( &entry_sdecimal
, data
);
6122 case LOCALE_SGROUPING
: return set_registry_entry( &entry_sgrouping
, data
);
6123 case LOCALE_SLIST
: return set_registry_entry( &entry_slist
, data
);
6124 case LOCALE_SLONGDATE
: return set_registry_entry( &entry_slongdate
, data
);
6125 case LOCALE_SMONDECIMALSEP
: return set_registry_entry( &entry_smondecimalsep
, data
);
6126 case LOCALE_SMONGROUPING
: return set_registry_entry( &entry_smongrouping
, data
);
6127 case LOCALE_SMONTHOUSANDSEP
: return set_registry_entry( &entry_smonthousandsep
, data
);
6128 case LOCALE_SNATIVEDIGITS
: return set_registry_entry( &entry_snativedigits
, data
);
6129 case LOCALE_SNEGATIVESIGN
: return set_registry_entry( &entry_snegativesign
, data
);
6130 case LOCALE_SPOSITIVESIGN
: return set_registry_entry( &entry_spositivesign
, data
);
6131 case LOCALE_SSHORTTIME
: return set_registry_entry( &entry_sshorttime
, data
);
6132 case LOCALE_STHOUSAND
: return set_registry_entry( &entry_sthousand
, data
);
6133 case LOCALE_SYEARMONTH
: return set_registry_entry( &entry_syearmonth
, data
);
6136 if (!get_locale_info( user_locale
, user_lcid
, LOCALE_SSHORTDATE
, tmp
, ARRAY_SIZE(tmp
) )) break;
6137 data
= locale_replace_separator( tmp
, data
);
6139 case LOCALE_SSHORTDATE
:
6140 if (!set_registry_entry( &entry_sshortdate
, data
)) return FALSE
;
6141 update_registry_value( LOCALE_IDATE
, L
"iDate" );
6142 update_registry_value( LOCALE_SDATE
, L
"sDate" );
6146 if (!get_locale_info( user_locale
, user_lcid
, LOCALE_STIMEFORMAT
, tmp
, ARRAY_SIZE(tmp
) )) break;
6147 data
= locale_replace_separator( tmp
, data
);
6149 case LOCALE_STIMEFORMAT
:
6150 if (!set_registry_entry( &entry_stimeformat
, data
)) return FALSE
;
6151 update_registry_value( LOCALE_ITIME
, L
"iTime" );
6152 update_registry_value( LOCALE_ITIMEMARKPOSN
, L
"iTimePrefix" );
6153 update_registry_value( LOCALE_ITLZERO
, L
"iTLZero" );
6154 update_registry_value( LOCALE_STIME
, L
"sTime" );
6158 if (!get_locale_info( user_locale
, user_lcid
, LOCALE_STIMEFORMAT
, tmp
, ARRAY_SIZE(tmp
) )) break;
6159 if (!(str
= find_format( tmp
, L
"Hh" ))) break;
6160 while (*str
== 'h' || *str
== 'H') *str
++ = (*data
== '0' ? 'h' : 'H');
6161 if (!set_registry_entry( &entry_stimeformat
, tmp
)) break;
6162 update_registry_value( LOCALE_ITIME
, L
"iTime" );
6165 case LOCALE_SINTLSYMBOL
:
6166 /* FIXME: also store sintlsymbol */
6167 set_registry_entry( &entry_scurrency
, data
);
6170 SetLastError( ERROR_INVALID_FLAGS
);
6175 /***********************************************************************
6176 * SetCalendarInfoW (kernelbase.@)
6178 INT WINAPI
/* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid
, CALID calendar
, CALTYPE type
, const WCHAR
*data
)
6180 FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid
, calendar
, type
, debugstr_w(data
) );
6185 /***********************************************************************
6186 * SetProcessPreferredUILanguages (kernelbase.@)
6188 BOOL WINAPI DECLSPEC_HOTPATCH
SetProcessPreferredUILanguages( DWORD flags
, PCZZWSTR buffer
, ULONG
*count
)
6190 return set_ntstatus( RtlSetProcessPreferredUILanguages( flags
, buffer
, count
));
6194 /***********************************************************************
6195 * SetThreadPreferredUILanguages (kernelbase.@)
6197 BOOL WINAPI DECLSPEC_HOTPATCH
SetThreadPreferredUILanguages( DWORD flags
, PCZZWSTR buffer
, ULONG
*count
)
6199 return set_ntstatus( RtlSetThreadPreferredUILanguages( flags
, buffer
, count
));
6203 /***********************************************************************
6204 * SetTimeZoneInformation (kernelbase.@)
6206 BOOL WINAPI DECLSPEC_HOTPATCH
SetTimeZoneInformation( const TIME_ZONE_INFORMATION
*info
)
6208 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION
*)info
));
6212 /******************************************************************************
6213 * SetUserGeoID (kernelbase.@)
6215 BOOL WINAPI DECLSPEC_HOTPATCH
SetUserGeoID( GEOID id
)
6217 const struct geo_id
*geo
= find_geo_id_entry( id
);
6223 SetLastError( ERROR_INVALID_PARAMETER
);
6226 if (!RegCreateKeyExW( intl_key
, L
"Geo", 0, NULL
, 0, KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
))
6228 const WCHAR
*name
= geo
->class == GEOCLASS_NATION
? L
"Nation" : L
"Region";
6229 swprintf( bufferW
, ARRAY_SIZE(bufferW
), L
"%u", geo
->id
);
6230 RegSetValueExW( hkey
, name
, 0, REG_SZ
, (BYTE
*)bufferW
, (lstrlenW(bufferW
) + 1) * sizeof(WCHAR
) );
6232 if (geo
->class == GEOCLASS_NATION
|| wcscmp( geo
->iso2
, L
"XX" ))
6233 lstrcpyW( bufferW
, geo
->iso2
);
6235 swprintf( bufferW
, ARRAY_SIZE(bufferW
), L
"%03u", geo
->uncode
);
6236 RegSetValueExW( hkey
, L
"Name", 0, REG_SZ
, (BYTE
*)bufferW
, (lstrlenW(bufferW
) + 1) * sizeof(WCHAR
) );
6237 RegCloseKey( hkey
);
6243 /***********************************************************************
6244 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
6246 BOOL WINAPI DECLSPEC_HOTPATCH
SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION
*info
,
6247 const SYSTEMTIME
*system
,
6250 TIME_ZONE_INFORMATION tzinfo
;
6255 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION
*)&tzinfo
);
6259 if (!SystemTimeToFileTime( system
, (FILETIME
*)&ft
)) return FALSE
;
6260 switch (get_timezone_id( info
, ft
, FALSE
))
6262 case TIME_ZONE_ID_UNKNOWN
:
6263 ft
.QuadPart
-= info
->Bias
* (LONGLONG
)600000000;
6265 case TIME_ZONE_ID_STANDARD
:
6266 ft
.QuadPart
-= (info
->Bias
+ info
->StandardBias
) * (LONGLONG
)600000000;
6268 case TIME_ZONE_ID_DAYLIGHT
:
6269 ft
.QuadPart
-= (info
->Bias
+ info
->DaylightBias
) * (LONGLONG
)600000000;
6274 return FileTimeToSystemTime( (FILETIME
*)&ft
, local
);
6278 /***********************************************************************
6279 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
6281 BOOL WINAPI DECLSPEC_HOTPATCH
TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION
*info
,
6282 const SYSTEMTIME
*local
,
6283 SYSTEMTIME
*system
)
6285 TIME_ZONE_INFORMATION tzinfo
;
6290 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION
*)&tzinfo
);
6294 if (!SystemTimeToFileTime( local
, (FILETIME
*)&ft
)) return FALSE
;
6295 switch (get_timezone_id( info
, ft
, TRUE
))
6297 case TIME_ZONE_ID_UNKNOWN
:
6298 ft
.QuadPart
+= info
->Bias
* (LONGLONG
)600000000;
6300 case TIME_ZONE_ID_STANDARD
:
6301 ft
.QuadPart
+= (info
->Bias
+ info
->StandardBias
) * (LONGLONG
)600000000;
6303 case TIME_ZONE_ID_DAYLIGHT
:
6304 ft
.QuadPart
+= (info
->Bias
+ info
->DaylightBias
) * (LONGLONG
)600000000;
6309 return FileTimeToSystemTime( (FILETIME
*)&ft
, system
);
6313 /***********************************************************************
6314 * VerLanguageNameA (kernelbase.@)
6316 DWORD WINAPI DECLSPEC_HOTPATCH
VerLanguageNameA( DWORD lang
, LPSTR buffer
, DWORD size
)
6318 return GetLocaleInfoA( MAKELCID( lang
, SORT_DEFAULT
), LOCALE_SENGLANGUAGE
, buffer
, size
);
6322 /***********************************************************************
6323 * VerLanguageNameW (kernelbase.@)
6325 DWORD WINAPI DECLSPEC_HOTPATCH
VerLanguageNameW( DWORD lang
, LPWSTR buffer
, DWORD size
)
6327 return GetLocaleInfoW( MAKELCID( lang
, SORT_DEFAULT
), LOCALE_SENGLANGUAGE
, buffer
, size
);
6331 /***********************************************************************
6332 * WideCharToMultiByte (kernelbase.@)
6334 INT WINAPI DECLSPEC_HOTPATCH
WideCharToMultiByte( UINT codepage
, DWORD flags
, LPCWSTR src
, INT srclen
,
6335 LPSTR dst
, INT dstlen
, LPCSTR defchar
, BOOL
*used
)
6339 if (!src
|| !srclen
|| (!dst
&& dstlen
) || dstlen
< 0)
6341 SetLastError( ERROR_INVALID_PARAMETER
);
6345 if (srclen
< 0) srclen
= lstrlenW(src
) + 1;
6350 ret
= wcstombs_cpsymbol( flags
, src
, srclen
, dst
, dstlen
, defchar
, used
);
6353 ret
= wcstombs_utf7( flags
, src
, srclen
, dst
, dstlen
, defchar
, used
);
6356 ret
= wcstombs_utf8( flags
, src
, srclen
, dst
, dstlen
, defchar
, used
);
6359 if (unix_cp
== CP_UTF8
)
6361 if (used
) *used
= FALSE
;
6362 ret
= wcstombs_utf8( flags
, src
, srclen
, dst
, dstlen
, NULL
, NULL
);
6368 ret
= wcstombs_codepage( codepage
, flags
, src
, srclen
, dst
, dstlen
, defchar
, used
);
6371 TRACE( "cp %d %s -> %s, ret = %d\n", codepage
, debugstr_wn(src
, srclen
), debugstr_an(dst
, ret
), ret
);
6376 /***********************************************************************
6377 * GetUserDefaultGeoName (kernelbase.@)
6379 INT WINAPI
GetUserDefaultGeoName(LPWSTR geo_name
, int count
)
6386 TRACE( "geo_name %p, count %d.\n", geo_name
, count
);
6388 if (count
&& !geo_name
)
6390 SetLastError( ERROR_INVALID_PARAMETER
);
6393 if (!(status
= RegOpenKeyExW( intl_key
, L
"Geo", 0, KEY_ALL_ACCESS
, &key
)))
6395 size
= sizeof(buffer
);
6396 status
= RegQueryValueExW( key
, L
"Name", NULL
, NULL
, (BYTE
*)buffer
, &size
);
6401 const struct geo_id
*geo
= find_geo_id_entry( GetUserGeoID( GEOCLASS_NATION
));
6402 if (geo
&& geo
->id
!= 39070)
6403 lstrcpyW( buffer
, geo
->iso2
);
6405 lstrcpyW( buffer
, L
"001" );
6407 size
= lstrlenW( buffer
) + 1;
6412 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
6415 lstrcpyW( geo_name
, buffer
);
6420 /***********************************************************************
6421 * SetUserDefaultGeoName (kernelbase.@)
6423 BOOL WINAPI
SetUserGeoName(PWSTR geo_name
)
6425 const struct geo_id
*geo
;
6427 TRACE( "geo_name %s.\n", debugstr_w( geo_name
));
6429 if (!geo_name
|| !(geo
= find_geo_name_entry( geo_name
)))
6431 SetLastError( ERROR_INVALID_PARAMETER
);
6434 return SetUserGeoID( geo
->id
);