libwine: Remove __wine_main_arg* from the public header.
[wine/zf.git] / dlls / kernelbase / locale.c
blob53e4e42da3a3f1610deb7eb7a72da60aa4c9aaa8
1 /*
2 * Locale support
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2003 Jon Griffiths
8 * Copyright 2005 Dmitry Timoshkov
9 * Copyright 2002, 2019 Alexandre Julliard
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <stdarg.h>
27 #include <stdlib.h>
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winreg.h"
34 #include "winnls.h"
35 #include "winuser.h"
36 #include "winternl.h"
37 #include "kernelbase.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(nls);
42 #define CALINFO_MAX_YEAR 2029
44 extern UINT CDECL __wine_get_unix_codepage(void);
46 extern const unsigned int collation_table[] DECLSPEC_HIDDEN;
48 static HANDLE kernel32_handle;
50 static const struct registry_value
52 DWORD lctype;
53 const WCHAR *name;
54 } registry_values[] =
56 { LOCALE_ICALENDARTYPE, L"iCalendarType" },
57 { LOCALE_ICURRDIGITS, L"iCurrDigits" },
58 { LOCALE_ICURRENCY, L"iCurrency" },
59 { LOCALE_IDIGITS, L"iDigits" },
60 { LOCALE_IFIRSTDAYOFWEEK, L"iFirstDayOfWeek" },
61 { LOCALE_IFIRSTWEEKOFYEAR, L"iFirstWeekOfYear" },
62 { LOCALE_ILZERO, L"iLZero" },
63 { LOCALE_IMEASURE, L"iMeasure" },
64 { LOCALE_INEGCURR, L"iNegCurr" },
65 { LOCALE_INEGNUMBER, L"iNegNumber" },
66 { LOCALE_IPAPERSIZE, L"iPaperSize" },
67 { LOCALE_ITIME, L"iTime" },
68 { LOCALE_S1159, L"s1159" },
69 { LOCALE_S2359, L"s2359" },
70 { LOCALE_SCURRENCY, L"sCurrency" },
71 { LOCALE_SDATE, L"sDate" },
72 { LOCALE_SDECIMAL, L"sDecimal" },
73 { LOCALE_SGROUPING, L"sGrouping" },
74 { LOCALE_SLIST, L"sList" },
75 { LOCALE_SLONGDATE, L"sLongDate" },
76 { LOCALE_SMONDECIMALSEP, L"sMonDecimalSep" },
77 { LOCALE_SMONGROUPING, L"sMonGrouping" },
78 { LOCALE_SMONTHOUSANDSEP, L"sMonThousandSep" },
79 { LOCALE_SNEGATIVESIGN, L"sNegativeSign" },
80 { LOCALE_SPOSITIVESIGN, L"sPositiveSign" },
81 { LOCALE_SSHORTDATE, L"sShortDate" },
82 { LOCALE_STHOUSAND, L"sThousand" },
83 { LOCALE_STIME, L"sTime" },
84 { LOCALE_STIMEFORMAT, L"sTimeFormat" },
85 { LOCALE_SYEARMONTH, L"sYearMonth" },
86 /* The following are not listed under MSDN as supported,
87 * but seem to be used and also stored in the registry.
89 { LOCALE_SNAME, L"LocaleName" },
90 { LOCALE_ICOUNTRY, L"iCountry" },
91 { LOCALE_IDATE, L"iDate" },
92 { LOCALE_ILDATE, L"iLDate" },
93 { LOCALE_ITLZERO, L"iTLZero" },
94 { LOCALE_SCOUNTRY, L"sCountry" },
95 { LOCALE_SABBREVLANGNAME, L"sLanguage" },
96 { LOCALE_IDIGITSUBSTITUTION, L"Numshape" },
97 { LOCALE_SNATIVEDIGITS, L"sNativeDigits" },
98 { LOCALE_ITIMEMARKPOSN, L"iTimePrefix" },
101 static WCHAR *registry_cache[ARRAY_SIZE(registry_values)];
103 static const struct { UINT cp; const WCHAR *name; } codepage_names[] =
105 { 37, L"IBM EBCDIC US Canada" },
106 { 424, L"IBM EBCDIC Hebrew" },
107 { 437, L"OEM United States" },
108 { 500, L"IBM EBCDIC International" },
109 { 737, L"OEM Greek 437G" },
110 { 775, L"OEM Baltic" },
111 { 850, L"OEM Multilingual Latin 1" },
112 { 852, L"OEM Slovak Latin 2" },
113 { 855, L"OEM Cyrillic" },
114 { 856, L"Hebrew PC" },
115 { 857, L"OEM Turkish" },
116 { 860, L"OEM Portuguese" },
117 { 861, L"OEM Icelandic" },
118 { 862, L"OEM Hebrew" },
119 { 863, L"OEM Canadian French" },
120 { 864, L"OEM Arabic" },
121 { 865, L"OEM Nordic" },
122 { 866, L"OEM Russian" },
123 { 869, L"OEM Greek" },
124 { 874, L"ANSI/OEM Thai" },
125 { 875, L"IBM EBCDIC Greek" },
126 { 878, L"Russian KOI8" },
127 { 932, L"ANSI/OEM Japanese Shift-JIS" },
128 { 936, L"ANSI/OEM Simplified Chinese GBK" },
129 { 949, L"ANSI/OEM Korean Unified Hangul" },
130 { 950, L"ANSI/OEM Traditional Chinese Big5" },
131 { 1006, L"IBM Arabic" },
132 { 1026, L"IBM EBCDIC Latin 5 Turkish" },
133 { 1250, L"ANSI Eastern Europe" },
134 { 1251, L"ANSI Cyrillic" },
135 { 1252, L"ANSI Latin 1" },
136 { 1253, L"ANSI Greek" },
137 { 1254, L"ANSI Turkish" },
138 { 1255, L"ANSI Hebrew" },
139 { 1256, L"ANSI Arabic" },
140 { 1257, L"ANSI Baltic" },
141 { 1258, L"ANSI/OEM Viet Nam" },
142 { 1361, L"Korean Johab" },
143 { 10000, L"Mac Roman" },
144 { 10001, L"Mac Japanese" },
145 { 10002, L"Mac Traditional Chinese" },
146 { 10003, L"Mac Korean" },
147 { 10004, L"Mac Arabic" },
148 { 10005, L"Mac Hebrew" },
149 { 10006, L"Mac Greek" },
150 { 10007, L"Mac Cyrillic" },
151 { 10008, L"Mac Simplified Chinese" },
152 { 10010, L"Mac Romanian" },
153 { 10017, L"Mac Ukrainian" },
154 { 10021, L"Mac Thai" },
155 { 10029, L"Mac Latin 2" },
156 { 10079, L"Mac Icelandic" },
157 { 10081, L"Mac Turkish" },
158 { 10082, L"Mac Croatian" },
159 { 20127, L"US-ASCII (7bit)" },
160 { 20866, L"Russian KOI8" },
161 { 20932, L"EUC-JP" },
162 { 21866, L"Ukrainian KOI8" },
163 { 28591, L"ISO 8859-1 Latin 1" },
164 { 28592, L"ISO 8859-2 Latin 2 (East European)" },
165 { 28593, L"ISO 8859-3 Latin 3 (South European)" },
166 { 28594, L"ISO 8859-4 Latin 4 (Baltic old)" },
167 { 28595, L"ISO 8859-5 Cyrillic" },
168 { 28596, L"ISO 8859-6 Arabic" },
169 { 28597, L"ISO 8859-7 Greek" },
170 { 28598, L"ISO 8859-8 Hebrew" },
171 { 28599, L"ISO 8859-9 Latin 5 (Turkish)" },
172 { 28600, L"ISO 8859-10 Latin 6 (Nordic)" },
173 { 28601, L"ISO 8859-11 Latin (Thai)" },
174 { 28603, L"ISO 8859-13 Latin 7 (Baltic)" },
175 { 28604, L"ISO 8859-14 Latin 8 (Celtic)" },
176 { 28605, L"ISO 8859-15 Latin 9 (Euro)" },
177 { 28606, L"ISO 8859-16 Latin 10 (Balkan)" },
178 { 65000, L"Unicode (UTF-7)" },
179 { 65001, L"Unicode (UTF-8)" }
182 /* Unicode expanded ligatures */
183 static const WCHAR ligatures[][5] =
185 { 0x00c6, 'A','E',0 },
186 { 0x00de, 'T','H',0 },
187 { 0x00df, 's','s',0 },
188 { 0x00e6, 'a','e',0 },
189 { 0x00fe, 't','h',0 },
190 { 0x0132, 'I','J',0 },
191 { 0x0133, 'i','j',0 },
192 { 0x0152, 'O','E',0 },
193 { 0x0153, 'o','e',0 },
194 { 0x01c4, 'D',0x017d,0 },
195 { 0x01c5, 'D',0x017e,0 },
196 { 0x01c6, 'd',0x017e,0 },
197 { 0x01c7, 'L','J',0 },
198 { 0x01c8, 'L','j',0 },
199 { 0x01c9, 'l','j',0 },
200 { 0x01ca, 'N','J',0 },
201 { 0x01cb, 'N','j',0 },
202 { 0x01cc, 'n','j',0 },
203 { 0x01e2, 0x0100,0x0112,0 },
204 { 0x01e3, 0x0101,0x0113,0 },
205 { 0x01f1, 'D','Z',0 },
206 { 0x01f2, 'D','z',0 },
207 { 0x01f3, 'd','z',0 },
208 { 0x01fc, 0x00c1,0x00c9,0 },
209 { 0x01fd, 0x00e1,0x00e9,0 },
210 { 0x05f0, 0x05d5,0x05d5,0 },
211 { 0x05f1, 0x05d5,0x05d9,0 },
212 { 0x05f2, 0x05d9,0x05d9,0 },
213 { 0xfb00, 'f','f',0 },
214 { 0xfb01, 'f','i',0 },
215 { 0xfb02, 'f','l',0 },
216 { 0xfb03, 'f','f','i',0 },
217 { 0xfb04, 'f','f','l',0 },
218 { 0xfb05, 0x017f,'t',0 },
219 { 0xfb06, 's','t',0 },
222 enum locationkind { LOCATION_NATION = 0, LOCATION_REGION, LOCATION_BOTH };
224 struct geoinfo
226 GEOID id;
227 WCHAR iso2W[3];
228 WCHAR iso3W[4];
229 GEOID parent;
230 int uncode;
231 enum locationkind kind;
234 static const struct geoinfo geoinfodata[] =
236 { 2, L"AG", L"ATG", 10039880, 28 }, /* Antigua and Barbuda */
237 { 3, L"AF", L"AFG", 47614, 4 }, /* Afghanistan */
238 { 4, L"DZ", L"DZA", 42487, 12 }, /* Algeria */
239 { 5, L"AZ", L"AZE", 47611, 31 }, /* Azerbaijan */
240 { 6, L"AL", L"ALB", 47610, 8 }, /* Albania */
241 { 7, L"AM", L"ARM", 47611, 51 }, /* Armenia */
242 { 8, L"AD", L"AND", 47610, 20 }, /* Andorra */
243 { 9, L"AO", L"AGO", 42484, 24 }, /* Angola */
244 { 10, L"AS", L"ASM", 26286, 16 }, /* American Samoa */
245 { 11, L"AR", L"ARG", 31396, 32 }, /* Argentina */
246 { 12, L"AU", L"AUS", 10210825, 36 }, /* Australia */
247 { 14, L"AT", L"AUT", 10210824, 40 }, /* Austria */
248 { 17, L"BH", L"BHR", 47611, 48 }, /* Bahrain */
249 { 18, L"BB", L"BRB", 10039880, 52 }, /* Barbados */
250 { 19, L"BW", L"BWA", 10039883, 72 }, /* Botswana */
251 { 20, L"BM", L"BMU", 23581, 60 }, /* Bermuda */
252 { 21, L"BE", L"BEL", 10210824, 56 }, /* Belgium */
253 { 22, L"BS", L"BHS", 10039880, 44 }, /* Bahamas, The */
254 { 23, L"BD", L"BGD", 47614, 50 }, /* Bangladesh */
255 { 24, L"BZ", L"BLZ", 27082, 84 }, /* Belize */
256 { 25, L"BA", L"BIH", 47610, 70 }, /* Bosnia and Herzegovina */
257 { 26, L"BO", L"BOL", 31396, 68 }, /* Bolivia */
258 { 27, L"MM", L"MMR", 47599, 104 }, /* Myanmar */
259 { 28, L"BJ", L"BEN", 42483, 204 }, /* Benin */
260 { 29, L"BY", L"BLR", 47609, 112 }, /* Belarus */
261 { 30, L"SB", L"SLB", 20900, 90 }, /* Solomon Islands */
262 { 32, L"BR", L"BRA", 31396, 76 }, /* Brazil */
263 { 34, L"BT", L"BTN", 47614, 64 }, /* Bhutan */
264 { 35, L"BG", L"BGR", 47609, 100 }, /* Bulgaria */
265 { 37, L"BN", L"BRN", 47599, 96 }, /* Brunei */
266 { 38, L"BI", L"BDI", 47603, 108 }, /* Burundi */
267 { 39, L"CA", L"CAN", 23581, 124 }, /* Canada */
268 { 40, L"KH", L"KHM", 47599, 116 }, /* Cambodia */
269 { 41, L"TD", L"TCD", 42484, 148 }, /* Chad */
270 { 42, L"LK", L"LKA", 47614, 144 }, /* Sri Lanka */
271 { 43, L"CG", L"COG", 42484, 178 }, /* Congo */
272 { 44, L"CD", L"COD", 42484, 180 }, /* Congo (DRC) */
273 { 45, L"CN", L"CHN", 47600, 156 }, /* China */
274 { 46, L"CL", L"CHL", 31396, 152 }, /* Chile */
275 { 49, L"CM", L"CMR", 42484, 120 }, /* Cameroon */
276 { 50, L"KM", L"COM", 47603, 174 }, /* Comoros */
277 { 51, L"CO", L"COL", 31396, 170 }, /* Colombia */
278 { 54, L"CR", L"CRI", 27082, 188 }, /* Costa Rica */
279 { 55, L"CF", L"CAF", 42484, 140 }, /* Central African Republic */
280 { 56, L"CU", L"CUB", 10039880, 192 }, /* Cuba */
281 { 57, L"CV", L"CPV", 42483, 132 }, /* Cape Verde */
282 { 59, L"CY", L"CYP", 47611, 196 }, /* Cyprus */
283 { 61, L"DK", L"DNK", 10039882, 208 }, /* Denmark */
284 { 62, L"DJ", L"DJI", 47603, 262 }, /* Djibouti */
285 { 63, L"DM", L"DMA", 10039880, 212 }, /* Dominica */
286 { 65, L"DO", L"DOM", 10039880, 214 }, /* Dominican Republic */
287 { 66, L"EC", L"ECU", 31396, 218 }, /* Ecuador */
288 { 67, L"EG", L"EGY", 42487, 818 }, /* Egypt */
289 { 68, L"IE", L"IRL", 10039882, 372 }, /* Ireland */
290 { 69, L"GQ", L"GNQ", 42484, 226 }, /* Equatorial Guinea */
291 { 70, L"EE", L"EST", 10039882, 233 }, /* Estonia */
292 { 71, L"ER", L"ERI", 47603, 232 }, /* Eritrea */
293 { 72, L"SV", L"SLV", 27082, 222 }, /* El Salvador */
294 { 73, L"ET", L"ETH", 47603, 231 }, /* Ethiopia */
295 { 75, L"CZ", L"CZE", 47609, 203 }, /* Czech Republic */
296 { 77, L"FI", L"FIN", 10039882, 246 }, /* Finland */
297 { 78, L"FJ", L"FJI", 20900, 242 }, /* Fiji Islands */
298 { 80, L"FM", L"FSM", 21206, 583 }, /* Micronesia */
299 { 81, L"FO", L"FRO", 10039882, 234 }, /* Faroe Islands */
300 { 84, L"FR", L"FRA", 10210824, 250 }, /* France */
301 { 86, L"GM", L"GMB", 42483, 270 }, /* Gambia, The */
302 { 87, L"GA", L"GAB", 42484, 266 }, /* Gabon */
303 { 88, L"GE", L"GEO", 47611, 268 }, /* Georgia */
304 { 89, L"GH", L"GHA", 42483, 288 }, /* Ghana */
305 { 90, L"GI", L"GIB", 47610, 292 }, /* Gibraltar */
306 { 91, L"GD", L"GRD", 10039880, 308 }, /* Grenada */
307 { 93, L"GL", L"GRL", 23581, 304 }, /* Greenland */
308 { 94, L"DE", L"DEU", 10210824, 276 }, /* Germany */
309 { 98, L"GR", L"GRC", 47610, 300 }, /* Greece */
310 { 99, L"GT", L"GTM", 27082, 320 }, /* Guatemala */
311 { 100, L"GN", L"GIN", 42483, 324 }, /* Guinea */
312 { 101, L"GY", L"GUY", 31396, 328 }, /* Guyana */
313 { 103, L"HT", L"HTI", 10039880, 332 }, /* Haiti */
314 { 104, L"HK", L"HKG", 47600, 344 }, /* Hong Kong S.A.R. */
315 { 106, L"HN", L"HND", 27082, 340 }, /* Honduras */
316 { 108, L"HR", L"HRV", 47610, 191 }, /* Croatia */
317 { 109, L"HU", L"HUN", 47609, 348 }, /* Hungary */
318 { 110, L"IS", L"ISL", 10039882, 352 }, /* Iceland */
319 { 111, L"ID", L"IDN", 47599, 360 }, /* Indonesia */
320 { 113, L"IN", L"IND", 47614, 356 }, /* India */
321 { 114, L"IO", L"IOT", 39070, 86 }, /* British Indian Ocean Territory */
322 { 116, L"IR", L"IRN", 47614, 364 }, /* Iran */
323 { 117, L"IL", L"ISR", 47611, 376 }, /* Israel */
324 { 118, L"IT", L"ITA", 47610, 380 }, /* Italy */
325 { 119, L"CI", L"CIV", 42483, 384 }, /* Côte d'Ivoire */
326 { 121, L"IQ", L"IRQ", 47611, 368 }, /* Iraq */
327 { 122, L"JP", L"JPN", 47600, 392 }, /* Japan */
328 { 124, L"JM", L"JAM", 10039880, 388 }, /* Jamaica */
329 { 125, L"SJ", L"SJM", 10039882, 744 }, /* Jan Mayen */
330 { 126, L"JO", L"JOR", 47611, 400 }, /* Jordan */
331 { 127, L"XX", L"XX", 161832256 }, /* Johnston Atoll */
332 { 129, L"KE", L"KEN", 47603, 404 }, /* Kenya */
333 { 130, L"KG", L"KGZ", 47590, 417 }, /* Kyrgyzstan */
334 { 131, L"KP", L"PRK", 47600, 408 }, /* North Korea */
335 { 133, L"KI", L"KIR", 21206, 296 }, /* Kiribati */
336 { 134, L"KR", L"KOR", 47600, 410 }, /* Korea */
337 { 136, L"KW", L"KWT", 47611, 414 }, /* Kuwait */
338 { 137, L"KZ", L"KAZ", 47590, 398 }, /* Kazakhstan */
339 { 138, L"LA", L"LAO", 47599, 418 }, /* Laos */
340 { 139, L"LB", L"LBN", 47611, 422 }, /* Lebanon */
341 { 140, L"LV", L"LVA", 10039882, 428 }, /* Latvia */
342 { 141, L"LT", L"LTU", 10039882, 440 }, /* Lithuania */
343 { 142, L"LR", L"LBR", 42483, 430 }, /* Liberia */
344 { 143, L"SK", L"SVK", 47609, 703 }, /* Slovakia */
345 { 145, L"LI", L"LIE", 10210824, 438 }, /* Liechtenstein */
346 { 146, L"LS", L"LSO", 10039883, 426 }, /* Lesotho */
347 { 147, L"LU", L"LUX", 10210824, 442 }, /* Luxembourg */
348 { 148, L"LY", L"LBY", 42487, 434 }, /* Libya */
349 { 149, L"MG", L"MDG", 47603, 450 }, /* Madagascar */
350 { 151, L"MO", L"MAC", 47600, 446 }, /* Macao S.A.R. */
351 { 152, L"MD", L"MDA", 47609, 498 }, /* Moldova */
352 { 154, L"MN", L"MNG", 47600, 496 }, /* Mongolia */
353 { 156, L"MW", L"MWI", 47603, 454 }, /* Malawi */
354 { 157, L"ML", L"MLI", 42483, 466 }, /* Mali */
355 { 158, L"MC", L"MCO", 10210824, 492 }, /* Monaco */
356 { 159, L"MA", L"MAR", 42487, 504 }, /* Morocco */
357 { 160, L"MU", L"MUS", 47603, 480 }, /* Mauritius */
358 { 162, L"MR", L"MRT", 42483, 478 }, /* Mauritania */
359 { 163, L"MT", L"MLT", 47610, 470 }, /* Malta */
360 { 164, L"OM", L"OMN", 47611, 512 }, /* Oman */
361 { 165, L"MV", L"MDV", 47614, 462 }, /* Maldives */
362 { 166, L"MX", L"MEX", 27082, 484 }, /* Mexico */
363 { 167, L"MY", L"MYS", 47599, 458 }, /* Malaysia */
364 { 168, L"MZ", L"MOZ", 47603, 508 }, /* Mozambique */
365 { 173, L"NE", L"NER", 42483, 562 }, /* Niger */
366 { 174, L"VU", L"VUT", 20900, 548 }, /* Vanuatu */
367 { 175, L"NG", L"NGA", 42483, 566 }, /* Nigeria */
368 { 176, L"NL", L"NLD", 10210824, 528 }, /* Netherlands */
369 { 177, L"NO", L"NOR", 10039882, 578 }, /* Norway */
370 { 178, L"NP", L"NPL", 47614, 524 }, /* Nepal */
371 { 180, L"NR", L"NRU", 21206, 520 }, /* Nauru */
372 { 181, L"SR", L"SUR", 31396, 740 }, /* Suriname */
373 { 182, L"NI", L"NIC", 27082, 558 }, /* Nicaragua */
374 { 183, L"NZ", L"NZL", 10210825, 554 }, /* New Zealand */
375 { 184, L"PS", L"PSE", 47611, 275 }, /* Palestinian Authority */
376 { 185, L"PY", L"PRY", 31396, 600 }, /* Paraguay */
377 { 187, L"PE", L"PER", 31396, 604 }, /* Peru */
378 { 190, L"PK", L"PAK", 47614, 586 }, /* Pakistan */
379 { 191, L"PL", L"POL", 47609, 616 }, /* Poland */
380 { 192, L"PA", L"PAN", 27082, 591 }, /* Panama */
381 { 193, L"PT", L"PRT", 47610, 620 }, /* Portugal */
382 { 194, L"PG", L"PNG", 20900, 598 }, /* Papua New Guinea */
383 { 195, L"PW", L"PLW", 21206, 585 }, /* Palau */
384 { 196, L"GW", L"GNB", 42483, 624 }, /* Guinea-Bissau */
385 { 197, L"QA", L"QAT", 47611, 634 }, /* Qatar */
386 { 198, L"RE", L"REU", 47603, 638 }, /* Reunion */
387 { 199, L"MH", L"MHL", 21206, 584 }, /* Marshall Islands */
388 { 200, L"RO", L"ROU", 47609, 642 }, /* Romania */
389 { 201, L"PH", L"PHL", 47599, 608 }, /* Philippines */
390 { 202, L"PR", L"PRI", 10039880, 630 }, /* Puerto Rico */
391 { 203, L"RU", L"RUS", 47609, 643 }, /* Russia */
392 { 204, L"RW", L"RWA", 47603, 646 }, /* Rwanda */
393 { 205, L"SA", L"SAU", 47611, 682 }, /* Saudi Arabia */
394 { 206, L"PM", L"SPM", 23581, 666 }, /* St. Pierre and Miquelon */
395 { 207, L"KN", L"KNA", 10039880, 659 }, /* St. Kitts and Nevis */
396 { 208, L"SC", L"SYC", 47603, 690 }, /* Seychelles */
397 { 209, L"ZA", L"ZAF", 10039883, 710 }, /* South Africa */
398 { 210, L"SN", L"SEN", 42483, 686 }, /* Senegal */
399 { 212, L"SI", L"SVN", 47610, 705 }, /* Slovenia */
400 { 213, L"SL", L"SLE", 42483, 694 }, /* Sierra Leone */
401 { 214, L"SM", L"SMR", 47610, 674 }, /* San Marino */
402 { 215, L"SG", L"SGP", 47599, 702 }, /* Singapore */
403 { 216, L"SO", L"SOM", 47603, 706 }, /* Somalia */
404 { 217, L"ES", L"ESP", 47610, 724 }, /* Spain */
405 { 218, L"LC", L"LCA", 10039880, 662 }, /* St. Lucia */
406 { 219, L"SD", L"SDN", 42487, 736 }, /* Sudan */
407 { 220, L"SJ", L"SJM", 10039882, 744 }, /* Svalbard */
408 { 221, L"SE", L"SWE", 10039882, 752 }, /* Sweden */
409 { 222, L"SY", L"SYR", 47611, 760 }, /* Syria */
410 { 223, L"CH", L"CHE", 10210824, 756 }, /* Switzerland */
411 { 224, L"AE", L"ARE", 47611, 784 }, /* United Arab Emirates */
412 { 225, L"TT", L"TTO", 10039880, 780 }, /* Trinidad and Tobago */
413 { 227, L"TH", L"THA", 47599, 764 }, /* Thailand */
414 { 228, L"TJ", L"TJK", 47590, 762 }, /* Tajikistan */
415 { 231, L"TO", L"TON", 26286, 776 }, /* Tonga */
416 { 232, L"TG", L"TGO", 42483, 768 }, /* Togo */
417 { 233, L"ST", L"STP", 42484, 678 }, /* São Tomé and Príncipe */
418 { 234, L"TN", L"TUN", 42487, 788 }, /* Tunisia */
419 { 235, L"TR", L"TUR", 47611, 792 }, /* Turkey */
420 { 236, L"TV", L"TUV", 26286, 798 }, /* Tuvalu */
421 { 237, L"TW", L"TWN", 47600, 158 }, /* Taiwan */
422 { 238, L"TM", L"TKM", 47590, 795 }, /* Turkmenistan */
423 { 239, L"TZ", L"TZA", 47603, 834 }, /* Tanzania */
424 { 240, L"UG", L"UGA", 47603, 800 }, /* Uganda */
425 { 241, L"UA", L"UKR", 47609, 804 }, /* Ukraine */
426 { 242, L"GB", L"GBR", 10039882, 826 }, /* United Kingdom */
427 { 244, L"US", L"USA", 23581, 840 }, /* United States */
428 { 245, L"BF", L"BFA", 42483, 854 }, /* Burkina Faso */
429 { 246, L"UY", L"URY", 31396, 858 }, /* Uruguay */
430 { 247, L"UZ", L"UZB", 47590, 860 }, /* Uzbekistan */
431 { 248, L"VC", L"VCT", 10039880, 670 }, /* St. Vincent and the Grenadines */
432 { 249, L"VE", L"VEN", 31396, 862 }, /* Bolivarian Republic of Venezuela */
433 { 251, L"VN", L"VNM", 47599, 704 }, /* Vietnam */
434 { 252, L"VI", L"VIR", 10039880, 850 }, /* Virgin Islands */
435 { 253, L"VA", L"VAT", 47610, 336 }, /* Vatican City */
436 { 254, L"NA", L"NAM", 10039883, 516 }, /* Namibia */
437 { 257, L"EH", L"ESH", 42487, 732 }, /* Western Sahara (disputed) */
438 { 258, L"XX", L"XX", 161832256 }, /* Wake Island */
439 { 259, L"WS", L"WSM", 26286, 882 }, /* Samoa */
440 { 260, L"SZ", L"SWZ", 10039883, 748 }, /* Swaziland */
441 { 261, L"YE", L"YEM", 47611, 887 }, /* Yemen */
442 { 263, L"ZM", L"ZMB", 47603, 894 }, /* Zambia */
443 { 264, L"ZW", L"ZWE", 47603, 716 }, /* Zimbabwe */
444 { 269, L"CS", L"SCG", 47610, 891 }, /* Serbia and Montenegro (Former) */
445 { 270, L"ME", L"MNE", 47610, 499 }, /* Montenegro */
446 { 271, L"RS", L"SRB", 47610, 688 }, /* Serbia */
447 { 273, L"CW", L"CUW", 10039880, 531 }, /* Curaçao */
448 { 276, L"SS", L"SSD", 42487, 728 }, /* South Sudan */
449 { 300, L"AI", L"AIA", 10039880, 660 }, /* Anguilla */
450 { 301, L"AQ", L"ATA", 39070, 10 }, /* Antarctica */
451 { 302, L"AW", L"ABW", 10039880, 533 }, /* Aruba */
452 { 303, L"XX", L"XX", 343 }, /* Ascension Island */
453 { 304, L"XX", L"XX", 10210825 }, /* Ashmore and Cartier Islands */
454 { 305, L"XX", L"XX", 161832256 }, /* Baker Island */
455 { 306, L"BV", L"BVT", 39070, 74 }, /* Bouvet Island */
456 { 307, L"KY", L"CYM", 10039880, 136 }, /* Cayman Islands */
457 { 308, L"XX", L"XX", 10210824, 830, LOCATION_BOTH }, /* Channel Islands */
458 { 309, L"CX", L"CXR", 12, 162 }, /* Christmas Island */
459 { 310, L"XX", L"XX", 27114 }, /* Clipperton Island */
460 { 311, L"CC", L"CCK", 10210825, 166 }, /* Cocos (Keeling) Islands */
461 { 312, L"CK", L"COK", 26286, 184 }, /* Cook Islands */
462 { 313, L"XX", L"XX", 10210825 }, /* Coral Sea Islands */
463 { 314, L"XX", L"XX", 114 }, /* Diego Garcia */
464 { 315, L"FK", L"FLK", 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
465 { 317, L"GF", L"GUF", 31396, 254 }, /* French Guiana */
466 { 318, L"PF", L"PYF", 26286, 258 }, /* French Polynesia */
467 { 319, L"TF", L"ATF", 39070, 260 }, /* French Southern and Antarctic Lands */
468 { 321, L"GP", L"GLP", 10039880, 312 }, /* Guadeloupe */
469 { 322, L"GU", L"GUM", 21206, 316 }, /* Guam */
470 { 323, L"XX", L"XX", 39070 }, /* Guantanamo Bay */
471 { 324, L"GG", L"GGY", 308, 831 }, /* Guernsey */
472 { 325, L"HM", L"HMD", 39070, 334 }, /* Heard Island and McDonald Islands */
473 { 326, L"XX", L"XX", 161832256 }, /* Howland Island */
474 { 327, L"XX", L"XX", 161832256 }, /* Jarvis Island */
475 { 328, L"JE", L"JEY", 308, 832 }, /* Jersey */
476 { 329, L"XX", L"XX", 161832256 }, /* Kingman Reef */
477 { 330, L"MQ", L"MTQ", 10039880, 474 }, /* Martinique */
478 { 331, L"YT", L"MYT", 47603, 175 }, /* Mayotte */
479 { 332, L"MS", L"MSR", 10039880, 500 }, /* Montserrat */
480 { 333, L"AN", L"ANT", 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
481 { 334, L"NC", L"NCL", 20900, 540 }, /* New Caledonia */
482 { 335, L"NU", L"NIU", 26286, 570 }, /* Niue */
483 { 336, L"NF", L"NFK", 10210825, 574 }, /* Norfolk Island */
484 { 337, L"MP", L"MNP", 21206, 580 }, /* Northern Mariana Islands */
485 { 338, L"XX", L"XX", 161832256 }, /* Palmyra Atoll */
486 { 339, L"PN", L"PCN", 26286, 612 }, /* Pitcairn Islands */
487 { 340, L"XX", L"XX", 337 }, /* Rota Island */
488 { 341, L"XX", L"XX", 337 }, /* Saipan */
489 { 342, L"GS", L"SGS", 39070, 239 }, /* South Georgia and the South Sandwich Islands */
490 { 343, L"SH", L"SHN", 42483, 654 }, /* St. Helena */
491 { 346, L"XX", L"XX", 337 }, /* Tinian Island */
492 { 347, L"TK", L"TKL", 26286, 772 }, /* Tokelau */
493 { 348, L"XX", L"XX", 343 }, /* Tristan da Cunha */
494 { 349, L"TC", L"TCA", 10039880, 796 }, /* Turks and Caicos Islands */
495 { 351, L"VG", L"VGB", 10039880, 92 }, /* Virgin Islands, British */
496 { 352, L"WF", L"WLF", 26286, 876 }, /* Wallis and Futuna */
497 { 742, L"XX", L"XX", 39070, 2, LOCATION_REGION }, /* Africa */
498 { 2129, L"XX", L"XX", 39070, 142, LOCATION_REGION }, /* Asia */
499 { 10541, L"XX", L"XX", 39070, 150, LOCATION_REGION }, /* Europe */
500 { 15126, L"IM", L"IMN", 10039882, 833 }, /* Man, Isle of */
501 { 19618, L"MK", L"MKD", 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
502 { 20900, L"XX", L"XX", 27114, 54, LOCATION_REGION }, /* Melanesia */
503 { 21206, L"XX", L"XX", 27114, 57, LOCATION_REGION }, /* Micronesia */
504 { 21242, L"XX", L"XX", 161832256 }, /* Midway Islands */
505 { 23581, L"XX", L"XX", 10026358, 21, LOCATION_REGION }, /* Northern America */
506 { 26286, L"XX", L"XX", 27114, 61, LOCATION_REGION }, /* Polynesia */
507 { 27082, L"XX", L"XX", 161832257, 13, LOCATION_REGION }, /* Central America */
508 { 27114, L"XX", L"XX", 39070, 9, LOCATION_REGION }, /* Oceania */
509 { 30967, L"SX", L"SXM", 10039880, 534 }, /* Sint Maarten (Dutch part) */
510 { 31396, L"XX", L"XX", 161832257, 5, LOCATION_REGION }, /* South America */
511 { 31706, L"MF", L"MAF", 10039880, 663 }, /* Saint Martin (French part) */
512 { 39070, L"XX", L"XX", 39070, 1, LOCATION_REGION }, /* World */
513 { 42483, L"XX", L"XX", 742, 11, LOCATION_REGION }, /* Western Africa */
514 { 42484, L"XX", L"XX", 742, 17, LOCATION_REGION }, /* Middle Africa */
515 { 42487, L"XX", L"XX", 742, 15, LOCATION_REGION }, /* Northern Africa */
516 { 47590, L"XX", L"XX", 2129, 143, LOCATION_REGION }, /* Central Asia */
517 { 47599, L"XX", L"XX", 2129, 35, LOCATION_REGION }, /* South-Eastern Asia */
518 { 47600, L"XX", L"XX", 2129, 30, LOCATION_REGION }, /* Eastern Asia */
519 { 47603, L"XX", L"XX", 742, 14, LOCATION_REGION }, /* Eastern Africa */
520 { 47609, L"XX", L"XX", 10541, 151, LOCATION_REGION }, /* Eastern Europe */
521 { 47610, L"XX", L"XX", 10541, 39, LOCATION_REGION }, /* Southern Europe */
522 { 47611, L"XX", L"XX", 2129, 145, LOCATION_REGION }, /* Middle East */
523 { 47614, L"XX", L"XX", 2129, 34, LOCATION_REGION }, /* Southern Asia */
524 { 7299303, L"TL", L"TLS", 47599, 626 }, /* Democratic Republic of Timor-Leste */
525 { 9914689, L"XK", L"XKS", 47610, 906 }, /* Kosovo */
526 { 10026358, L"XX", L"XX", 39070, 19, LOCATION_REGION }, /* Americas */
527 { 10028789, L"AX", L"ALA", 10039882, 248 }, /* Åland Islands */
528 { 10039880, L"XX", L"XX", 161832257, 29, LOCATION_REGION }, /* Caribbean */
529 { 10039882, L"XX", L"XX", 10541, 154, LOCATION_REGION }, /* Northern Europe */
530 { 10039883, L"XX", L"XX", 742, 18, LOCATION_REGION }, /* Southern Africa */
531 { 10210824, L"XX", L"XX", 10541, 155, LOCATION_REGION }, /* Western Europe */
532 { 10210825, L"XX", L"XX", 27114, 53, LOCATION_REGION }, /* Australia and New Zealand */
533 { 161832015, L"BL", L"BLM", 10039880, 652 }, /* Saint Barthélemy */
534 { 161832256, L"UM", L"UMI", 27114, 581 }, /* U.S. Minor Outlying Islands */
535 { 161832257, L"XX", L"XX", 10026358, 419, LOCATION_REGION }, /* Latin America and the Caribbean */
538 /* NLS normalization file */
539 struct norm_table
541 WCHAR name[13]; /* 00 file name */
542 USHORT checksum[3]; /* 1a checksum? */
543 USHORT version[4]; /* 20 Unicode version */
544 USHORT form; /* 28 normalization form */
545 USHORT len_factor; /* 2a factor for length estimates */
546 USHORT unknown1; /* 2c */
547 USHORT decomp_size; /* 2e decomposition hash size */
548 USHORT comp_size; /* 30 composition hash size */
549 USHORT unknown2; /* 32 */
550 USHORT classes; /* 34 combining classes table offset */
551 USHORT props_level1; /* 36 char properties table level 1 offset */
552 USHORT props_level2; /* 38 char properties table level 2 offset */
553 USHORT decomp_hash; /* 3a decomposition hash table offset */
554 USHORT decomp_map; /* 3c decomposition character map table offset */
555 USHORT decomp_seq; /* 3e decomposition character sequences offset */
556 USHORT comp_hash; /* 40 composition hash table offset */
557 USHORT comp_seq; /* 42 composition character sequences offset */
558 /* BYTE[] combining class values */
559 /* BYTE[0x2200] char properties index level 1 */
560 /* BYTE[] char properties index level 2 */
561 /* WORD[] decomposition hash table */
562 /* WORD[] decomposition character map */
563 /* WORD[] decomposition character sequences */
564 /* WORD[] composition hash table */
565 /* WORD[] composition character sequences */
568 static NLSTABLEINFO nls_info;
569 static UINT mac_cp = 10000;
570 static HKEY intl_key;
571 static HKEY nls_key;
572 static HKEY tz_key;
574 static CPTABLEINFO codepages[128];
575 static unsigned int nb_codepages;
577 static struct norm_table *norm_info;
579 struct sortguid
581 GUID id; /* sort GUID */
582 DWORD flags; /* flags */
583 DWORD compr; /* offset to compression table */
584 DWORD except; /* exception table offset in sortkey table */
585 DWORD ling_except; /* exception table offset for linguistic casing */
586 DWORD casemap; /* linguistic casemap table offset */
589 #define FLAG_HAS_3_BYTE_WEIGHTS 0x01
590 #define FLAG_REVERSEDIACRITICS 0x10
591 #define FLAG_DOUBLECOMPRESSION 0x20
592 #define FLAG_INVERSECASING 0x40
594 static const struct sortguid *current_locale_sort;
596 static const GUID default_sort_guid = { 0x00000001, 0x57ee, 0x1e5c, { 0x00, 0xb4, 0xd0, 0x00, 0x0b, 0xb1, 0xe1, 0x1e }};
598 static struct
600 DWORD *keys; /* sortkey table, indexed by char */
601 USHORT *casemap; /* casemap table, in l_intl.nls format */
602 WORD *ctypes; /* CT_CTYPE1,2,3 values */
603 BYTE *ctype_idx; /* index to map char to ctypes array entry */
604 DWORD version; /* NLS version */
605 DWORD guid_count; /* number of sort GUIDs */
606 struct sortguid *guids; /* table of sort GUIDs */
607 } sort;
609 static CRITICAL_SECTION locale_section;
610 static CRITICAL_SECTION_DEBUG critsect_debug =
612 0, 0, &locale_section,
613 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
614 0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") }
616 static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
619 static void init_sortkeys( DWORD *ptr )
621 WORD *ctype;
622 DWORD *table;
624 sort.keys = (DWORD *)((char *)ptr + ptr[0]);
625 sort.casemap = (USHORT *)((char *)ptr + ptr[1]);
627 ctype = (WORD *)((char *)ptr + ptr[2]);
628 sort.ctypes = ctype + 2;
629 sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2;
631 table = (DWORD *)((char *)ptr + ptr[3]);
632 sort.version = table[0];
633 sort.guid_count = table[1];
634 sort.guids = (struct sortguid *)(table + 2);
638 static const struct sortguid *find_sortguid( const GUID *guid )
640 int pos, ret, min = 0, max = sort.guid_count - 1;
642 while (min <= max)
644 pos = (min + max) / 2;
645 ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) );
646 if (!ret) return &sort.guids[pos];
647 if (ret > 0) min = pos + 1;
648 else max = pos - 1;
650 ERR( "no sort found for %s\n", debugstr_guid( guid ));
651 return NULL;
655 static const struct sortguid *get_language_sort( const WCHAR *locale )
657 WCHAR *p, *end, buffer[LOCALE_NAME_MAX_LENGTH], guidstr[39];
658 const struct sortguid *ret;
659 UNICODE_STRING str;
660 GUID guid;
661 HKEY key = 0;
662 DWORD size, type;
664 if (locale == LOCALE_NAME_USER_DEFAULT)
666 if (current_locale_sort) return current_locale_sort;
667 GetUserDefaultLocaleName( buffer, ARRAY_SIZE( buffer ));
669 else lstrcpynW( buffer, locale, LOCALE_NAME_MAX_LENGTH );
671 if (buffer[0] && !RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key ))
673 for (;;)
675 size = sizeof(guidstr);
676 if (!RegQueryValueExW( key, buffer, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ)
678 RtlInitUnicodeString( &str, guidstr );
679 if (!RtlGUIDFromString( &str, &guid ))
681 ret = find_sortguid( &guid );
682 goto done;
684 break;
686 for (p = end = buffer; *p; p++) if (*p == '-' || *p == '_') end = p;
687 if (end == buffer) break;
688 *end = 0;
691 ret = find_sortguid( &default_sort_guid );
692 done:
693 RegCloseKey( key );
694 return ret;
698 /***********************************************************************
699 * init_locale
701 void init_locale(void)
703 UINT ansi_cp = 0, oem_cp = 0;
704 USHORT *ansi_ptr, *oem_ptr;
705 void *sort_ptr;
706 LCID lcid = GetUserDefaultLCID();
707 WCHAR bufferW[80];
708 DYNAMIC_TIME_ZONE_INFORMATION timezone;
709 GEOID geoid = GEOID_NOT_AVAILABLE;
710 DWORD count, dispos, i;
711 SIZE_T size;
712 HKEY hkey;
714 kernel32_handle = GetModuleHandleW( L"kernel32.dll" );
716 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
717 (WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) );
718 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
719 (WCHAR *)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
720 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
721 (WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
723 NtGetNlsSectionPtr( 9, 0, NULL, &sort_ptr, &size );
724 NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size );
725 init_sortkeys( sort_ptr );
727 if (!ansi_cp || NtGetNlsSectionPtr( 11, ansi_cp, NULL, (void **)&ansi_ptr, &size ))
728 NtGetNlsSectionPtr( 11, 1252, NULL, (void **)&ansi_ptr, &size );
729 if (!oem_cp || NtGetNlsSectionPtr( 11, oem_cp, 0, (void **)&oem_ptr, &size ))
730 NtGetNlsSectionPtr( 11, 437, NULL, (void **)&oem_ptr, &size );
731 NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr;
732 NtCurrentTeb()->Peb->OemCodePageData = oem_ptr;
733 NtCurrentTeb()->Peb->UnicodeCaseTableData = sort.casemap;
734 RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info );
735 RtlResetRtlTranslations( &nls_info );
737 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
738 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
739 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
740 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tz_key, NULL );
741 RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International",
742 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL );
744 current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT );
746 if (GetDynamicTimeZoneInformation( &timezone ) != TIME_ZONE_ID_INVALID &&
747 !RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\TimeZoneInformation",
748 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
750 RegSetValueExW( hkey, L"StandardName", 0, REG_SZ, (BYTE *)timezone.StandardName,
751 (lstrlenW(timezone.StandardName) + 1) * sizeof(WCHAR) );
752 RegSetValueExW( hkey, L"TimeZoneKeyName", 0, REG_SZ, (BYTE *)timezone.TimeZoneKeyName,
753 (lstrlenW(timezone.TimeZoneKeyName) + 1) * sizeof(WCHAR) );
754 RegCloseKey( hkey );
757 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, &dispos ))
759 if (dispos == REG_CREATED_NEW_KEY)
761 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IGEOID | LOCALE_RETURN_NUMBER,
762 (WCHAR *)&geoid, sizeof(geoid) / sizeof(WCHAR) );
763 SetUserGeoID( geoid );
765 RegCloseKey( hkey );
768 /* Update registry contents if the user locale has changed.
769 * This simulates the action of the Windows control panel. */
771 count = sizeof(bufferW);
772 if (!RegQueryValueExW( intl_key, L"Locale", NULL, NULL, (BYTE *)bufferW, &count ))
774 if (wcstoul( bufferW, NULL, 16 ) == lcid) return; /* already set correctly */
775 TRACE( "updating registry, locale changed %s -> %08x\n", debugstr_w(bufferW), lcid );
777 else TRACE( "updating registry, locale changed none -> %08x\n", lcid );
778 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%08x", lcid );
779 RegSetValueExW( intl_key, L"Locale", 0, REG_SZ,
780 (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
782 for (i = 0; i < ARRAY_SIZE(registry_values); i++)
784 GetLocaleInfoW( LOCALE_USER_DEFAULT, registry_values[i].lctype | LOCALE_NOUSEROVERRIDE,
785 bufferW, ARRAY_SIZE( bufferW ));
786 RegSetValueExW( intl_key, registry_values[i].name, 0, REG_SZ,
787 (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
790 if (geoid == GEOID_NOT_AVAILABLE)
792 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IGEOID | LOCALE_RETURN_NUMBER,
793 (WCHAR *)&geoid, sizeof(geoid) / sizeof(WCHAR) );
794 SetUserGeoID( geoid );
797 if (!RegCreateKeyExW( nls_key, L"Codepage",
798 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
800 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", ansi_cp );
801 RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
802 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", oem_cp );
803 RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
804 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", mac_cp );
805 RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
806 RegCloseKey( hkey );
811 static inline USHORT get_table_entry( const USHORT *table, WCHAR ch )
813 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
817 static inline WCHAR casemap( const USHORT *table, WCHAR ch )
819 return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
823 static inline WORD get_char_type( DWORD type, WCHAR ch )
825 const BYTE *ptr = sort.ctype_idx + ((const WORD *)sort.ctype_idx)[ch >> 8];
826 ptr = sort.ctype_idx + ((const WORD *)ptr)[(ch >> 4) & 0x0f] + (ch & 0x0f);
827 return sort.ctypes[*ptr * 3 + type / 2];
831 static BYTE rol( BYTE val, BYTE count )
833 return (val << count) | (val >> (8 - count));
837 static BYTE get_char_props( const struct norm_table *info, unsigned int ch )
839 const BYTE *level1 = (const BYTE *)((const USHORT *)info + info->props_level1);
840 const BYTE *level2 = (const BYTE *)((const USHORT *)info + info->props_level2);
841 BYTE off = level1[ch / 128];
843 if (!off || off >= 0xfb) return rol( off, 5 );
844 return level2[(off - 1) * 128 + ch % 128];
848 static const WCHAR *get_decomposition( WCHAR ch, unsigned int *ret_len )
850 const struct pair { WCHAR src; USHORT dst; } *pairs;
851 const USHORT *hash_table = (const USHORT *)norm_info + norm_info->decomp_hash;
852 const WCHAR *ret;
853 unsigned int i, pos, end, len, hash;
855 *ret_len = 1;
856 hash = ch % norm_info->decomp_size;
857 pos = hash_table[hash];
858 if (pos >> 13)
860 if (get_char_props( norm_info, ch ) != 0xbf) return NULL;
861 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pos & 0x1fff);
862 len = pos >> 13;
864 else
866 pairs = (const struct pair *)((const USHORT *)norm_info + norm_info->decomp_map);
868 /* find the end of the hash bucket */
869 for (i = hash + 1; i < norm_info->decomp_size; i++) if (!(hash_table[i] >> 13)) break;
870 if (i < norm_info->decomp_size) end = hash_table[i];
871 else for (end = pos; pairs[end].src; end++) ;
873 for ( ; pos < end; pos++)
875 if (pairs[pos].src != (WCHAR)ch) continue;
876 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pairs[pos].dst & 0x1fff);
877 len = pairs[pos].dst >> 13;
878 break;
880 if (pos >= end) return NULL;
883 if (len == 7) while (ret[len]) len++;
884 if (!ret[0]) len = 0; /* ignored char */
885 *ret_len = len;
886 return ret;
890 static WCHAR compose_chars( WCHAR ch1, WCHAR ch2 )
892 const USHORT *table = (const USHORT *)norm_info + norm_info->comp_hash;
893 const WCHAR *chars = (const USHORT *)norm_info + norm_info->comp_seq;
894 unsigned int hash, start, end, i;
895 WCHAR ch[3];
897 hash = (ch1 + 95 * ch2) % norm_info->comp_size;
898 start = table[hash];
899 end = table[hash + 1];
900 while (start < end)
902 for (i = 0; i < 3; i++, start++)
904 ch[i] = chars[start];
905 if (IS_HIGH_SURROGATE( ch[i] )) start++;
907 if (ch[0] == ch1 && ch[1] == ch2) return ch[2];
909 return 0;
913 static UINT get_lcid_codepage( LCID lcid, ULONG flags )
915 UINT ret = GetACP();
917 if (!(flags & LOCALE_USE_CP_ACP) && lcid != GetSystemDefaultLCID())
918 GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
919 (WCHAR *)&ret, sizeof(ret)/sizeof(WCHAR) );
920 return ret;
924 static BOOL is_genitive_name_supported( LCTYPE lctype )
926 switch (LOWORD(lctype))
928 case LOCALE_SMONTHNAME1:
929 case LOCALE_SMONTHNAME2:
930 case LOCALE_SMONTHNAME3:
931 case LOCALE_SMONTHNAME4:
932 case LOCALE_SMONTHNAME5:
933 case LOCALE_SMONTHNAME6:
934 case LOCALE_SMONTHNAME7:
935 case LOCALE_SMONTHNAME8:
936 case LOCALE_SMONTHNAME9:
937 case LOCALE_SMONTHNAME10:
938 case LOCALE_SMONTHNAME11:
939 case LOCALE_SMONTHNAME12:
940 case LOCALE_SMONTHNAME13:
941 return TRUE;
942 default:
943 return FALSE;
948 static int get_value_base_by_lctype( LCTYPE lctype )
950 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
954 static const struct registry_value *get_locale_registry_value( DWORD lctype )
956 unsigned int i;
958 for (i = 0; i < ARRAY_SIZE( registry_values ); i++)
959 if (registry_values[i].lctype == lctype) return &registry_values[i];
960 return NULL;
964 static INT get_registry_locale_info( const struct registry_value *registry_value, LPWSTR buffer, INT len )
966 DWORD size, index = registry_value - registry_values;
967 INT ret;
969 RtlEnterCriticalSection( &locale_section );
971 if (!registry_cache[index])
973 size = len * sizeof(WCHAR);
974 ret = RegQueryValueExW( intl_key, registry_value->name, NULL, NULL, (BYTE *)buffer, &size );
975 if (!ret)
977 if (buffer && (registry_cache[index] = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) )))
979 memcpy( registry_cache[index], buffer, size );
980 registry_cache[index][size / sizeof(WCHAR)] = 0;
982 RtlLeaveCriticalSection( &locale_section );
983 return size / sizeof(WCHAR);
985 else
987 RtlLeaveCriticalSection( &locale_section );
988 if (ret == ERROR_FILE_NOT_FOUND) return -1;
989 if (ret == ERROR_MORE_DATA) SetLastError( ERROR_INSUFFICIENT_BUFFER );
990 else SetLastError( ret );
991 return 0;
995 ret = lstrlenW( registry_cache[index] ) + 1;
996 if (buffer)
998 if (ret > len)
1000 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1001 ret = 0;
1003 else lstrcpyW( buffer, registry_cache[index] );
1005 RtlLeaveCriticalSection( &locale_section );
1006 return ret;
1010 static const CPTABLEINFO *get_codepage_table( UINT codepage )
1012 unsigned int i;
1013 USHORT *ptr;
1014 SIZE_T size;
1016 switch (codepage)
1018 case CP_ACP:
1019 return &nls_info.AnsiTableInfo;
1020 case CP_OEMCP:
1021 return &nls_info.OemTableInfo;
1022 case CP_MACCP:
1023 codepage = mac_cp;
1024 break;
1025 case CP_THREAD_ACP:
1026 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return &nls_info.AnsiTableInfo;
1027 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 );
1028 if (!codepage) return &nls_info.AnsiTableInfo;
1029 break;
1030 default:
1031 if (codepage == nls_info.AnsiTableInfo.CodePage) return &nls_info.AnsiTableInfo;
1032 if (codepage == nls_info.OemTableInfo.CodePage) return &nls_info.OemTableInfo;
1033 break;
1036 RtlEnterCriticalSection( &locale_section );
1038 for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done;
1040 if (i == ARRAY_SIZE( codepages ))
1042 RtlLeaveCriticalSection( &locale_section );
1043 ERR( "too many codepages\n" );
1044 return NULL;
1046 if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size ))
1048 RtlLeaveCriticalSection( &locale_section );
1049 SetLastError( ERROR_INVALID_PARAMETER );
1050 return NULL;
1052 RtlInitCodePageTable( ptr, &codepages[i] );
1053 nb_codepages++;
1054 done:
1055 RtlLeaveCriticalSection( &locale_section );
1056 return &codepages[i];
1060 static const WCHAR *get_ligature( WCHAR wc )
1062 int low = 0, high = ARRAY_SIZE( ligatures ) -1;
1063 while (low <= high)
1065 int pos = (low + high) / 2;
1066 if (ligatures[pos][0] < wc) low = pos + 1;
1067 else if (ligatures[pos][0] > wc) high = pos - 1;
1068 else return ligatures[pos] + 1;
1070 return NULL;
1074 static NTSTATUS expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1076 int i, len, pos = 0;
1077 NTSTATUS ret = STATUS_SUCCESS;
1078 const WCHAR *expand;
1080 for (i = 0; i < srclen; i++)
1082 if (!(expand = get_ligature( src[i] )))
1084 expand = src + i;
1085 len = 1;
1087 else len = lstrlenW( expand );
1089 if (*dstlen && ret == STATUS_SUCCESS)
1091 if (pos + len <= *dstlen) memcpy( dst + pos, expand, len * sizeof(WCHAR) );
1092 else ret = STATUS_BUFFER_TOO_SMALL;
1094 pos += len;
1096 *dstlen = pos;
1097 return ret;
1101 static NTSTATUS fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1103 extern const WCHAR wine_digitmap[] DECLSPEC_HIDDEN;
1104 int i, len = *dstlen;
1106 *dstlen = srclen;
1107 if (!len) return STATUS_SUCCESS;
1108 if (srclen > len) return STATUS_BUFFER_TOO_SMALL;
1109 for (i = 0; i < srclen; i++)
1111 WCHAR digit = get_table_entry( wine_digitmap, src[i] );
1112 dst[i] = digit ? digit : src[i];
1114 return STATUS_SUCCESS;
1118 static NTSTATUS fold_string( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1120 NTSTATUS ret;
1121 WCHAR *tmp;
1123 switch (flags)
1125 case MAP_PRECOMPOSED:
1126 return RtlNormalizeString( NormalizationC, src, srclen, dst, dstlen );
1127 case MAP_FOLDCZONE:
1128 case MAP_PRECOMPOSED | MAP_FOLDCZONE:
1129 return RtlNormalizeString( NormalizationKC, src, srclen, dst, dstlen );
1130 case MAP_COMPOSITE:
1131 return RtlNormalizeString( NormalizationD, src, srclen, dst, dstlen );
1132 case MAP_COMPOSITE | MAP_FOLDCZONE:
1133 return RtlNormalizeString( NormalizationKD, src, srclen, dst, dstlen );
1134 case MAP_FOLDDIGITS:
1135 return fold_digits( src, srclen, dst, dstlen );
1136 case MAP_EXPAND_LIGATURES:
1137 case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
1138 return expand_ligatures( src, srclen, dst, dstlen );
1139 case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
1140 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1141 return STATUS_NO_MEMORY;
1142 fold_digits( src, srclen, tmp, &srclen );
1143 ret = RtlNormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
1144 break;
1145 case MAP_FOLDDIGITS | MAP_FOLDCZONE:
1146 case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
1147 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1148 return STATUS_NO_MEMORY;
1149 fold_digits( src, srclen, tmp, &srclen );
1150 ret = RtlNormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
1151 break;
1152 case MAP_FOLDDIGITS | MAP_COMPOSITE:
1153 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1154 return STATUS_NO_MEMORY;
1155 fold_digits( src, srclen, tmp, &srclen );
1156 ret = RtlNormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
1157 break;
1158 case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
1159 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1160 return STATUS_NO_MEMORY;
1161 fold_digits( src, srclen, tmp, &srclen );
1162 ret = RtlNormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
1163 break;
1164 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
1165 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
1166 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1167 return STATUS_NO_MEMORY;
1168 fold_digits( src, srclen, tmp, &srclen );
1169 ret = expand_ligatures( tmp, srclen, dst, dstlen );
1170 break;
1171 default:
1172 return STATUS_INVALID_PARAMETER_1;
1174 RtlFreeHeap( GetProcessHeap(), 0, tmp );
1175 return ret;
1179 static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1181 int len, i;
1183 if (flags)
1185 SetLastError( ERROR_INVALID_FLAGS );
1186 return 0;
1188 if (!dstlen) return srclen;
1189 len = min( srclen, dstlen );
1190 for (i = 0; i < len; i++)
1192 unsigned char c = src[i];
1193 dst[i] = (c < 0x20) ? c : c + 0xf000;
1195 if (len < srclen)
1197 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1198 return 0;
1200 return len;
1204 static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1206 static const signed char base64_decoding_table[] =
1208 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1209 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1210 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1211 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1212 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1213 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1214 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1215 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1218 const char *source_end = src + srclen;
1219 int offset = 0, pos = 0;
1220 DWORD byte_pair = 0;
1222 if (flags)
1224 SetLastError( ERROR_INVALID_FLAGS );
1225 return 0;
1227 #define OUTPUT(ch) \
1228 do { \
1229 if (dstlen > 0) \
1231 if (pos >= dstlen) goto overflow; \
1232 dst[pos] = (ch); \
1234 pos++; \
1235 } while(0)
1237 while (src < source_end)
1239 if (*src == '+')
1241 src++;
1242 if (src >= source_end) break;
1243 if (*src == '-')
1245 /* just a plus sign escaped as +- */
1246 OUTPUT( '+' );
1247 src++;
1248 continue;
1253 signed char sextet = *src;
1254 if (sextet == '-')
1256 /* skip over the dash and end base64 decoding
1257 * the current, unfinished byte pair is discarded */
1258 src++;
1259 offset = 0;
1260 break;
1262 if (sextet < 0)
1264 /* the next character of src is < 0 and therefore not part of a base64 sequence
1265 * the current, unfinished byte pair is NOT discarded in this case
1266 * this is probably a bug in Windows */
1267 break;
1269 sextet = base64_decoding_table[sextet];
1270 if (sextet == -1)
1272 /* -1 means that the next character of src is not part of a base64 sequence
1273 * in other words, all sextets in this base64 sequence have been processed
1274 * the current, unfinished byte pair is discarded */
1275 offset = 0;
1276 break;
1279 byte_pair = (byte_pair << 6) | sextet;
1280 offset += 6;
1281 if (offset >= 16)
1283 /* this byte pair is done */
1284 OUTPUT( byte_pair >> (offset - 16) );
1285 offset -= 16;
1287 src++;
1289 while (src < source_end);
1291 else
1293 OUTPUT( (unsigned char)*src );
1294 src++;
1297 return pos;
1299 overflow:
1300 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1301 return 0;
1302 #undef OUTPUT
1306 static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1308 DWORD reslen;
1309 NTSTATUS status;
1311 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
1313 SetLastError( ERROR_INVALID_FLAGS );
1314 return 0;
1316 if (!dstlen) dst = NULL;
1317 status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen );
1318 if (status == STATUS_SOME_NOT_MAPPED)
1320 if (flags & MB_ERR_INVALID_CHARS)
1322 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
1323 return 0;
1326 else if (!set_ntstatus( status )) reslen = 0;
1328 return reslen / sizeof(WCHAR);
1332 static inline int is_private_use_area_char( WCHAR code )
1334 return (code >= 0xe000 && code <= 0xf8ff);
1338 static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen )
1340 if (info->DBCSOffsets)
1342 for ( ; srclen; src++, srclen-- )
1344 USHORT off = info->DBCSOffsets[*src];
1345 if (off)
1347 if (srclen == 1) break; /* partial char, error */
1348 if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar &&
1349 ((src[0] << 8) | src[1]) != info->TransUniDefaultChar) break;
1350 src++;
1351 srclen--;
1352 continue;
1354 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
1355 break;
1356 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
1359 else
1361 for ( ; srclen; src++, srclen-- )
1363 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
1364 break;
1365 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
1368 return !!srclen;
1373 static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen,
1374 WCHAR *dst, int dstlen )
1376 WCHAR ch;
1377 USHORT off;
1378 int len;
1379 const WCHAR *decomp;
1380 unsigned int decomp_len;
1382 if (info->DBCSOffsets)
1384 if (!dstlen) /* compute length */
1386 for (len = 0; srclen; srclen--, src++, len += decomp_len)
1388 if ((off = info->DBCSOffsets[*src]))
1390 if (srclen > 1 && src[1])
1392 src++;
1393 srclen--;
1394 ch = info->DBCSOffsets[off + *src];
1396 else ch = info->UniDefaultChar;
1398 else ch = info->MultiByteTable[*src];
1399 get_decomposition( ch, &decomp_len );
1401 return len;
1404 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
1406 if ((off = info->DBCSOffsets[*src]))
1408 if (srclen > 1 && src[1])
1410 src++;
1411 srclen--;
1412 ch = info->DBCSOffsets[off + *src];
1414 else ch = info->UniDefaultChar;
1416 else ch = info->MultiByteTable[*src];
1418 if ((decomp = get_decomposition( ch, &decomp_len )))
1420 if (len < decomp_len) break;
1421 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
1423 else *dst = ch;
1426 else
1428 if (!dstlen) /* compute length */
1430 for (len = 0; srclen; srclen--, src++, len += decomp_len)
1431 get_decomposition( info->MultiByteTable[*src], &decomp_len );
1432 return len;
1435 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
1437 ch = info->MultiByteTable[*src];
1438 if ((decomp = get_decomposition( ch, &decomp_len )))
1440 if (len < decomp_len) break;
1441 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
1443 else *dst = ch;
1447 if (srclen)
1449 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1450 return 0;
1452 return dstlen - len;
1456 static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
1457 WCHAR *dst, int dstlen )
1459 const USHORT *table = info->MultiByteTable;
1460 int ret = srclen;
1462 if (!dstlen) return srclen;
1464 if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */
1466 srclen = dstlen;
1467 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1468 ret = 0;
1471 while (srclen >= 16)
1473 dst[0] = table[src[0]];
1474 dst[1] = table[src[1]];
1475 dst[2] = table[src[2]];
1476 dst[3] = table[src[3]];
1477 dst[4] = table[src[4]];
1478 dst[5] = table[src[5]];
1479 dst[6] = table[src[6]];
1480 dst[7] = table[src[7]];
1481 dst[8] = table[src[8]];
1482 dst[9] = table[src[9]];
1483 dst[10] = table[src[10]];
1484 dst[11] = table[src[11]];
1485 dst[12] = table[src[12]];
1486 dst[13] = table[src[13]];
1487 dst[14] = table[src[14]];
1488 dst[15] = table[src[15]];
1489 src += 16;
1490 dst += 16;
1491 srclen -= 16;
1494 /* now handle the remaining characters */
1495 src += srclen;
1496 dst += srclen;
1497 switch (srclen)
1499 case 15: dst[-15] = table[src[-15]];
1500 case 14: dst[-14] = table[src[-14]];
1501 case 13: dst[-13] = table[src[-13]];
1502 case 12: dst[-12] = table[src[-12]];
1503 case 11: dst[-11] = table[src[-11]];
1504 case 10: dst[-10] = table[src[-10]];
1505 case 9: dst[-9] = table[src[-9]];
1506 case 8: dst[-8] = table[src[-8]];
1507 case 7: dst[-7] = table[src[-7]];
1508 case 6: dst[-6] = table[src[-6]];
1509 case 5: dst[-5] = table[src[-5]];
1510 case 4: dst[-4] = table[src[-4]];
1511 case 3: dst[-3] = table[src[-3]];
1512 case 2: dst[-2] = table[src[-2]];
1513 case 1: dst[-1] = table[src[-1]];
1514 case 0: break;
1516 return ret;
1520 static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
1521 WCHAR *dst, int dstlen )
1523 USHORT off;
1524 int i;
1526 if (!dstlen)
1528 for (i = 0; srclen; i++, src++, srclen--)
1529 if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; }
1530 return i;
1533 for (i = dstlen; srclen && i; i--, srclen--, src++, dst++)
1535 if ((off = info->DBCSOffsets[*src]))
1537 if (srclen > 1 && src[1])
1539 src++;
1540 srclen--;
1541 *dst = info->DBCSOffsets[off + *src];
1543 else *dst = info->UniDefaultChar;
1545 else *dst = info->MultiByteTable[*src];
1547 if (srclen)
1549 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1550 return 0;
1552 return dstlen - i;
1556 static int mbstowcs_codepage( UINT codepage, DWORD flags, const char *src, int srclen,
1557 WCHAR *dst, int dstlen )
1559 CPTABLEINFO local_info;
1560 const CPTABLEINFO *info = get_codepage_table( codepage );
1561 const unsigned char *str = (const unsigned char *)src;
1563 if (!info)
1565 SetLastError( ERROR_INVALID_PARAMETER );
1566 return 0;
1568 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
1570 SetLastError( ERROR_INVALID_FLAGS );
1571 return 0;
1574 if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256)
1576 local_info = *info;
1577 local_info.MultiByteTable += 257;
1578 info = &local_info;
1580 if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen ))
1582 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
1583 return 0;
1586 if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen );
1588 if (info->DBCSOffsets)
1589 return mbstowcs_dbcs( info, str, srclen, dst, dstlen );
1590 else
1591 return mbstowcs_sbcs( info, str, srclen, dst, dstlen );
1595 static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
1596 const char *defchar, BOOL *used )
1598 int len, i;
1600 if (flags)
1602 SetLastError( ERROR_INVALID_FLAGS );
1603 return 0;
1605 if (defchar || used)
1607 SetLastError( ERROR_INVALID_PARAMETER );
1608 return 0;
1610 if (!dstlen) return srclen;
1611 len = min( srclen, dstlen );
1612 for (i = 0; i < len; i++)
1614 if (src[i] < 0x20) dst[i] = src[i];
1615 else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000;
1616 else
1618 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
1619 return 0;
1622 if (srclen > len)
1624 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1625 return 0;
1627 return len;
1631 static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
1632 const char *defchar, BOOL *used )
1634 static const char directly_encodable[] =
1636 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
1637 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1638 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
1639 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
1640 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1641 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1642 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1643 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
1645 #define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
1647 static const char base64_encoding_table[] =
1648 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1650 const WCHAR *source_end = src + srclen;
1651 int pos = 0;
1653 if (defchar || used)
1655 SetLastError( ERROR_INVALID_PARAMETER );
1656 return 0;
1658 if (flags)
1660 SetLastError( ERROR_INVALID_FLAGS );
1661 return 0;
1664 #define OUTPUT(ch) \
1665 do { \
1666 if (dstlen > 0) \
1668 if (pos >= dstlen) goto overflow; \
1669 dst[pos] = (ch); \
1671 pos++; \
1672 } while (0)
1674 while (src < source_end)
1676 if (*src == '+')
1678 OUTPUT( '+' );
1679 OUTPUT( '-' );
1680 src++;
1682 else if (ENCODABLE(*src))
1684 OUTPUT( *src );
1685 src++;
1687 else
1689 unsigned int offset = 0, byte_pair = 0;
1691 OUTPUT( '+' );
1692 while (src < source_end && !ENCODABLE(*src))
1694 byte_pair = (byte_pair << 16) | *src;
1695 offset += 16;
1696 while (offset >= 6)
1698 offset -= 6;
1699 OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] );
1701 src++;
1703 if (offset)
1705 /* Windows won't create a padded base64 character if there's no room for the - sign
1706 * as well ; this is probably a bug in Windows */
1707 if (dstlen > 0 && pos + 1 >= dstlen) goto overflow;
1708 byte_pair <<= (6 - offset);
1709 OUTPUT( base64_encoding_table[byte_pair & 0x3f] );
1711 /* Windows always explicitly terminates the base64 sequence
1712 even though RFC 2152 (page 3, rule 2) does not require this */
1713 OUTPUT( '-' );
1716 return pos;
1718 overflow:
1719 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1720 return 0;
1721 #undef OUTPUT
1722 #undef ENCODABLE
1726 static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
1727 const char *defchar, BOOL *used )
1729 DWORD reslen;
1730 NTSTATUS status;
1732 if (defchar || used)
1734 SetLastError( ERROR_INVALID_PARAMETER );
1735 return 0;
1737 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
1738 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
1740 SetLastError( ERROR_INVALID_FLAGS );
1741 return 0;
1743 if (!dstlen) dst = NULL;
1744 status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
1745 if (status == STATUS_SOME_NOT_MAPPED)
1747 if (flags & WC_ERR_INVALID_CHARS)
1749 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
1750 return 0;
1753 else if (!set_ntstatus( status )) reslen = 0;
1754 return reslen;
1758 static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
1759 char *dst, unsigned int dstlen )
1761 const char *table = info->WideCharTable;
1762 int ret = srclen;
1764 if (!dstlen) return srclen;
1766 if (dstlen < srclen)
1768 /* buffer too small: fill it up to dstlen and return error */
1769 srclen = dstlen;
1770 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1771 ret = 0;
1774 while (srclen >= 16)
1776 dst[0] = table[src[0]];
1777 dst[1] = table[src[1]];
1778 dst[2] = table[src[2]];
1779 dst[3] = table[src[3]];
1780 dst[4] = table[src[4]];
1781 dst[5] = table[src[5]];
1782 dst[6] = table[src[6]];
1783 dst[7] = table[src[7]];
1784 dst[8] = table[src[8]];
1785 dst[9] = table[src[9]];
1786 dst[10] = table[src[10]];
1787 dst[11] = table[src[11]];
1788 dst[12] = table[src[12]];
1789 dst[13] = table[src[13]];
1790 dst[14] = table[src[14]];
1791 dst[15] = table[src[15]];
1792 src += 16;
1793 dst += 16;
1794 srclen -= 16;
1797 /* now handle remaining characters */
1798 src += srclen;
1799 dst += srclen;
1800 switch(srclen)
1802 case 15: dst[-15] = table[src[-15]];
1803 case 14: dst[-14] = table[src[-14]];
1804 case 13: dst[-13] = table[src[-13]];
1805 case 12: dst[-12] = table[src[-12]];
1806 case 11: dst[-11] = table[src[-11]];
1807 case 10: dst[-10] = table[src[-10]];
1808 case 9: dst[-9] = table[src[-9]];
1809 case 8: dst[-8] = table[src[-8]];
1810 case 7: dst[-7] = table[src[-7]];
1811 case 6: dst[-6] = table[src[-6]];
1812 case 5: dst[-5] = table[src[-5]];
1813 case 4: dst[-4] = table[src[-4]];
1814 case 3: dst[-3] = table[src[-3]];
1815 case 2: dst[-2] = table[src[-2]];
1816 case 1: dst[-1] = table[src[-1]];
1817 case 0: break;
1819 return ret;
1823 static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
1824 char *dst, unsigned int dstlen )
1826 const USHORT *table = info->WideCharTable;
1827 int i;
1829 if (!dstlen)
1831 for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++;
1832 return i;
1835 for (i = dstlen; srclen && i; i--, srclen--, src++)
1837 if (table[*src] & 0xff00)
1839 if (i == 1) break; /* do not output a partial char */
1840 i--;
1841 *dst++ = table[*src] >> 8;
1843 *dst++ = (char)table[*src];
1845 if (srclen)
1847 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1848 return 0;
1850 return dstlen - i;
1854 static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
1856 const unsigned char *table = info->WideCharTable;
1858 if (wch >= 0x10000) return 0;
1859 if ((flags & WC_NO_BEST_FIT_CHARS) || table[wch] == info->DefaultChar)
1860 return (info->MultiByteTable[table[wch]] == wch);
1861 return 1;
1865 static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
1867 const unsigned short *table = info->WideCharTable;
1868 unsigned short ch;
1870 if (wch >= 0x10000) return 0;
1871 ch = table[wch];
1872 if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
1874 if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch;
1875 return info->MultiByteTable[ch] == wch;
1877 return 1;
1881 static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
1882 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
1884 const char *table = info->WideCharTable;
1885 const char def = defchar ? *defchar : (char)info->DefaultChar;
1886 int i;
1887 BOOL tmp;
1888 WCHAR wch;
1889 unsigned int composed;
1891 if (!used) used = &tmp; /* avoid checking on every char */
1892 *used = FALSE;
1894 if (!dstlen)
1896 for (i = 0; srclen; i++, src++, srclen--)
1898 wch = *src;
1899 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
1901 /* now check if we can use the composed char */
1902 if (is_valid_sbcs_mapping( info, flags, composed ))
1904 /* we have a good mapping, use it */
1905 src++;
1906 srclen--;
1907 continue;
1909 /* no mapping for the composed char, check the other flags */
1910 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
1912 *used = TRUE;
1913 src++; /* skip the non-spacing char */
1914 srclen--;
1915 continue;
1917 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
1919 src++;
1920 srclen--;
1922 /* WC_SEPCHARS is the default */
1924 if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch );
1926 return i;
1929 for (i = dstlen; srclen && i; dst++, i--, src++, srclen--)
1931 wch = *src;
1932 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
1934 /* now check if we can use the composed char */
1935 if (is_valid_sbcs_mapping( info, flags, composed ))
1937 /* we have a good mapping, use it */
1938 *dst = table[composed];
1939 src++;
1940 srclen--;
1941 continue;
1943 /* no mapping for the composed char, check the other flags */
1944 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
1946 *dst = def;
1947 *used = TRUE;
1948 src++; /* skip the non-spacing char */
1949 srclen--;
1950 continue;
1952 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
1954 src++;
1955 srclen--;
1957 /* WC_SEPCHARS is the default */
1960 *dst = table[wch];
1961 if (!is_valid_sbcs_mapping( info, flags, wch ))
1963 *dst = def;
1964 *used = TRUE;
1967 if (srclen)
1969 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1970 return 0;
1972 return dstlen - i;
1976 static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
1977 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
1979 const USHORT *table = info->WideCharTable;
1980 WCHAR wch, defchar_value;
1981 unsigned int composed;
1982 unsigned short res;
1983 BOOL tmp;
1984 int i;
1986 if (!defchar[1]) defchar_value = (unsigned char)defchar[0];
1987 else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1];
1989 if (!used) used = &tmp; /* avoid checking on every char */
1990 *used = FALSE;
1992 if (!dstlen)
1994 if (!defchar && !used && !(flags & WC_COMPOSITECHECK))
1996 for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++;
1997 return i;
1999 for (i = 0; srclen; srclen--, src++, i++)
2001 wch = *src;
2002 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2004 /* now check if we can use the composed char */
2005 if (is_valid_dbcs_mapping( info, flags, composed ))
2007 /* we have a good mapping for the composed char, use it */
2008 res = table[composed];
2009 if (res & 0xff00) i++;
2010 src++;
2011 srclen--;
2012 continue;
2014 /* no mapping for the composed char, check the other flags */
2015 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2017 if (defchar_value & 0xff00) i++;
2018 *used = TRUE;
2019 src++; /* skip the non-spacing char */
2020 srclen--;
2021 continue;
2023 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2025 src++;
2026 srclen--;
2028 /* WC_SEPCHARS is the default */
2031 res = table[wch];
2032 if (!is_valid_dbcs_mapping( info, flags, wch ))
2034 res = defchar_value;
2035 *used = TRUE;
2037 if (res & 0xff00) i++;
2039 return i;
2043 for (i = dstlen; srclen && i; i--, srclen--, src++)
2045 wch = *src;
2046 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2048 /* now check if we can use the composed char */
2049 if (is_valid_dbcs_mapping( info, flags, composed ))
2051 /* we have a good mapping for the composed char, use it */
2052 res = table[composed];
2053 src++;
2054 srclen--;
2055 goto output_char;
2057 /* no mapping for the composed char, check the other flags */
2058 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2060 res = defchar_value;
2061 *used = TRUE;
2062 src++; /* skip the non-spacing char */
2063 srclen--;
2064 goto output_char;
2066 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2068 src++;
2069 srclen--;
2071 /* WC_SEPCHARS is the default */
2074 res = table[wch];
2075 if (!is_valid_dbcs_mapping( info, flags, wch ))
2077 res = defchar_value;
2078 *used = TRUE;
2081 output_char:
2082 if (res & 0xff00)
2084 if (i == 1) break; /* do not output a partial char */
2085 i--;
2086 *dst++ = res >> 8;
2088 *dst++ = (char)res;
2090 if (srclen)
2092 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2093 return 0;
2095 return dstlen - i;
2099 static int wcstombs_codepage( UINT codepage, DWORD flags, const WCHAR *src, int srclen,
2100 char *dst, int dstlen, const char *defchar, BOOL *used )
2102 const CPTABLEINFO *info = get_codepage_table( codepage );
2104 if (!info)
2106 SetLastError( ERROR_INVALID_PARAMETER );
2107 return 0;
2109 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
2110 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
2112 SetLastError( ERROR_INVALID_FLAGS );
2113 return 0;
2115 if (flags || defchar || used)
2117 if (!defchar) defchar = (const char *)&info->DefaultChar;
2118 if (info->DBCSOffsets)
2119 return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
2120 else
2121 return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
2123 if (info->DBCSOffsets)
2124 return wcstombs_dbcs( info, src, srclen, dst, dstlen );
2125 else
2126 return wcstombs_sbcs( info, src, srclen, dst, dstlen );
2130 static int get_sortkey( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen )
2132 WCHAR dummy[4]; /* no decomposition is larger than 4 chars */
2133 int key_len[4];
2134 char *key_ptr[4];
2135 const WCHAR *src_save = src;
2136 int srclen_save = srclen;
2138 key_len[0] = key_len[1] = key_len[2] = key_len[3] = 0;
2139 for (; srclen; srclen--, src++)
2141 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
2142 dummy[0] = *src;
2143 if (decomposed_len)
2145 for (i = 0; i < decomposed_len; i++)
2147 WCHAR wch = dummy[i];
2148 unsigned int ce;
2150 if ((flags & NORM_IGNORESYMBOLS) &&
2151 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
2152 continue;
2154 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
2156 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
2157 if (ce != (unsigned int)-1)
2159 if (ce >> 16) key_len[0] += 2;
2160 if ((ce >> 8) & 0xff) key_len[1]++;
2161 if ((ce >> 4) & 0x0f) key_len[2]++;
2162 if (ce & 1)
2164 if (wch >> 8) key_len[3]++;
2165 key_len[3]++;
2168 else
2170 key_len[0] += 2;
2171 if (wch >> 8) key_len[0]++;
2172 if (wch & 0xff) key_len[0]++;
2178 if (!dstlen) /* compute length */
2179 /* 4 * '\1' + key length */
2180 return key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4;
2182 if (dstlen < key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4 + 1)
2183 return 0; /* overflow */
2185 src = src_save;
2186 srclen = srclen_save;
2188 key_ptr[0] = dst;
2189 key_ptr[1] = key_ptr[0] + key_len[0] + 1;
2190 key_ptr[2] = key_ptr[1] + key_len[1] + 1;
2191 key_ptr[3] = key_ptr[2] + key_len[2] + 1;
2193 for (; srclen; srclen--, src++)
2195 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
2196 dummy[0] = *src;
2197 if (decomposed_len)
2199 for (i = 0; i < decomposed_len; i++)
2201 WCHAR wch = dummy[i];
2202 unsigned int ce;
2204 if ((flags & NORM_IGNORESYMBOLS) &&
2205 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
2206 continue;
2208 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
2210 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
2211 if (ce != (unsigned int)-1)
2213 WCHAR key;
2214 if ((key = ce >> 16))
2216 *key_ptr[0]++ = key >> 8;
2217 *key_ptr[0]++ = key & 0xff;
2219 /* make key 1 start from 2 */
2220 if ((key = (ce >> 8) & 0xff)) *key_ptr[1]++ = key + 1;
2221 /* make key 2 start from 2 */
2222 if ((key = (ce >> 4) & 0x0f)) *key_ptr[2]++ = key + 1;
2223 /* key 3 is always a character code */
2224 if (ce & 1)
2226 if (wch >> 8) *key_ptr[3]++ = wch >> 8;
2227 if (wch & 0xff) *key_ptr[3]++ = wch & 0xff;
2230 else
2232 *key_ptr[0]++ = 0xff;
2233 *key_ptr[0]++ = 0xfe;
2234 if (wch >> 8) *key_ptr[0]++ = wch >> 8;
2235 if (wch & 0xff) *key_ptr[0]++ = wch & 0xff;
2241 *key_ptr[0] = 1;
2242 *key_ptr[1] = 1;
2243 *key_ptr[2] = 1;
2244 *key_ptr[3]++ = 1;
2245 *key_ptr[3] = 0;
2246 return key_ptr[3] - dst;
2250 /* compose a full-width katakana. return consumed source characters. */
2251 static int compose_katakana( const WCHAR *src, int srclen, WCHAR *dst )
2253 static const BYTE katakana_map[] =
2255 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
2256 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
2257 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
2258 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
2259 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
2260 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
2261 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
2262 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
2264 WCHAR dummy;
2265 int shift;
2267 if (!dst) dst = &dummy;
2269 switch (*src)
2271 case 0x309b:
2272 case 0x309c:
2273 *dst = *src - 2;
2274 return 1;
2275 case 0x30f0:
2276 case 0x30f1:
2277 case 0x30fd:
2278 *dst = *src;
2279 break;
2280 default:
2281 shift = *src - 0xff61;
2282 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
2283 *dst = katakana_map[shift] | 0x3000;
2284 break;
2287 if (srclen <= 1) return 1;
2289 switch (src[1])
2291 case 0xff9e: /* datakuten (voiced sound) */
2292 if ((*src >= 0xff76 && *src <= 0xff84) || (*src >= 0xff8a && *src <= 0xff8e) || *src == 0x30fd)
2293 *dst += 1;
2294 else if (*src == 0xff73)
2295 *dst = 0x30f4; /* KATAKANA LETTER VU */
2296 else if (*src == 0xff9c)
2297 *dst = 0x30f7; /* KATAKANA LETTER VA */
2298 else if (*src == 0x30f0)
2299 *dst = 0x30f8; /* KATAKANA LETTER VI */
2300 else if (*src == 0x30f1)
2301 *dst = 0x30f9; /* KATAKANA LETTER VE */
2302 else if (*src == 0xff66)
2303 *dst = 0x30fa; /* KATAKANA LETTER VO */
2304 else
2305 return 1;
2306 break;
2307 case 0xff9f: /* handakuten (semi-voiced sound) */
2308 if (*src >= 0xff8a && *src <= 0xff8e)
2309 *dst += 2;
2310 else
2311 return 1;
2312 break;
2313 default:
2314 return 1;
2316 return 2;
2319 /* map one or two half-width characters to one full-width character */
2320 static int map_to_fullwidth( const WCHAR *src, int srclen, WCHAR *dst )
2322 INT n;
2324 if (*src <= '~' && *src > ' ' && *src != '\\')
2325 *dst = *src - 0x20 + 0xff00;
2326 else if (*src == ' ')
2327 *dst = 0x3000;
2328 else if (*src <= 0x00af && *src >= 0x00a2)
2330 static const BYTE misc_symbols_table[] =
2332 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
2333 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
2335 if (misc_symbols_table[*src - 0x00a2])
2336 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
2337 else
2338 *dst = *src;
2340 else if (*src == 0x20a9) /* WON SIGN */
2341 *dst = 0xffe6;
2342 else if ((n = compose_katakana(src, srclen, dst)) > 0)
2343 return n;
2344 else if (*src >= 0xffa0 && *src <= 0xffdc)
2346 static const BYTE hangul_mapping_table[] =
2348 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
2349 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
2350 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
2351 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
2352 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
2353 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
2354 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
2355 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
2358 if (hangul_mapping_table[*src - 0xffa0])
2359 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
2360 else
2361 *dst = *src;
2363 else
2364 *dst = *src;
2366 return 1;
2369 /* decompose a full-width katakana character into one or two half-width characters. */
2370 static int decompose_katakana( WCHAR c, WCHAR *dst, int dstlen )
2372 static const BYTE katakana_map[] =
2374 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
2375 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
2376 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
2377 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
2378 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
2379 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
2380 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
2381 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
2382 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
2383 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
2384 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
2385 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
2386 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
2388 int len = 0, shift = c - 0x3099;
2389 BYTE k;
2391 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
2393 k = katakana_map[shift];
2394 if (!k)
2396 if (dstlen > 0) *dst = c;
2397 len++;
2399 else if (k > 0x60)
2401 if (dstlen > 0) *dst = k | 0xff00;
2402 len++;
2404 else
2406 if (dstlen >= 2)
2408 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
2409 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
2411 len += 2;
2413 return len;
2416 /* map single full-width character to single or double half-width characters. */
2417 static int map_to_halfwidth( WCHAR c, WCHAR *dst, int dstlen )
2419 int n = decompose_katakana( c, dst, dstlen );
2420 if (n > 0) return n;
2422 if (c == 0x3000)
2423 *dst = ' ';
2424 else if (c == 0x3001)
2425 *dst = 0xff64;
2426 else if (c == 0x3002)
2427 *dst = 0xff61;
2428 else if (c == 0x300c || c == 0x300d)
2429 *dst = (c - 0x300c) + 0xff62;
2430 else if (c >= 0x3131 && c <= 0x3163)
2432 *dst = c - 0x3131 + 0xffa1;
2433 if (*dst >= 0xffbf) *dst += 3;
2434 if (*dst >= 0xffc8) *dst += 2;
2435 if (*dst >= 0xffd0) *dst += 2;
2436 if (*dst >= 0xffd8) *dst += 2;
2438 else if (c == 0x3164)
2439 *dst = 0xffa0;
2440 else if (c == 0x2019)
2441 *dst = '\'';
2442 else if (c == 0x201d)
2443 *dst = '"';
2444 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
2445 *dst = c - 0xff00 + 0x20;
2446 else if (c >= 0xffe0 && c <= 0xffe6)
2448 static const WCHAR misc_symbol_map[] = { 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9 };
2449 *dst = misc_symbol_map[c - 0xffe0];
2451 else
2452 *dst = c;
2454 return 1;
2458 /* 32-bit collation element table format:
2459 * unicode weight - high 16 bit, diacritic weight - high 8 bit of low 16 bit,
2460 * case weight - high 4 bit of low 8 bit.
2463 enum weight { UNICODE_WEIGHT, DIACRITIC_WEIGHT, CASE_WEIGHT };
2465 static unsigned int get_weight( WCHAR ch, enum weight type )
2467 unsigned int ret;
2469 ret = collation_table[collation_table[collation_table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
2470 if (ret == ~0u) return ch;
2472 switch (type)
2474 case UNICODE_WEIGHT: return ret >> 16;
2475 case DIACRITIC_WEIGHT: return (ret >> 8) & 0xff;
2476 case CASE_WEIGHT: return (ret >> 4) & 0x0f;
2477 default: return 0;
2482 static void inc_str_pos( const WCHAR **str, int *len, unsigned int *dpos, unsigned int *dlen )
2484 (*dpos)++;
2485 if (*dpos == *dlen)
2487 *dpos = *dlen = 0;
2488 (*str)++;
2489 (*len)--;
2494 static int compare_weights(int flags, const WCHAR *str1, int len1,
2495 const WCHAR *str2, int len2, enum weight type )
2497 unsigned int ce1, ce2, dpos1 = 0, dpos2 = 0, dlen1 = 0, dlen2 = 0;
2498 const WCHAR *dstr1 = NULL, *dstr2 = NULL;
2500 while (len1 > 0 && len2 > 0)
2502 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
2503 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
2505 if (flags & NORM_IGNORESYMBOLS)
2507 int skip = 0;
2508 /* FIXME: not tested */
2509 if (get_char_type( CT_CTYPE1, dstr1[dpos1] ) & (C1_PUNCT | C1_SPACE))
2511 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
2512 skip = 1;
2514 if (get_char_type( CT_CTYPE1, dstr2[dpos2] ) & (C1_PUNCT | C1_SPACE))
2516 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
2517 skip = 1;
2519 if (skip) continue;
2522 /* hyphen and apostrophe are treated differently depending on
2523 * whether SORT_STRINGSORT specified or not
2525 if (type == UNICODE_WEIGHT && !(flags & SORT_STRINGSORT))
2527 if (dstr1[dpos1] == '-' || dstr1[dpos1] == '\'')
2529 if (dstr2[dpos2] != '-' && dstr2[dpos2] != '\'')
2531 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
2532 continue;
2535 else if (dstr2[dpos2] == '-' || dstr2[dpos2] == '\'')
2537 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
2538 continue;
2542 ce1 = get_weight( dstr1[dpos1], type );
2543 if (!ce1)
2545 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
2546 continue;
2548 ce2 = get_weight( dstr2[dpos2], type );
2549 if (!ce2)
2551 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
2552 continue;
2555 if (ce1 - ce2) return ce1 - ce2;
2557 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
2558 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
2560 while (len1)
2562 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
2563 ce1 = get_weight( dstr1[dpos1], type );
2564 if (ce1) break;
2565 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
2567 while (len2)
2569 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
2570 ce2 = get_weight( dstr2[dpos2], type );
2571 if (ce2) break;
2572 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
2574 return len1 - len2;
2578 static const struct geoinfo *get_geoinfo_ptr( GEOID geoid )
2580 int min = 0, max = ARRAY_SIZE( geoinfodata )-1;
2582 while (min <= max)
2584 int n = (min + max)/2;
2585 const struct geoinfo *ptr = &geoinfodata[n];
2586 if (geoid == ptr->id) /* we don't need empty entries */
2587 return *ptr->iso2W ? ptr : NULL;
2588 if (ptr->id > geoid) max = n-1;
2589 else min = n+1;
2591 return NULL;
2595 static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare )
2597 static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
2598 int first, last, limit, dayinsecs;
2600 if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */
2601 if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */
2603 /* if year is 0 then date is in day-of-week format, otherwise
2604 * it's absolute date.
2606 if (!compare->wYear)
2608 /* wDay is interpreted as number of the week in the month
2609 * 5 means: the last week in the month */
2610 /* calculate the day of the first DayOfWeek in the month */
2611 first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1;
2612 /* check needed for the 5th weekday of the month */
2613 last = month_lengths[tf->Month - 1] +
2614 (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400))));
2615 limit = first + 7 * (compare->wDay - 1);
2616 if (limit > last) limit -= 7;
2618 else limit = compare->wDay;
2620 limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60;
2621 dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second;
2622 return dayinsecs - limit;
2626 static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local )
2628 int year;
2629 BOOL before_standard_date, after_daylight_date;
2630 LARGE_INTEGER t2;
2631 TIME_FIELDS tf;
2633 if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN;
2635 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
2636 if (info->StandardDate.wMonth == 0 ||
2637 (info->StandardDate.wYear == 0 &&
2638 (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 ||
2639 info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5)))
2641 SetLastError( ERROR_INVALID_PARAMETER );
2642 return TIME_ZONE_ID_INVALID;
2645 if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000;
2646 RtlTimeToTimeFields( &time, &tf );
2647 year = tf.Year;
2648 if (!is_local)
2650 t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000;
2651 RtlTimeToTimeFields( &t2, &tf );
2653 if (tf.Year == year)
2654 before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0;
2655 else
2656 before_standard_date = tf.Year < year;
2658 if (!is_local)
2660 t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000;
2661 RtlTimeToTimeFields( &t2, &tf );
2663 if (tf.Year == year)
2664 after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0;
2665 else
2666 after_daylight_date = tf.Year > year;
2668 if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */
2670 if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
2672 else /* Down south */
2674 if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
2676 return TIME_ZONE_ID_STANDARD;
2680 /* Note: the Internal_ functions are not documented. The number of parameters
2681 * should be correct, but their exact meaning may not.
2684 /******************************************************************************
2685 * Internal_EnumCalendarInfo (kernelbase.@)
2687 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc, LCID lcid, CALID id,
2688 CALTYPE type, BOOL unicode, BOOL ex,
2689 BOOL exex, LPARAM lparam )
2691 WCHAR buffer[256];
2692 DWORD optional = 0;
2693 INT ret;
2695 if (!proc)
2697 SetLastError( ERROR_INVALID_PARAMETER );
2698 return FALSE;
2701 if (id == ENUM_ALL_CALENDARS)
2703 if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
2704 (WCHAR *)&id, sizeof(id) / sizeof(WCHAR) )) return FALSE;
2705 if (!GetLocaleInfoW( lcid, LOCALE_IOPTIONALCALENDAR | LOCALE_RETURN_NUMBER,
2706 (WCHAR *)&optional, sizeof(optional) / sizeof(WCHAR) )) optional = 0;
2709 for (;;)
2711 if (type & CAL_RETURN_NUMBER)
2712 ret = GetCalendarInfoW( lcid, id, type, NULL, 0, (LPDWORD)buffer );
2713 else if (unicode)
2714 ret = GetCalendarInfoW( lcid, id, type, buffer, ARRAY_SIZE(buffer), NULL );
2715 else
2717 WCHAR bufW[256];
2718 ret = GetCalendarInfoW( lcid, id, type, bufW, ARRAY_SIZE(bufW), NULL );
2719 if (ret) WideCharToMultiByte( CP_ACP, 0, bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
2722 if (ret)
2724 if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
2725 else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
2726 else ret = proc( buffer );
2728 if (!ret) break;
2729 if (!optional) break;
2730 id = optional;
2732 return TRUE;
2736 /**************************************************************************
2737 * Internal_EnumDateFormats (kernelbase.@)
2739 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
2740 BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
2742 WCHAR buffer[256];
2743 LCTYPE lctype;
2744 CALID cal_id;
2745 INT ret;
2747 if (!proc)
2749 SetLastError( ERROR_INVALID_PARAMETER );
2750 return FALSE;
2752 if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER,
2753 (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR) ))
2754 return FALSE;
2756 switch (flags & ~LOCALE_USE_CP_ACP)
2758 case 0:
2759 case DATE_SHORTDATE:
2760 lctype = LOCALE_SSHORTDATE;
2761 break;
2762 case DATE_LONGDATE:
2763 lctype = LOCALE_SLONGDATE;
2764 break;
2765 case DATE_YEARMONTH:
2766 lctype = LOCALE_SYEARMONTH;
2767 break;
2768 default:
2769 FIXME( "unknown date format 0x%08x\n", flags );
2770 SetLastError( ERROR_INVALID_PARAMETER );
2771 return FALSE;
2774 lctype |= flags & LOCALE_USE_CP_ACP;
2775 if (unicode)
2776 ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
2777 else
2778 ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
2780 if (ret)
2782 if (exex) ((DATEFMT_ENUMPROCEXEX)proc)( buffer, cal_id, lparam );
2783 else if (ex) ((DATEFMT_ENUMPROCEXW)proc)( buffer, cal_id );
2784 else proc( buffer );
2786 return TRUE;
2790 /******************************************************************************
2791 * Internal_EnumLanguageGroupLocales (kernelbase.@)
2793 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
2794 DWORD flags, LONG_PTR param, BOOL unicode )
2796 WCHAR name[10], value[10];
2797 DWORD name_len, value_len, type, index = 0, alt = 0;
2798 HKEY key, altkey;
2799 LCID lcid;
2801 if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
2803 SetLastError( ERROR_INVALID_PARAMETER );
2804 return FALSE;
2807 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
2808 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
2810 for (;;)
2812 name_len = ARRAY_SIZE(name);
2813 value_len = sizeof(value);
2814 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
2815 &type, (BYTE *)value, &value_len ))
2817 if (alt++) break;
2818 index = 0;
2819 continue;
2821 if (type != REG_SZ) continue;
2822 if (id != wcstoul( value, NULL, 16 )) continue;
2823 lcid = wcstoul( name, NULL, 16 );
2824 if (!unicode)
2826 char nameA[10];
2827 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
2828 if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
2830 else if (!proc( id, lcid, name, param )) break;
2832 RegCloseKey( altkey );
2833 RegCloseKey( key );
2834 return TRUE;
2838 /***********************************************************************
2839 * Internal_EnumSystemCodePages (kernelbase.@)
2841 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
2842 BOOL unicode )
2844 WCHAR name[10];
2845 DWORD name_len, type, index = 0;
2846 HKEY key;
2848 if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
2850 for (;;)
2852 name_len = ARRAY_SIZE(name);
2853 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
2854 if (type != REG_SZ) continue;
2855 if (!wcstoul( name, NULL, 10 )) continue;
2856 if (!unicode)
2858 char nameA[10];
2859 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
2860 if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
2862 else if (!proc( name )) break;
2864 RegCloseKey( key );
2865 return TRUE;
2869 /******************************************************************************
2870 * Internal_EnumSystemLanguageGroups (kernelbase.@)
2872 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
2873 DWORD flags, LONG_PTR param, BOOL unicode )
2875 WCHAR name[10], value[10], descr[80];
2876 DWORD name_len, value_len, type, index = 0;
2877 HKEY key;
2878 LGRPID id;
2880 if (!proc)
2882 SetLastError( ERROR_INVALID_PARAMETER );
2883 return FALSE;
2886 switch (flags)
2888 case 0:
2889 flags = LGRPID_INSTALLED;
2890 break;
2891 case LGRPID_INSTALLED:
2892 case LGRPID_SUPPORTED:
2893 break;
2894 default:
2895 SetLastError( ERROR_INVALID_FLAGS );
2896 return FALSE;
2899 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
2901 for (;;)
2903 name_len = ARRAY_SIZE(name);
2904 value_len = sizeof(value);
2905 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
2906 if (type != REG_SZ) continue;
2908 id = wcstoul( name, NULL, 16 );
2910 if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
2911 if (!LoadStringW( kernel32_handle, 0x2000 + id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
2912 TRACE( "%p: %u %s %s %x %lx\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
2913 if (!unicode)
2915 char nameA[10], descrA[80];
2916 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
2917 WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
2918 if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
2920 else if (!proc( id, name, descr, flags, param )) break;
2922 RegCloseKey( key );
2923 return TRUE;
2927 /**************************************************************************
2928 * Internal_EnumTimeFormats (kernelbase.@)
2930 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
2931 BOOL unicode, BOOL ex, LPARAM lparam )
2933 WCHAR buffer[256];
2934 LCTYPE lctype;
2935 INT ret;
2937 if (!proc)
2939 SetLastError( ERROR_INVALID_PARAMETER );
2940 return FALSE;
2942 switch (flags & ~LOCALE_USE_CP_ACP)
2944 case 0:
2945 lctype = LOCALE_STIMEFORMAT;
2946 break;
2947 case TIME_NOSECONDS:
2948 lctype = LOCALE_SSHORTTIME;
2949 break;
2950 default:
2951 FIXME( "Unknown time format %x\n", flags );
2952 SetLastError( ERROR_INVALID_PARAMETER );
2953 return FALSE;
2956 lctype |= flags & LOCALE_USE_CP_ACP;
2957 if (unicode)
2958 ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
2959 else
2960 ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
2962 if (ret)
2964 if (ex) ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
2965 else proc( buffer );
2967 return TRUE;
2971 /******************************************************************************
2972 * Internal_EnumUILanguages (kernelbase.@)
2974 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
2975 LONG_PTR param, BOOL unicode )
2977 WCHAR name[10];
2978 DWORD name_len, type, index = 0;
2979 HKEY key;
2981 if (!proc)
2983 SetLastError( ERROR_INVALID_PARAMETER );
2984 return FALSE;
2986 if (flags & ~MUI_LANGUAGE_ID)
2988 SetLastError( ERROR_INVALID_FLAGS );
2989 return FALSE;
2992 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
2994 for (;;)
2996 name_len = ARRAY_SIZE(name);
2997 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
2998 if (type != REG_SZ) continue;
2999 if (!wcstoul( name, NULL, 16 )) continue;
3000 if (!unicode)
3002 char nameA[10];
3003 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3004 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
3006 else if (!proc( name, param )) break;
3008 RegCloseKey( key );
3009 return TRUE;
3013 /******************************************************************************
3014 * CompareStringEx (kernelbase.@)
3016 INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1,
3017 const WCHAR *str2, int len2, NLSVERSIONINFO *version,
3018 void *reserved, LPARAM handle )
3020 DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | SORT_STRINGSORT |
3021 NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH | LOCALE_USE_CP_ACP;
3022 DWORD semistub_flags = NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE | 0x10000000;
3023 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3024 INT ret;
3025 static int once;
3027 if (version) FIXME( "unexpected version parameter\n" );
3028 if (reserved) FIXME( "unexpected reserved value\n" );
3029 if (handle) FIXME( "unexpected handle\n" );
3031 if (!str1 || !str2)
3033 SetLastError( ERROR_INVALID_PARAMETER );
3034 return 0;
3037 if (flags & ~(supported_flags | semistub_flags))
3039 SetLastError( ERROR_INVALID_FLAGS );
3040 return 0;
3043 if (flags & semistub_flags)
3045 if (!once++) FIXME( "semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags );
3048 if (len1 < 0) len1 = lstrlenW(str1);
3049 if (len2 < 0) len2 = lstrlenW(str2);
3051 ret = compare_weights( flags, str1, len1, str2, len2, UNICODE_WEIGHT );
3052 if (!ret)
3054 if (!(flags & NORM_IGNORENONSPACE))
3055 ret = compare_weights( flags, str1, len1, str2, len2, DIACRITIC_WEIGHT );
3056 if (!ret && !(flags & NORM_IGNORECASE))
3057 ret = compare_weights( flags, str1, len1, str2, len2, CASE_WEIGHT );
3059 if (!ret) return CSTR_EQUAL;
3060 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3064 /******************************************************************************
3065 * CompareStringA (kernelbase.@)
3067 INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1,
3068 const char *str2, int len2 )
3070 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3071 WCHAR *buf2W = buf1W + 130;
3072 LPWSTR str1W, str2W;
3073 INT len1W = 0, len2W = 0, ret;
3074 UINT locale_cp = CP_ACP;
3076 if (!str1 || !str2)
3078 SetLastError( ERROR_INVALID_PARAMETER );
3079 return 0;
3081 if (len1 < 0) len1 = strlen(str1);
3082 if (len2 < 0) len2 = strlen(str2);
3084 locale_cp = get_lcid_codepage( lcid, flags );
3085 if (len1)
3087 if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 );
3088 if (len1W) str1W = buf1W;
3089 else
3091 len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 );
3092 str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) );
3093 if (!str1W)
3095 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3096 return 0;
3098 MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W );
3101 else
3103 len1W = 0;
3104 str1W = buf1W;
3107 if (len2)
3109 if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 );
3110 if (len2W) str2W = buf2W;
3111 else
3113 len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 );
3114 str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) );
3115 if (!str2W)
3117 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
3118 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3119 return 0;
3121 MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W );
3124 else
3126 len2W = 0;
3127 str2W = buf2W;
3130 ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W );
3132 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
3133 if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W );
3134 return ret;
3138 /******************************************************************************
3139 * CompareStringW (kernelbase.@)
3141 INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1,
3142 const WCHAR *str2, int len2 )
3144 return CompareStringEx( NULL, flags, str1, len1, str2, len2, NULL, NULL, 0 );
3148 /******************************************************************************
3149 * CompareStringOrdinal (kernelbase.@)
3151 INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
3152 const WCHAR *str2, INT len2, BOOL ignore_case )
3154 int ret;
3156 if (!str1 || !str2)
3158 SetLastError( ERROR_INVALID_PARAMETER );
3159 return 0;
3161 if (len1 < 0) len1 = lstrlenW( str1 );
3162 if (len2 < 0) len2 = lstrlenW( str2 );
3164 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
3165 if (ret < 0) return CSTR_LESS_THAN;
3166 if (ret > 0) return CSTR_GREATER_THAN;
3167 return CSTR_EQUAL;
3171 /******************************************************************************
3172 * ConvertDefaultLocale (kernelbase.@)
3174 LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
3176 switch (lcid)
3178 case LOCALE_INVARIANT:
3179 return lcid; /* keep as-is */
3180 case LOCALE_SYSTEM_DEFAULT:
3181 return GetSystemDefaultLCID();
3182 case LOCALE_USER_DEFAULT:
3183 case LOCALE_NEUTRAL:
3184 return GetUserDefaultLCID();
3185 case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ):
3186 case MAKELANGID( LANG_CHINESE, 0x1e ):
3187 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
3188 case MAKELANGID( LANG_CHINESE, 0x1f ):
3189 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG );
3190 case MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ):
3191 return MAKELANGID( LANG_SPANISH, SUBLANG_SPANISH_MODERN );
3192 case MAKELANGID( LANG_IRISH, SUBLANG_NEUTRAL ):
3193 return MAKELANGID( LANG_IRISH, SUBLANG_IRISH_IRELAND );
3194 case MAKELANGID( LANG_BENGALI, SUBLANG_NEUTRAL ):
3195 return MAKELANGID( LANG_BENGALI, SUBLANG_BENGALI_BANGLADESH );
3196 case MAKELANGID( LANG_SINDHI, SUBLANG_NEUTRAL ):
3197 return MAKELANGID( LANG_SINDHI, SUBLANG_SINDHI_AFGHANISTAN );
3198 case MAKELANGID( LANG_INUKTITUT, SUBLANG_NEUTRAL ):
3199 return MAKELANGID( LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA_LATIN );
3200 case MAKELANGID( LANG_TAMAZIGHT, SUBLANG_NEUTRAL ):
3201 return MAKELANGID( LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN );
3202 case MAKELANGID( LANG_FULAH, SUBLANG_NEUTRAL ):
3203 return MAKELANGID( LANG_FULAH, SUBLANG_FULAH_SENEGAL );
3204 case MAKELANGID( LANG_TIGRINYA, SUBLANG_NEUTRAL ):
3205 return MAKELANGID( LANG_TIGRINYA, SUBLANG_TIGRINYA_ERITREA );
3206 default:
3207 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
3208 if (SUBLANGID(lcid) == SUBLANG_NEUTRAL && SORTIDFROMLCID(lcid) == SORT_DEFAULT)
3209 lcid = MAKELANGID( PRIMARYLANGID(lcid), SUBLANG_DEFAULT );
3210 break;
3212 return lcid;
3216 /******************************************************************************
3217 * EnumCalendarInfoW (kernelbase.@)
3219 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
3220 CALID id, CALTYPE type )
3222 return Internal_EnumCalendarInfo( proc, lcid, id, type, TRUE, FALSE, FALSE, 0 );
3226 /******************************************************************************
3227 * EnumCalendarInfoExW (kernelbase.@)
3229 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
3230 CALID id, CALTYPE type )
3232 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, FALSE, 0 );
3235 /******************************************************************************
3236 * EnumCalendarInfoExEx (kernelbase.@)
3238 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
3239 LPCWSTR reserved, CALTYPE type, LPARAM lparam )
3241 LCID lcid = LocaleNameToLCID( locale, 0 );
3242 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, TRUE, lparam );
3246 /**************************************************************************
3247 * EnumDateFormatsW (kernelbase.@)
3249 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
3251 return Internal_EnumDateFormats( proc, lcid, flags, TRUE, FALSE, FALSE, 0 );
3255 /**************************************************************************
3256 * EnumDateFormatsExW (kernelbase.@)
3258 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
3260 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, FALSE, 0 );
3264 /**************************************************************************
3265 * EnumDateFormatsExEx (kernelbase.@)
3267 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
3268 DWORD flags, LPARAM lparam )
3270 LCID lcid = LocaleNameToLCID( locale, 0 );
3271 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, TRUE, lparam );
3276 /******************************************************************************
3277 * EnumDynamicTimeZoneInformation (kernelbase.@)
3279 DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index,
3280 DYNAMIC_TIME_ZONE_INFORMATION *info )
3282 DYNAMIC_TIME_ZONE_INFORMATION tz;
3283 LSTATUS ret;
3284 DWORD size;
3286 if (!info) return ERROR_INVALID_PARAMETER;
3288 size = ARRAY_SIZE(tz.TimeZoneKeyName);
3289 ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL );
3290 if (ret) return ret;
3292 tz.DynamicDaylightTimeDisabled = TRUE;
3293 if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError();
3295 lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName );
3296 info->DynamicDaylightTimeDisabled = FALSE;
3297 return 0;
3301 /******************************************************************************
3302 * EnumLanguageGroupLocalesW (kernelbase.@)
3304 BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
3305 DWORD flags, LONG_PTR param )
3307 return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
3311 /******************************************************************************
3312 * EnumUILanguagesW (kernelbase.@)
3314 BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
3316 return Internal_EnumUILanguages( proc, flags, param, TRUE );
3320 /***********************************************************************
3321 * EnumSystemCodePagesW (kernelbase.@)
3323 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
3325 return Internal_EnumSystemCodePages( proc, flags, TRUE );
3329 /******************************************************************************
3330 * EnumSystemGeoID (kernelbase.@)
3332 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc )
3334 INT i;
3336 TRACE( "(%d, %d, %p)\n", class, parent, proc );
3338 if (!proc)
3340 SetLastError( ERROR_INVALID_PARAMETER );
3341 return FALSE;
3343 if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL)
3345 SetLastError( ERROR_INVALID_FLAGS );
3346 return FALSE;
3349 for (i = 0; i < ARRAY_SIZE(geoinfodata); i++)
3351 const struct geoinfo *ptr = &geoinfodata[i];
3353 if (class == GEOCLASS_NATION && (ptr->kind != LOCATION_NATION)) continue;
3354 /* LOCATION_BOTH counts as region */
3355 if (class == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION)) continue;
3356 if (parent && ptr->parent != parent) continue;
3357 if (!proc( ptr->id )) break;
3359 return TRUE;
3363 /******************************************************************************
3364 * EnumSystemLanguageGroupsW (kernelbase.@)
3366 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
3367 DWORD flags, LONG_PTR param )
3369 return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
3373 /******************************************************************************
3374 * EnumSystemLocalesA (kernelbase.@)
3376 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
3378 char name[10];
3379 DWORD name_len, type, index = 0;
3380 HKEY key;
3382 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
3384 for (;;)
3386 name_len = ARRAY_SIZE(name);
3387 if (RegEnumValueA( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
3388 if (type != REG_SZ) continue;
3389 if (!strtoul( name, NULL, 16 )) continue;
3390 if (!proc( name )) break;
3392 RegCloseKey( key );
3393 return TRUE;
3397 /******************************************************************************
3398 * EnumSystemLocalesW (kernelbase.@)
3400 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
3402 WCHAR name[10];
3403 DWORD name_len, type, index = 0;
3404 HKEY key;
3406 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
3408 for (;;)
3410 name_len = ARRAY_SIZE(name);
3411 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
3412 if (type != REG_SZ) continue;
3413 if (!wcstoul( name, NULL, 16 )) continue;
3414 if (!proc( name )) break;
3416 RegCloseKey( key );
3417 return TRUE;
3421 /******************************************************************************
3422 * EnumSystemLocalesEx (kernelbase.@)
3424 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
3425 LPARAM param, void *reserved )
3427 WCHAR buffer[256], name[10];
3428 DWORD name_len, type, neutral, flags, index = 0, alt = 0;
3429 HKEY key, altkey;
3430 LCID lcid;
3432 if (reserved)
3434 SetLastError( ERROR_INVALID_PARAMETER );
3435 return FALSE;
3438 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
3439 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
3441 for (;;)
3443 name_len = ARRAY_SIZE(name);
3444 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL, &type, NULL, NULL ))
3446 if (alt++) break;
3447 index = 0;
3448 continue;
3450 if (type != REG_SZ) continue;
3451 if (!(lcid = wcstoul( name, NULL, 16 ))) continue;
3453 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, buffer, ARRAY_SIZE( buffer ));
3454 if (!GetLocaleInfoW( lcid, LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
3455 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
3456 neutral = 0;
3458 if (alt)
3459 flags = LOCALE_ALTERNATE_SORTS;
3460 else
3461 flags = LOCALE_WINDOWS | (neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA);
3463 if (wanted_flags && !(flags & wanted_flags)) continue;
3464 if (!proc( buffer, flags, param )) break;
3466 RegCloseKey( altkey );
3467 RegCloseKey( key );
3468 return TRUE;
3472 /**************************************************************************
3473 * EnumTimeFormatsW (kernelbase.@)
3475 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
3477 return Internal_EnumTimeFormats( proc, lcid, flags, TRUE, FALSE, 0 );
3481 /**************************************************************************
3482 * EnumTimeFormatsEx (kernelbase.@)
3484 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
3485 DWORD flags, LPARAM lparam )
3487 LCID lcid = LocaleNameToLCID( locale, 0 );
3488 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, lparam );
3492 /**************************************************************************
3493 * FindNLSString (kernelbase.@)
3495 INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src,
3496 int srclen, const WCHAR *value, int valuelen, int *found )
3498 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
3500 LCIDToLocaleName( lcid, locale, ARRAY_SIZE(locale), 0 );
3501 return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 );
3505 /**************************************************************************
3506 * FindNLSStringEx (kernelbase.@)
3508 INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src,
3509 int srclen, const WCHAR *value, int valuelen, int *found,
3510 NLSVERSIONINFO *version, void *reserved, LPARAM handle )
3512 /* FIXME: this function should normalize strings before calling CompareStringEx() */
3513 DWORD mask = flags;
3514 int offset, inc, count;
3516 TRACE( "%s %x %s %d %s %d %p %p %p %ld\n", wine_dbgstr_w(locale), flags,
3517 wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found,
3518 version, reserved, handle );
3520 if (version || reserved || handle || !IsValidLocaleName(locale) ||
3521 !src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1)
3523 SetLastError( ERROR_INVALID_PARAMETER );
3524 return -1;
3526 if (srclen == -1) srclen = lstrlenW(src);
3527 if (valuelen == -1) valuelen = lstrlenW(value);
3529 srclen -= valuelen;
3530 if (srclen < 0) return -1;
3532 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
3533 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? srclen + 1 : 1;
3534 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : srclen;
3535 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
3536 while (count--)
3538 if (CompareStringEx( locale, mask, src + offset, valuelen,
3539 value, valuelen, NULL, NULL, 0 ) == CSTR_EQUAL)
3541 if (found) *found = valuelen;
3542 return offset;
3544 offset += inc;
3546 return -1;
3550 /******************************************************************************
3551 * FindStringOrdinal (kernelbase.@)
3553 INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
3554 const WCHAR *val, INT val_size, BOOL ignore_case )
3556 INT offset, inc, count;
3558 TRACE( "%#x %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
3559 wine_dbgstr_w(val), val_size, ignore_case );
3561 if (!src || !val)
3563 SetLastError( ERROR_INVALID_PARAMETER );
3564 return -1;
3567 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
3569 SetLastError( ERROR_INVALID_FLAGS );
3570 return -1;
3573 if (src_size == -1) src_size = lstrlenW( src );
3574 if (val_size == -1) val_size = lstrlenW( val );
3576 SetLastError( ERROR_SUCCESS );
3577 src_size -= val_size;
3578 if (src_size < 0) return -1;
3580 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
3581 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
3582 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
3583 while (count--)
3585 if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
3586 return offset;
3587 offset += inc;
3589 return -1;
3593 /******************************************************************************
3594 * FoldStringW (kernelbase.@)
3596 INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
3598 NTSTATUS status;
3599 WCHAR *buf = dst;
3600 int len = dstlen;
3602 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3604 SetLastError( ERROR_INVALID_PARAMETER );
3605 return 0;
3607 if (srclen == -1) srclen = lstrlenW(src) + 1;
3609 if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE)))
3611 len = srclen * 4;
3612 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
3614 SetLastError( ERROR_OUTOFMEMORY );
3615 return 0;
3619 for (;;)
3621 status = fold_string( flags, src, srclen, buf, &len );
3622 if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf );
3623 if (status != STATUS_BUFFER_TOO_SMALL) break;
3624 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
3626 SetLastError( ERROR_OUTOFMEMORY );
3627 return 0;
3630 if (status == STATUS_INVALID_PARAMETER_1)
3632 SetLastError( ERROR_INVALID_FLAGS );
3633 return 0;
3635 if (!set_ntstatus( status )) return 0;
3637 if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
3638 return len;
3642 /******************************************************************************
3643 * GetACP (kernelbase.@)
3645 UINT WINAPI GetACP(void)
3647 return nls_info.AnsiTableInfo.CodePage;
3651 /***********************************************************************
3652 * GetCPInfo (kernelbase.@)
3654 BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
3656 const CPTABLEINFO *table;
3658 if (!cpinfo)
3660 SetLastError( ERROR_INVALID_PARAMETER );
3661 return FALSE;
3663 switch (codepage)
3665 case CP_UTF7:
3666 case CP_UTF8:
3667 cpinfo->DefaultChar[0] = 0x3f;
3668 cpinfo->DefaultChar[1] = 0;
3669 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
3670 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
3671 break;
3672 default:
3673 if (!(table = get_codepage_table( codepage ))) return FALSE;
3674 cpinfo->MaxCharSize = table->MaximumCharacterSize;
3675 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
3676 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
3677 break;
3679 return TRUE;
3683 /***********************************************************************
3684 * GetCPInfoExW (kernelbase.@)
3686 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
3688 const CPTABLEINFO *table;
3689 int min, max, pos;
3691 if (!cpinfo)
3693 SetLastError( ERROR_INVALID_PARAMETER );
3694 return FALSE;
3696 switch (codepage)
3698 case CP_UTF7:
3699 cpinfo->DefaultChar[0] = 0x3f;
3700 cpinfo->DefaultChar[1] = 0;
3701 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
3702 cpinfo->MaxCharSize = 5;
3703 cpinfo->CodePage = CP_UTF7;
3704 cpinfo->UnicodeDefaultChar = 0x3f;
3705 break;
3706 case CP_UTF8:
3707 cpinfo->DefaultChar[0] = 0x3f;
3708 cpinfo->DefaultChar[1] = 0;
3709 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
3710 cpinfo->MaxCharSize = 4;
3711 cpinfo->CodePage = CP_UTF8;
3712 cpinfo->UnicodeDefaultChar = 0x3f;
3713 break;
3714 default:
3715 if (!(table = get_codepage_table( codepage ))) return FALSE;
3716 cpinfo->MaxCharSize = table->MaximumCharacterSize;
3717 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
3718 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
3719 cpinfo->CodePage = table->CodePage;
3720 cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
3721 break;
3724 min = 0;
3725 max = ARRAY_SIZE(codepage_names) - 1;
3726 cpinfo->CodePageName[0] = 0;
3727 while (min <= max)
3729 pos = (min + max) / 2;
3730 if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
3731 else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
3732 else
3734 wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
3735 break;
3738 return TRUE;
3742 /***********************************************************************
3743 * GetCalendarInfoW (kernelbase.@)
3745 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
3746 WCHAR *data, INT count, DWORD *value )
3748 static const LCTYPE lctype_map[] =
3750 0, /* not used */
3751 0, /* CAL_ICALINTVALUE */
3752 0, /* CAL_SCALNAME */
3753 0, /* CAL_IYEAROFFSETRANGE */
3754 0, /* CAL_SERASTRING */
3755 LOCALE_SSHORTDATE,
3756 LOCALE_SLONGDATE,
3757 LOCALE_SDAYNAME1,
3758 LOCALE_SDAYNAME2,
3759 LOCALE_SDAYNAME3,
3760 LOCALE_SDAYNAME4,
3761 LOCALE_SDAYNAME5,
3762 LOCALE_SDAYNAME6,
3763 LOCALE_SDAYNAME7,
3764 LOCALE_SABBREVDAYNAME1,
3765 LOCALE_SABBREVDAYNAME2,
3766 LOCALE_SABBREVDAYNAME3,
3767 LOCALE_SABBREVDAYNAME4,
3768 LOCALE_SABBREVDAYNAME5,
3769 LOCALE_SABBREVDAYNAME6,
3770 LOCALE_SABBREVDAYNAME7,
3771 LOCALE_SMONTHNAME1,
3772 LOCALE_SMONTHNAME2,
3773 LOCALE_SMONTHNAME3,
3774 LOCALE_SMONTHNAME4,
3775 LOCALE_SMONTHNAME5,
3776 LOCALE_SMONTHNAME6,
3777 LOCALE_SMONTHNAME7,
3778 LOCALE_SMONTHNAME8,
3779 LOCALE_SMONTHNAME9,
3780 LOCALE_SMONTHNAME10,
3781 LOCALE_SMONTHNAME11,
3782 LOCALE_SMONTHNAME12,
3783 LOCALE_SMONTHNAME13,
3784 LOCALE_SABBREVMONTHNAME1,
3785 LOCALE_SABBREVMONTHNAME2,
3786 LOCALE_SABBREVMONTHNAME3,
3787 LOCALE_SABBREVMONTHNAME4,
3788 LOCALE_SABBREVMONTHNAME5,
3789 LOCALE_SABBREVMONTHNAME6,
3790 LOCALE_SABBREVMONTHNAME7,
3791 LOCALE_SABBREVMONTHNAME8,
3792 LOCALE_SABBREVMONTHNAME9,
3793 LOCALE_SABBREVMONTHNAME10,
3794 LOCALE_SABBREVMONTHNAME11,
3795 LOCALE_SABBREVMONTHNAME12,
3796 LOCALE_SABBREVMONTHNAME13,
3797 LOCALE_SYEARMONTH,
3798 0, /* CAL_ITWODIGITYEARMAX */
3799 LOCALE_SSHORTESTDAYNAME1,
3800 LOCALE_SSHORTESTDAYNAME2,
3801 LOCALE_SSHORTESTDAYNAME3,
3802 LOCALE_SSHORTESTDAYNAME4,
3803 LOCALE_SSHORTESTDAYNAME5,
3804 LOCALE_SSHORTESTDAYNAME6,
3805 LOCALE_SSHORTESTDAYNAME7,
3806 LOCALE_SMONTHDAY,
3807 0, /* CAL_SABBREVERASTRING */
3809 DWORD flags = 0;
3810 CALTYPE calinfo = type & 0xffff;
3812 if (type & CAL_NOUSEROVERRIDE) FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
3813 if (type & CAL_USE_CP_ACP) FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
3815 if ((type & CAL_RETURN_NUMBER) && !value)
3817 SetLastError( ERROR_INVALID_PARAMETER );
3818 return 0;
3821 if (type & CAL_RETURN_GENITIVE_NAMES) flags |= LOCALE_RETURN_GENITIVE_NAMES;
3823 switch (calinfo)
3825 case CAL_ICALINTVALUE:
3826 if (type & CAL_RETURN_NUMBER)
3827 return GetLocaleInfoW( lcid, LOCALE_RETURN_NUMBER | LOCALE_ICALENDARTYPE,
3828 (WCHAR *)value, sizeof(*value) / sizeof(WCHAR) );
3829 return GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE, data, count );
3831 case CAL_SCALNAME:
3832 FIXME( "Unimplemented caltype %d\n", calinfo );
3833 if (data) *data = 0;
3834 return 1;
3836 case CAL_IYEAROFFSETRANGE:
3837 case CAL_SERASTRING:
3838 case CAL_SABBREVERASTRING:
3839 FIXME( "Unimplemented caltype %d\n", calinfo );
3840 return 0;
3842 case CAL_SSHORTDATE:
3843 case CAL_SLONGDATE:
3844 case CAL_SDAYNAME1:
3845 case CAL_SDAYNAME2:
3846 case CAL_SDAYNAME3:
3847 case CAL_SDAYNAME4:
3848 case CAL_SDAYNAME5:
3849 case CAL_SDAYNAME6:
3850 case CAL_SDAYNAME7:
3851 case CAL_SABBREVDAYNAME1:
3852 case CAL_SABBREVDAYNAME2:
3853 case CAL_SABBREVDAYNAME3:
3854 case CAL_SABBREVDAYNAME4:
3855 case CAL_SABBREVDAYNAME5:
3856 case CAL_SABBREVDAYNAME6:
3857 case CAL_SABBREVDAYNAME7:
3858 case CAL_SMONTHNAME1:
3859 case CAL_SMONTHNAME2:
3860 case CAL_SMONTHNAME3:
3861 case CAL_SMONTHNAME4:
3862 case CAL_SMONTHNAME5:
3863 case CAL_SMONTHNAME6:
3864 case CAL_SMONTHNAME7:
3865 case CAL_SMONTHNAME8:
3866 case CAL_SMONTHNAME9:
3867 case CAL_SMONTHNAME10:
3868 case CAL_SMONTHNAME11:
3869 case CAL_SMONTHNAME12:
3870 case CAL_SMONTHNAME13:
3871 case CAL_SABBREVMONTHNAME1:
3872 case CAL_SABBREVMONTHNAME2:
3873 case CAL_SABBREVMONTHNAME3:
3874 case CAL_SABBREVMONTHNAME4:
3875 case CAL_SABBREVMONTHNAME5:
3876 case CAL_SABBREVMONTHNAME6:
3877 case CAL_SABBREVMONTHNAME7:
3878 case CAL_SABBREVMONTHNAME8:
3879 case CAL_SABBREVMONTHNAME9:
3880 case CAL_SABBREVMONTHNAME10:
3881 case CAL_SABBREVMONTHNAME11:
3882 case CAL_SABBREVMONTHNAME12:
3883 case CAL_SABBREVMONTHNAME13:
3884 case CAL_SMONTHDAY:
3885 case CAL_SYEARMONTH:
3886 case CAL_SSHORTESTDAYNAME1:
3887 case CAL_SSHORTESTDAYNAME2:
3888 case CAL_SSHORTESTDAYNAME3:
3889 case CAL_SSHORTESTDAYNAME4:
3890 case CAL_SSHORTESTDAYNAME5:
3891 case CAL_SSHORTESTDAYNAME6:
3892 case CAL_SSHORTESTDAYNAME7:
3893 return GetLocaleInfoW( lcid, lctype_map[calinfo] | flags, data, count );
3895 case CAL_ITWODIGITYEARMAX:
3896 if (type & CAL_RETURN_NUMBER)
3898 *value = CALINFO_MAX_YEAR;
3899 return sizeof(DWORD) / sizeof(WCHAR);
3901 else
3903 WCHAR buffer[10];
3904 int ret = swprintf( buffer, ARRAY_SIZE(buffer), L"%u", CALINFO_MAX_YEAR ) + 1;
3905 if (!data) return ret;
3906 if (ret <= count)
3908 lstrcpyW( data, buffer );
3909 return ret;
3911 SetLastError( ERROR_INSUFFICIENT_BUFFER );
3912 return 0;
3914 break;
3915 default:
3916 FIXME( "Unknown caltype %d\n", calinfo );
3917 SetLastError( ERROR_INVALID_FLAGS );
3918 return 0;
3920 return 0;
3924 /***********************************************************************
3925 * GetCalendarInfoEx (kernelbase.@)
3927 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *locale, CALID calendar, const WCHAR *reserved,
3928 CALTYPE type, WCHAR *data, INT count, DWORD *value )
3930 LCID lcid = LocaleNameToLCID( locale, 0 );
3931 return GetCalendarInfoW( lcid, calendar, type, data, count, value );
3935 /***********************************************************************
3936 * GetDynamicTimeZoneInformation (kernelbase.@)
3938 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info )
3940 HKEY key;
3941 LARGE_INTEGER now;
3943 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info )))
3944 return TIME_ZONE_ID_INVALID;
3946 if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))
3948 RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
3949 sizeof(info->StandardName), NULL, 0, system_dir );
3950 RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
3951 sizeof(info->DaylightName), NULL, 0, system_dir );
3952 RegCloseKey( key );
3954 else return TIME_ZONE_ID_INVALID;
3956 NtQuerySystemTime( &now );
3957 return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE );
3961 /******************************************************************************
3962 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
3964 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info,
3965 DWORD *first, DWORD *last )
3967 HKEY key, dst_key = 0;
3968 DWORD type, count, ret = ERROR_FILE_NOT_FOUND;
3970 if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret;
3972 if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done;
3973 count = sizeof(DWORD);
3974 if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done;
3975 if (type != REG_DWORD) goto done;
3976 count = sizeof(DWORD);
3977 if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done;
3978 if (type != REG_DWORD) goto done;
3979 ret = 0;
3981 done:
3982 RegCloseKey( dst_key );
3983 RegCloseKey( key );
3984 return ret;
3988 /******************************************************************************
3989 * GetGeoInfoW (kernelbase.@)
3991 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang )
3993 const struct geoinfo *ptr = get_geoinfo_ptr( id );
3994 WCHAR bufferW[12];
3995 const WCHAR *str = bufferW;
3996 int len;
3998 TRACE( "%d %d %p %d %d\n", id, type, data, count, lang );
4000 if (!ptr)
4002 SetLastError( ERROR_INVALID_PARAMETER );
4003 return 0;
4005 switch (type)
4007 case GEO_NATION:
4008 if (ptr->kind != LOCATION_NATION) return 0;
4009 /* fall through */
4010 case GEO_ID:
4011 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", ptr->id );
4012 break;
4013 case GEO_ISO_UN_NUMBER:
4014 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", ptr->uncode );
4015 break;
4016 case GEO_PARENT:
4017 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", ptr->parent );
4018 break;
4019 case GEO_ISO2:
4020 str = ptr->iso2W;
4021 break;
4022 case GEO_ISO3:
4023 str = ptr->iso3W;
4024 break;
4025 case GEO_RFC1766:
4026 case GEO_LCID:
4027 case GEO_FRIENDLYNAME:
4028 case GEO_OFFICIALNAME:
4029 case GEO_TIMEZONES:
4030 case GEO_OFFICIALLANGUAGES:
4031 case GEO_LATITUDE:
4032 case GEO_LONGITUDE:
4033 case GEO_DIALINGCODE:
4034 case GEO_CURRENCYCODE:
4035 case GEO_CURRENCYSYMBOL:
4036 case GEO_NAME:
4037 FIXME( "type %d is not supported\n", type );
4038 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4039 return 0;
4040 default:
4041 WARN( "unrecognized type %d\n", type );
4042 SetLastError( ERROR_INVALID_FLAGS );
4043 return 0;
4046 len = lstrlenW(str) + 1;
4047 if (!data || !count) return len;
4049 memcpy( data, str, min(len, count) * sizeof(WCHAR) );
4050 if (count < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
4051 return count < len ? 0 : len;
4055 /******************************************************************************
4056 * GetLocaleInfoA (kernelbase.@)
4058 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
4060 WCHAR *bufferW;
4061 INT lenW, ret;
4063 TRACE( "lcid=0x%x lctype=0x%x %p %d\n", lcid, lctype, buffer, len );
4065 if (len < 0 || (len && !buffer))
4067 SetLastError( ERROR_INVALID_PARAMETER );
4068 return 0;
4070 if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
4072 SetLastError( ERROR_INVALID_FLAGS );
4073 return 0;
4076 if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
4077 return GetLocaleInfoW( lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) ) * sizeof(WCHAR);
4079 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
4081 if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
4083 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4084 return 0;
4086 ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW );
4087 if (ret) ret = WideCharToMultiByte( get_lcid_codepage( lcid, lctype ), 0,
4088 bufferW, ret, buffer, len, NULL, NULL );
4089 RtlFreeHeap( GetProcessHeap(), 0, bufferW );
4090 return ret;
4094 /******************************************************************************
4095 * GetLocaleInfoW (kernelbase.@)
4097 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
4099 HRSRC hrsrc;
4100 HGLOBAL hmem;
4101 INT ret;
4102 UINT lcflags = lctype;
4103 const WCHAR *p;
4104 unsigned int i;
4106 if (len < 0 || (len && !buffer))
4108 SetLastError( ERROR_INVALID_PARAMETER );
4109 return 0;
4111 if (lctype & LOCALE_RETURN_GENITIVE_NAMES && !is_genitive_name_supported( lctype ))
4113 SetLastError( ERROR_INVALID_FLAGS );
4114 return 0;
4117 if (!len) buffer = NULL;
4119 lcid = ConvertDefaultLocale( lcid );
4120 lctype = LOWORD(lctype);
4122 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
4124 /* first check for overrides in the registry */
4126 if (!(lcflags & LOCALE_NOUSEROVERRIDE) && lcid == ConvertDefaultLocale( LOCALE_USER_DEFAULT ))
4128 const struct registry_value *value = get_locale_registry_value( lctype );
4130 if (value)
4132 if (lcflags & LOCALE_RETURN_NUMBER)
4134 WCHAR tmp[16];
4135 ret = get_registry_locale_info( value, tmp, ARRAY_SIZE( tmp ));
4136 if (ret > 0)
4138 WCHAR *end;
4139 UINT number = wcstol( tmp, &end, get_value_base_by_lctype( lctype ) );
4140 if (*end) /* invalid number */
4142 SetLastError( ERROR_INVALID_FLAGS );
4143 return 0;
4145 ret = sizeof(UINT) / sizeof(WCHAR);
4146 if (!len) return ret;
4147 if (ret > len)
4149 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4150 return 0;
4152 memcpy( buffer, &number, sizeof(number) );
4155 else ret = get_registry_locale_info( value, buffer, len );
4157 if (ret != -1) return ret;
4161 /* now load it from kernel resources */
4163 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
4164 ULongToPtr((lctype >> 4) + 1), lcid )))
4166 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
4167 return 0;
4169 if (!(hmem = LoadResource( kernel32_handle, hrsrc ))) return 0;
4171 p = LockResource( hmem );
4172 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
4174 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT) / sizeof(WCHAR);
4175 else if (is_genitive_name_supported( lctype ) && *p)
4177 /* genitive form is stored after a null separator from a nominative */
4178 for (i = 1; i <= *p; i++) if (!p[i]) break;
4180 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
4182 ret = *p - i + 1;
4183 p += i;
4185 else ret = i;
4187 else
4188 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
4190 if (!len) return ret;
4192 if (ret > len)
4194 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4195 return 0;
4198 if (lcflags & LOCALE_RETURN_NUMBER)
4200 UINT number;
4201 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
4202 if (!tmp) return 0;
4203 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
4204 tmp[*p] = 0;
4205 number = wcstol( tmp, &end, get_value_base_by_lctype( lctype ) );
4206 if (!*end)
4207 memcpy( buffer, &number, sizeof(number) );
4208 else /* invalid number */
4210 SetLastError( ERROR_INVALID_FLAGS );
4211 ret = 0;
4213 HeapFree( GetProcessHeap(), 0, tmp );
4215 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
4216 lcid, lctype, buffer, len, number );
4218 else
4220 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
4221 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
4223 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
4224 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
4226 return ret;
4230 /******************************************************************************
4231 * GetLocaleInfoEx (kernelbase.@)
4233 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *locale, LCTYPE info, WCHAR *buffer, INT len )
4235 LCID lcid = LocaleNameToLCID( locale, 0 );
4237 TRACE( "%s lcid=0x%x 0x%x\n", debugstr_w(locale), lcid, info );
4239 if (!lcid) return 0;
4241 /* special handling for neutral locale names */
4242 if (locale && lstrlenW( locale ) == 2)
4244 switch (LOWORD( info ))
4246 case LOCALE_SNAME:
4247 if (len && len < 3)
4249 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4250 return 0;
4252 if (len) lstrcpyW( buffer, locale );
4253 return 3;
4254 case LOCALE_SPARENT:
4255 if (len) buffer[0] = 0;
4256 return 1;
4259 return GetLocaleInfoW( lcid, info, buffer, len );
4263 /******************************************************************************
4264 * GetNLSVersion (kernelbase.@)
4266 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info )
4268 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
4270 if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId ))
4272 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4273 return FALSE;
4275 if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES ))
4277 SetLastError( ERROR_INVALID_PARAMETER );
4278 return FALSE;
4280 return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info );
4284 /******************************************************************************
4285 * GetNLSVersionEx (kernelbase.@)
4287 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale,
4288 NLSVERSIONINFOEX *info )
4290 LCID lcid = 0;
4292 if (func != COMPARE_STRING)
4294 SetLastError( ERROR_INVALID_FLAGS );
4295 return FALSE;
4297 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
4298 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
4300 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4301 return FALSE;
4304 if (!(lcid = LocaleNameToLCID( locale, 0 ))) return FALSE;
4306 info->dwNLSVersion = info->dwDefinedVersion = sort.version;
4307 if (info->dwNLSVersionInfoSize >= sizeof(*info))
4309 const struct sortguid *sortid = get_language_sort( locale );
4310 info->dwEffectiveId = lcid;
4311 info->guidCustomVersion = sortid ? sortid->id : default_sort_guid;
4313 return TRUE;
4317 /******************************************************************************
4318 * GetOEMCP (kernelbase.@)
4320 UINT WINAPI GetOEMCP(void)
4322 return nls_info.OemTableInfo.CodePage;
4326 /***********************************************************************
4327 * GetStringTypeA (kernelbase.@)
4329 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
4330 WORD *chartype )
4332 UINT cp;
4333 INT countW;
4334 LPWSTR srcW;
4335 BOOL ret = FALSE;
4337 if (count == -1) count = strlen(src) + 1;
4339 cp = get_lcid_codepage( locale, 0 );
4340 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
4341 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
4343 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
4345 * NOTE: the target buffer has 1 word for each CHARACTER in the source
4346 * string, with multibyte characters there maybe be more bytes in count
4347 * than character space in the buffer!
4349 ret = GetStringTypeW(type, srcW, countW, chartype);
4350 HeapFree(GetProcessHeap(), 0, srcW);
4352 return ret;
4356 /***********************************************************************
4357 * GetStringTypeW (kernelbase.@)
4359 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
4361 if (!src)
4363 SetLastError( ERROR_INVALID_PARAMETER );
4364 return FALSE;
4366 if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3)
4368 SetLastError( ERROR_INVALID_PARAMETER );
4369 return FALSE;
4372 if (count == -1) count = lstrlenW(src) + 1;
4374 while (count--) *chartype++ = get_char_type( type, *src++ );
4376 return TRUE;
4380 /***********************************************************************
4381 * GetStringTypeExW (kernelbase.@)
4383 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
4384 WORD *chartype )
4386 /* locale is ignored for Unicode */
4387 return GetStringTypeW( type, src, count, chartype );
4391 /***********************************************************************
4392 * GetSystemDefaultLCID (kernelbase.@)
4394 LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
4396 LCID lcid;
4397 NtQueryDefaultLocale( FALSE, &lcid );
4398 return lcid;
4402 /***********************************************************************
4403 * GetSystemDefaultLangID (kernelbase.@)
4405 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
4407 return LANGIDFROMLCID( GetSystemDefaultLCID() );
4411 /***********************************************************************
4412 * GetSystemDefaultLocaleName (kernelbase.@)
4414 INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT len )
4416 return LCIDToLocaleName( GetSystemDefaultLCID(), name, len, 0 );
4420 /***********************************************************************
4421 * GetSystemDefaultUILanguage (kernelbase.@)
4423 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
4425 LANGID lang;
4426 NtQueryInstallUILanguage( &lang );
4427 return lang;
4431 /***********************************************************************
4432 * GetTimeZoneInformation (kernelbase.@)
4434 DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info )
4436 DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
4437 DWORD ret = GetDynamicTimeZoneInformation( &tzinfo );
4439 memcpy( info, &tzinfo, sizeof(*info) );
4440 return ret;
4444 /***********************************************************************
4445 * GetTimeZoneInformationForYear (kernelbase.@)
4447 BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year,
4448 DYNAMIC_TIME_ZONE_INFORMATION *dynamic,
4449 TIME_ZONE_INFORMATION *info )
4451 DYNAMIC_TIME_ZONE_INFORMATION local_info;
4452 HKEY key = 0, dst_key;
4453 DWORD count;
4454 LRESULT ret;
4455 struct
4457 LONG bias;
4458 LONG std_bias;
4459 LONG dlt_bias;
4460 SYSTEMTIME std_date;
4461 SYSTEMTIME dlt_date;
4462 } data;
4464 TRACE( "(%u,%p)\n", year, info );
4466 if (!dynamic)
4468 if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE;
4469 dynamic = &local_info;
4472 if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done;
4473 if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
4474 sizeof(info->StandardName), NULL, 0, system_dir ))
4476 count = sizeof(info->StandardName);
4477 if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count )))
4478 goto done;
4480 if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
4481 sizeof(info->DaylightName), NULL, 0, system_dir ))
4483 count = sizeof(info->DaylightName);
4484 if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count )))
4485 goto done;
4488 ret = ERROR_FILE_NOT_FOUND;
4489 if (!dynamic->DynamicDaylightTimeDisabled &&
4490 !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key ))
4492 WCHAR yearW[16];
4493 swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year );
4494 count = sizeof(data);
4495 ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count );
4496 RegCloseKey( dst_key );
4498 if (ret)
4500 count = sizeof(data);
4501 ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count );
4504 if (!ret)
4506 info->Bias = data.bias;
4507 info->StandardBias = data.std_bias;
4508 info->DaylightBias = data.dlt_bias;
4509 info->StandardDate = data.std_date;
4510 info->DaylightDate = data.dlt_date;
4513 done:
4514 RegCloseKey( key );
4515 if (ret) SetLastError( ret );
4516 return !ret;
4520 /***********************************************************************
4521 * GetUserDefaultLCID (kernelbase.@)
4523 LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
4525 LCID lcid;
4526 NtQueryDefaultLocale( TRUE, &lcid );
4527 return lcid;
4531 /***********************************************************************
4532 * GetUserDefaultLangID (kernelbase.@)
4534 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
4536 return LANGIDFROMLCID( GetUserDefaultLCID() );
4540 /***********************************************************************
4541 * GetUserDefaultLocaleName (kernelbase.@)
4543 INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
4545 return LCIDToLocaleName( GetUserDefaultLCID(), name, len, 0 );
4549 /***********************************************************************
4550 * GetUserDefaultUILanguage (kernelbase.@)
4552 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
4554 LANGID lang;
4555 NtQueryDefaultUILanguage( &lang );
4556 return lang;
4560 /******************************************************************************
4561 * GetUserGeoID (kernelbase.@)
4563 GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass )
4565 GEOID ret = 39070;
4566 const WCHAR *name;
4567 WCHAR bufferW[40];
4568 HKEY hkey;
4570 switch (geoclass)
4572 case GEOCLASS_NATION:
4573 name = L"Nation";
4574 break;
4575 case GEOCLASS_REGION:
4576 name = L"Region";
4577 break;
4578 default:
4579 WARN("Unknown geoclass %d\n", geoclass);
4580 return GEOID_NOT_AVAILABLE;
4582 if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey ))
4584 DWORD count = sizeof(bufferW);
4585 if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count ))
4586 ret = wcstol( bufferW, NULL, 10 );
4587 RegCloseKey( hkey );
4589 return ret;
4593 /******************************************************************************
4594 * IdnToAscii (kernelbase.@)
4596 INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen,
4597 WCHAR *dst, INT dstlen )
4599 NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
4600 if (!set_ntstatus( status )) return 0;
4601 return dstlen;
4605 /******************************************************************************
4606 * IdnToNameprepUnicode (kernelbase.@)
4608 INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
4609 WCHAR *dst, INT dstlen )
4611 NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen );
4612 if (!set_ntstatus( status )) return 0;
4613 return dstlen;
4617 /******************************************************************************
4618 * IdnToUnicode (kernelbase.@)
4620 INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen,
4621 WCHAR *dst, INT dstlen )
4623 NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen );
4624 if (!set_ntstatus( status )) return 0;
4625 return dstlen;
4629 /******************************************************************************
4630 * IsCharAlphaA (kernelbase.@)
4632 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
4634 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
4635 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
4639 /******************************************************************************
4640 * IsCharAlphaW (kernelbase.@)
4642 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
4644 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
4648 /******************************************************************************
4649 * IsCharAlphaNumericA (kernelbase.@)
4651 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
4653 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
4654 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
4658 /******************************************************************************
4659 * IsCharAlphaNumericW (kernelbase.@)
4661 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
4663 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
4667 /******************************************************************************
4668 * IsCharBlankW (kernelbase.@)
4670 BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
4672 return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK);
4676 /******************************************************************************
4677 * IsCharCntrlW (kernelbase.@)
4679 BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
4681 return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL);
4685 /******************************************************************************
4686 * IsCharDigitW (kernelbase.@)
4688 BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
4690 return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT);
4694 /******************************************************************************
4695 * IsCharLowerA (kernelbase.@)
4697 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
4699 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
4700 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
4704 /******************************************************************************
4705 * IsCharLowerW (kernelbase.@)
4707 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
4709 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
4713 /******************************************************************************
4714 * IsCharPunctW (kernelbase.@)
4716 BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
4718 return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT);
4722 /******************************************************************************
4723 * IsCharSpaceA (kernelbase.@)
4725 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
4727 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
4728 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
4732 /******************************************************************************
4733 * IsCharSpaceW (kernelbase.@)
4735 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
4737 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
4741 /******************************************************************************
4742 * IsCharUpperA (kernelbase.@)
4744 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
4746 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
4747 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
4751 /******************************************************************************
4752 * IsCharUpperW (kernelbase.@)
4754 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
4756 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
4760 /******************************************************************************
4761 * IsCharXDigitW (kernelbase.@)
4763 BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
4765 return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT);
4769 /******************************************************************************
4770 * IsDBCSLeadByte (kernelbase.@)
4772 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
4774 return nls_info.AnsiTableInfo.DBCSCodePage && nls_info.AnsiTableInfo.DBCSOffsets[testchar];
4778 /******************************************************************************
4779 * IsDBCSLeadByteEx (kernelbase.@)
4781 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
4783 const CPTABLEINFO *table = get_codepage_table( codepage );
4784 return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
4788 /******************************************************************************
4789 * IsNormalizedString (kernelbase.@)
4791 BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
4793 BOOLEAN res;
4794 if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
4795 return res;
4799 /******************************************************************************
4800 * IsValidCodePage (kernelbase.@)
4802 BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
4804 switch (codepage)
4806 case CP_ACP:
4807 case CP_OEMCP:
4808 case CP_MACCP:
4809 case CP_THREAD_ACP:
4810 return FALSE;
4811 case CP_UTF7:
4812 case CP_UTF8:
4813 return TRUE;
4814 default:
4815 return get_codepage_table( codepage ) != NULL;
4820 /******************************************************************************
4821 * IsValidLanguageGroup (kernelbase.@)
4823 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
4825 WCHAR name[10], value[10];
4826 DWORD type, value_len = sizeof(value);
4827 BOOL ret = FALSE;
4828 HKEY key;
4830 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
4832 swprintf( name, ARRAY_SIZE(name), L"%x", id );
4833 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
4834 ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
4836 RegCloseKey( key );
4837 return ret;
4841 /******************************************************************************
4842 * IsValidLocale (kernelbase.@)
4844 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
4846 /* check if language is registered in the kernel32 resources */
4847 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
4848 ULongToPtr( (LOCALE_ILANGUAGE >> 4) + 1 ), LANGIDFROMLCID(lcid)) != 0;
4852 /******************************************************************************
4853 * IsValidLocaleName (kernelbase.@)
4855 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
4857 LCID lcid;
4859 return !RtlLocaleNameToLcid( locale, &lcid, 2 );
4863 /******************************************************************************
4864 * IsValidNLSVersion (kernelbase.@)
4866 DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale,
4867 NLSVERSIONINFOEX *info )
4869 static const GUID GUID_NULL;
4870 NLSVERSIONINFOEX infoex;
4871 DWORD ret;
4873 if (func != COMPARE_STRING)
4875 SetLastError( ERROR_INVALID_PARAMETER );
4876 return FALSE;
4878 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
4879 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
4881 SetLastError( ERROR_INVALID_PARAMETER );
4882 return FALSE;
4884 infoex.dwNLSVersionInfoSize = sizeof(infoex);
4885 if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE;
4887 ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff);
4888 if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL ))
4889 ret = find_sortguid( &info->guidCustomVersion ) != NULL;
4891 if (!ret) SetLastError( ERROR_SUCCESS );
4892 return ret;
4896 /***********************************************************************
4897 * LCIDToLocaleName (kernelbase.@)
4899 INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
4901 static int once;
4902 if (flags && !once++) FIXME( "unsupported flags %x\n", flags );
4904 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
4908 /***********************************************************************
4909 * LCMapStringEx (kernelbase.@)
4911 INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
4912 WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
4913 void *reserved, LPARAM handle )
4915 const struct sortguid *sortid;
4916 LPWSTR dst_ptr;
4917 INT len;
4919 if (version) FIXME( "unsupported version structure %p\n", version );
4920 if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
4921 if (handle)
4923 static int once;
4924 if (!once++) FIXME( "unsupported lparam %lx\n", handle );
4927 if (!src || !srclen || dstlen < 0)
4929 SetLastError( ERROR_INVALID_PARAMETER );
4930 return 0;
4933 /* mutually exclusive flags */
4934 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
4935 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
4936 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
4937 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
4938 !flags)
4940 SetLastError( ERROR_INVALID_FLAGS );
4941 return 0;
4944 if (!dstlen) dst = NULL;
4946 if (!(sortid = get_language_sort( locale )))
4948 FIXME( "unknown locale %s\n", debugstr_w(locale) );
4949 SetLastError( ERROR_INVALID_PARAMETER );
4950 return 0;
4953 if (flags & LCMAP_SORTKEY)
4955 INT ret;
4957 if (src == dst)
4959 SetLastError( ERROR_INVALID_FLAGS );
4960 return 0;
4962 if (srclen < 0) srclen = lstrlenW(src);
4964 TRACE( "(%s,0x%08x,%s,%d,%p,%d)\n",
4965 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
4967 if ((ret = get_sortkey( flags, src, srclen, (char *)dst, dstlen ))) ret++;
4968 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
4969 return ret;
4972 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
4973 if (flags & SORT_STRINGSORT)
4975 SetLastError( ERROR_INVALID_FLAGS );
4976 return 0;
4978 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
4979 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
4980 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
4981 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
4983 SetLastError( ERROR_INVALID_FLAGS );
4984 return 0;
4987 if (srclen < 0) srclen = lstrlenW(src) + 1;
4989 TRACE( "(%s,0x%08x,%s,%d,%p,%d)\n",
4990 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
4992 if (!dst) /* return required string length */
4994 if (flags & NORM_IGNORESYMBOLS)
4996 for (len = 0; srclen; src++, srclen--)
4997 if (!(get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE))) len++;
4999 else if (flags & LCMAP_FULLWIDTH)
5001 for (len = 0; srclen; src++, srclen--, len++)
5003 if (compose_katakana( src, srclen, NULL ) == 2)
5005 src++;
5006 srclen--;
5010 else if (flags & LCMAP_HALFWIDTH)
5012 for (len = 0; srclen; src++, srclen--, len++)
5014 WCHAR wch = *src;
5015 /* map Hiragana to Katakana before decomposition if needed */
5016 if ((flags & LCMAP_KATAKANA) &&
5017 ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E))
5018 wch += 0x60;
5020 if (decompose_katakana( wch, NULL, 0 ) == 2) len++;
5023 else len = srclen;
5024 return len;
5027 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
5029 SetLastError( ERROR_INVALID_FLAGS );
5030 return 0;
5033 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
5035 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
5037 if ((flags & NORM_IGNORESYMBOLS) && (get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE)))
5038 continue;
5039 *dst_ptr++ = *src;
5040 len--;
5042 goto done;
5045 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
5047 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
5049 WCHAR wch;
5050 if (flags & LCMAP_FULLWIDTH)
5052 /* map half-width character to full-width one,
5053 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
5054 if (map_to_fullwidth( src, srclen, &wch ) == 2)
5056 src++;
5057 srclen--;
5060 else wch = *src;
5062 if (flags & LCMAP_KATAKANA)
5064 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
5065 we can't use C3_HIRAGANA as some characters can't map to katakana */
5066 if ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E) wch += 0x60;
5068 else if (flags & LCMAP_HIRAGANA)
5070 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
5071 we can't use C3_KATAKANA as some characters can't map to hiragana */
5072 if ((wch >= 0x30A1 && wch <= 0x30F6) || wch == 0x30FD || wch == 0x30FE) wch -= 0x60;
5075 if (flags & LCMAP_HALFWIDTH)
5077 /* map full-width character to half-width one,
5078 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
5079 if (map_to_halfwidth(wch, dst_ptr, len) == 2)
5081 len--;
5082 dst_ptr++;
5083 if (!len) break;
5086 else *dst_ptr = wch;
5088 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen) goto done;
5090 srclen = dst_ptr - dst;
5091 src = dst;
5094 if (flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
5096 const USHORT *table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
5097 table = table + 2 + (flags & LCMAP_LOWERCASE ? table[1] : 0);
5098 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--, len--)
5099 *dst_ptr++ = casemap( table, *src );
5101 else
5103 len = min( srclen, dstlen );
5104 memcpy( dst, src, len * sizeof(WCHAR) );
5105 dst_ptr = dst + len;
5106 srclen -= len;
5109 done:
5110 if (srclen)
5112 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5113 return 0;
5116 return dst_ptr - dst;
5120 /***********************************************************************
5121 * LCMapStringA (kernelbase.@)
5123 INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
5124 char *dst, int dstlen )
5126 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
5127 LPWSTR srcW, dstW;
5128 INT ret = 0, srclenW, dstlenW;
5129 UINT locale_cp = CP_ACP;
5131 if (!src || !srclen || dstlen < 0)
5133 SetLastError( ERROR_INVALID_PARAMETER );
5134 return 0;
5137 locale_cp = get_lcid_codepage( lcid, flags );
5139 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
5140 if (srclenW) srcW = bufW;
5141 else
5143 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
5144 srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
5145 if (!srcW)
5147 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5148 return 0;
5150 MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
5153 if (flags & LCMAP_SORTKEY)
5155 if (src == dst)
5157 SetLastError( ERROR_INVALID_FLAGS );
5158 goto done;
5160 ret = LCMapStringEx( NULL, flags, srcW, srclenW, (WCHAR *)dst, dstlen, NULL, NULL, 0 );
5161 goto done;
5164 if (flags & SORT_STRINGSORT)
5166 SetLastError( ERROR_INVALID_FLAGS );
5167 goto done;
5170 dstlenW = LCMapStringEx( NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0 );
5171 if (!dstlenW) goto done;
5173 dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
5174 if (!dstW)
5176 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
5177 goto done;
5179 LCMapStringEx( NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0 );
5180 ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
5181 HeapFree( GetProcessHeap(), 0, dstW );
5183 done:
5184 if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
5185 return ret;
5189 /***********************************************************************
5190 * LCMapStringW (kernelbase.@)
5192 INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
5193 WCHAR *dst, int dstlen )
5195 return LCMapStringEx( NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
5199 /***********************************************************************
5200 * LocaleNameToLCID (kernelbase.@)
5202 LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
5204 LCID lcid;
5206 if (!name) return GetUserDefaultLCID();
5207 if (!set_ntstatus( RtlLocaleNameToLcid( name, &lcid, 2 ))) return 0;
5208 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES)) lcid = ConvertDefaultLocale( lcid );
5209 return lcid;
5213 /******************************************************************************
5214 * MultiByteToWideChar (kernelbase.@)
5216 INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
5217 WCHAR *dst, INT dstlen )
5219 int ret;
5221 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
5223 SetLastError( ERROR_INVALID_PARAMETER );
5224 return 0;
5226 if (srclen < 0) srclen = strlen(src) + 1;
5228 switch (codepage)
5230 case CP_SYMBOL:
5231 ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
5232 break;
5233 case CP_UTF7:
5234 ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
5235 break;
5236 case CP_UTF8:
5237 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
5238 break;
5239 case CP_UNIXCP:
5240 codepage = __wine_get_unix_codepage();
5241 if (codepage == CP_UTF8)
5243 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
5244 #ifdef __APPLE__ /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
5245 if (ret && dstlen) RtlNormalizeString( NormalizationC, dst, ret, dst, &ret );
5246 #endif
5247 break;
5249 /* fall through */
5250 default:
5251 ret = mbstowcs_codepage( codepage, flags, src, srclen, dst, dstlen );
5252 break;
5254 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
5255 return ret;
5259 /******************************************************************************
5260 * NormalizeString (kernelbase.@)
5262 INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
5263 WCHAR *dst, INT dst_len)
5265 NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len );
5267 switch (status)
5269 case STATUS_OBJECT_NAME_NOT_FOUND:
5270 status = STATUS_INVALID_PARAMETER;
5271 break;
5272 case STATUS_BUFFER_TOO_SMALL:
5273 case STATUS_NO_UNICODE_TRANSLATION:
5274 dst_len = -dst_len;
5275 break;
5277 SetLastError( RtlNtStatusToDosError( status ));
5278 return dst_len;
5282 /******************************************************************************
5283 * ResolveLocaleName (kernelbase.@)
5285 INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
5287 FIXME( "stub: %s, %p, %d\n", wine_dbgstr_w(name), buffer, len );
5289 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
5290 return 0;
5294 /******************************************************************************
5295 * SetLocaleInfoW (kernelbase.@)
5297 BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
5299 const struct registry_value *value;
5300 DWORD index;
5301 LSTATUS status;
5303 lctype = LOWORD(lctype);
5304 value = get_locale_registry_value( lctype );
5306 if (!data || !value)
5308 SetLastError( ERROR_INVALID_PARAMETER );
5309 return FALSE;
5312 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
5314 SetLastError( ERROR_INVALID_FLAGS );
5315 return FALSE;
5318 TRACE( "setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
5320 /* FIXME: should check that data to set is sane */
5322 status = RegSetValueExW( intl_key, value->name, 0, REG_SZ, (BYTE *)data, (lstrlenW(data)+1)*sizeof(WCHAR) );
5323 index = value - registry_values;
5325 RtlEnterCriticalSection( &locale_section );
5326 HeapFree( GetProcessHeap(), 0, registry_cache[index] );
5327 registry_cache[index] = NULL;
5328 RtlLeaveCriticalSection( &locale_section );
5330 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
5332 /* Set I-value from S value */
5333 WCHAR *pD, *pM, *pY, buf[2];
5335 pD = wcschr( data, 'd' );
5336 pM = wcschr( data, 'M' );
5337 pY = wcschr( data, 'y' );
5339 if (pD <= pM) buf[0] = '1'; /* D-M-Y */
5340 else if (pY <= pM) buf[0] = '2'; /* Y-M-D */
5341 else buf[0] = '0'; /* M-D-Y */
5342 buf[1] = 0;
5344 lctype = (lctype == LOCALE_SSHORTDATE) ? LOCALE_IDATE : LOCALE_ILDATE;
5345 value = get_locale_registry_value( lctype );
5346 index = value - registry_values;
5348 RegSetValueExW( intl_key, value->name, 0, REG_SZ, (BYTE *)buf, sizeof(buf) );
5350 RtlEnterCriticalSection( &locale_section );
5351 HeapFree( GetProcessHeap(), 0, registry_cache[index] );
5352 registry_cache[index] = NULL;
5353 RtlLeaveCriticalSection( &locale_section );
5355 return set_ntstatus( status );
5359 /***********************************************************************
5360 * SetCalendarInfoW (kernelbase.@)
5362 INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
5364 FIXME( "(%08x,%08x,%08x,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
5365 return 0;
5369 /***********************************************************************
5370 * SetTimeZoneInformation (kernelbase.@)
5372 BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info )
5374 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info ));
5378 /******************************************************************************
5379 * SetUserGeoID (kernelbase.@)
5381 BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id )
5383 const struct geoinfo *geoinfo = get_geoinfo_ptr( id );
5384 WCHAR bufferW[10];
5385 HKEY hkey;
5387 if (!geoinfo)
5389 SetLastError( ERROR_INVALID_PARAMETER );
5390 return FALSE;
5392 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
5394 const WCHAR *name = geoinfo->kind == LOCATION_NATION ? L"Nation" : L"Region";
5395 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geoinfo->id );
5396 RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
5397 RegCloseKey( hkey );
5399 return TRUE;
5403 /***********************************************************************
5404 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
5406 BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info,
5407 const SYSTEMTIME *system,
5408 SYSTEMTIME *local )
5410 TIME_ZONE_INFORMATION tzinfo;
5411 LARGE_INTEGER ft;
5413 if (!info)
5415 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
5416 info = &tzinfo;
5419 if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE;
5420 switch (get_timezone_id( info, ft, FALSE ))
5422 case TIME_ZONE_ID_UNKNOWN:
5423 ft.QuadPart -= info->Bias * (LONGLONG)600000000;
5424 break;
5425 case TIME_ZONE_ID_STANDARD:
5426 ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000;
5427 break;
5428 case TIME_ZONE_ID_DAYLIGHT:
5429 ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
5430 break;
5431 default:
5432 return FALSE;
5434 return FileTimeToSystemTime( (FILETIME *)&ft, local );
5438 /***********************************************************************
5439 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
5441 BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info,
5442 const SYSTEMTIME *local,
5443 SYSTEMTIME *system )
5445 TIME_ZONE_INFORMATION tzinfo;
5446 LARGE_INTEGER ft;
5448 if (!info)
5450 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
5451 info = &tzinfo;
5454 if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE;
5455 switch (get_timezone_id( info, ft, TRUE ))
5457 case TIME_ZONE_ID_UNKNOWN:
5458 ft.QuadPart += info->Bias * (LONGLONG)600000000;
5459 break;
5460 case TIME_ZONE_ID_STANDARD:
5461 ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000;
5462 break;
5463 case TIME_ZONE_ID_DAYLIGHT:
5464 ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
5465 break;
5466 default:
5467 return FALSE;
5469 return FileTimeToSystemTime( (FILETIME *)&ft, system );
5473 /***********************************************************************
5474 * VerLanguageNameA (kernelbase.@)
5476 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
5478 return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
5482 /***********************************************************************
5483 * VerLanguageNameW (kernelbase.@)
5485 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
5487 return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
5491 /***********************************************************************
5492 * WideCharToMultiByte (kernelbase.@)
5494 INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
5495 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
5497 int ret;
5499 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
5501 SetLastError( ERROR_INVALID_PARAMETER );
5502 return 0;
5505 if (srclen < 0) srclen = lstrlenW(src) + 1;
5507 switch (codepage)
5509 case CP_SYMBOL:
5510 ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
5511 break;
5512 case CP_UTF7:
5513 ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
5514 break;
5515 case CP_UTF8:
5516 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
5517 break;
5518 case CP_UNIXCP:
5519 codepage = __wine_get_unix_codepage();
5520 if (codepage == CP_UTF8)
5522 if (used) *used = FALSE;
5523 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, NULL, NULL );
5524 break;
5526 /* fall through */
5527 default:
5528 ret = wcstombs_codepage( codepage, flags, src, srclen, dst, dstlen, defchar, used );
5529 break;
5531 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
5532 return ret;