kernelbase: Reimplement LOCALE_FONTSIGNATURE/SOPENTYPELANGUAGETAG in GetLocaleInfoW...
[wine.git] / dlls / kernelbase / locale.c
blob8cb2120357c65c1855b3755e806e3771d6ee1c61
1 /*
2 * Locale support
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2003 Jon Griffiths
8 * Copyright 2005 Dmitry Timoshkov
9 * Copyright 2002, 2019 Alexandre Julliard
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <stdarg.h>
27 #include <stdlib.h>
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #define WINNORMALIZEAPI
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winreg.h"
35 #include "winnls.h"
36 #include "winuser.h"
37 #include "winternl.h"
38 #include "kernelbase.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(nls);
43 #define CALINFO_MAX_YEAR 2029
45 extern const unsigned int collation_table[] DECLSPEC_HIDDEN;
47 static HANDLE kernel32_handle;
49 static const struct registry_value
51 DWORD lctype;
52 const WCHAR *name;
53 } registry_values[] =
55 { LOCALE_ICALENDARTYPE, L"iCalendarType" },
56 { LOCALE_ICURRDIGITS, L"iCurrDigits" },
57 { LOCALE_ICURRENCY, L"iCurrency" },
58 { LOCALE_IDIGITS, L"iDigits" },
59 { LOCALE_IFIRSTDAYOFWEEK, L"iFirstDayOfWeek" },
60 { LOCALE_IFIRSTWEEKOFYEAR, L"iFirstWeekOfYear" },
61 { LOCALE_ILZERO, L"iLZero" },
62 { LOCALE_IMEASURE, L"iMeasure" },
63 { LOCALE_INEGCURR, L"iNegCurr" },
64 { LOCALE_INEGNUMBER, L"iNegNumber" },
65 { LOCALE_IPAPERSIZE, L"iPaperSize" },
66 { LOCALE_ITIME, L"iTime" },
67 { LOCALE_S1159, L"s1159" },
68 { LOCALE_S2359, L"s2359" },
69 { LOCALE_SCURRENCY, L"sCurrency" },
70 { LOCALE_SDATE, L"sDate" },
71 { LOCALE_SDECIMAL, L"sDecimal" },
72 { LOCALE_SGROUPING, L"sGrouping" },
73 { LOCALE_SLIST, L"sList" },
74 { LOCALE_SLONGDATE, L"sLongDate" },
75 { LOCALE_SMONDECIMALSEP, L"sMonDecimalSep" },
76 { LOCALE_SMONGROUPING, L"sMonGrouping" },
77 { LOCALE_SMONTHOUSANDSEP, L"sMonThousandSep" },
78 { LOCALE_SNEGATIVESIGN, L"sNegativeSign" },
79 { LOCALE_SPOSITIVESIGN, L"sPositiveSign" },
80 { LOCALE_SSHORTDATE, L"sShortDate" },
81 { LOCALE_STHOUSAND, L"sThousand" },
82 { LOCALE_STIME, L"sTime" },
83 { LOCALE_STIMEFORMAT, L"sTimeFormat" },
84 { LOCALE_SYEARMONTH, L"sYearMonth" },
85 /* The following are not listed under MSDN as supported,
86 * but seem to be used and also stored in the registry.
88 { LOCALE_SNAME, L"LocaleName" },
89 { LOCALE_ICOUNTRY, L"iCountry" },
90 { LOCALE_IDATE, L"iDate" },
91 { LOCALE_ILDATE, L"iLDate" },
92 { LOCALE_ITLZERO, L"iTLZero" },
93 { LOCALE_SCOUNTRY, L"sCountry" },
94 { LOCALE_SABBREVLANGNAME, L"sLanguage" },
95 { LOCALE_IDIGITSUBSTITUTION, L"Numshape" },
96 { LOCALE_SNATIVEDIGITS, L"sNativeDigits" },
97 { LOCALE_ITIMEMARKPOSN, L"iTimePrefix" },
100 static WCHAR *registry_cache[ARRAY_SIZE(registry_values)];
102 static const struct { UINT cp; const WCHAR *name; } codepage_names[] =
104 { 37, L"IBM EBCDIC US Canada" },
105 { 424, L"IBM EBCDIC Hebrew" },
106 { 437, L"OEM United States" },
107 { 500, L"IBM EBCDIC International" },
108 { 708, L"Arabic ASMO" },
109 { 720, L"Arabic (Transparent ASMO)" },
110 { 737, L"OEM Greek 437G" },
111 { 775, L"OEM Baltic" },
112 { 850, L"OEM Multilingual Latin 1" },
113 { 852, L"OEM Slovak Latin 2" },
114 { 855, L"OEM Cyrillic" },
115 { 856, L"Hebrew PC" },
116 { 857, L"OEM Turkish" },
117 { 860, L"OEM Portuguese" },
118 { 861, L"OEM Icelandic" },
119 { 862, L"OEM Hebrew" },
120 { 863, L"OEM Canadian French" },
121 { 864, L"OEM Arabic" },
122 { 865, L"OEM Nordic" },
123 { 866, L"OEM Russian" },
124 { 869, L"OEM Greek" },
125 { 874, L"ANSI/OEM Thai" },
126 { 875, L"IBM EBCDIC Greek" },
127 { 878, L"Russian KOI8" },
128 { 932, L"ANSI/OEM Japanese Shift-JIS" },
129 { 936, L"ANSI/OEM Simplified Chinese GBK" },
130 { 949, L"ANSI/OEM Korean Unified Hangul" },
131 { 950, L"ANSI/OEM Traditional Chinese Big5" },
132 { 1006, L"IBM Arabic" },
133 { 1026, L"IBM EBCDIC Latin 5 Turkish" },
134 { 1250, L"ANSI Eastern Europe" },
135 { 1251, L"ANSI Cyrillic" },
136 { 1252, L"ANSI Latin 1" },
137 { 1253, L"ANSI Greek" },
138 { 1254, L"ANSI Turkish" },
139 { 1255, L"ANSI Hebrew" },
140 { 1256, L"ANSI Arabic" },
141 { 1257, L"ANSI Baltic" },
142 { 1258, L"ANSI/OEM Viet Nam" },
143 { 1361, L"Korean Johab" },
144 { 10000, L"Mac Roman" },
145 { 10001, L"Mac Japanese" },
146 { 10002, L"Mac Traditional Chinese" },
147 { 10003, L"Mac Korean" },
148 { 10004, L"Mac Arabic" },
149 { 10005, L"Mac Hebrew" },
150 { 10006, L"Mac Greek" },
151 { 10007, L"Mac Cyrillic" },
152 { 10008, L"Mac Simplified Chinese" },
153 { 10010, L"Mac Romanian" },
154 { 10017, L"Mac Ukrainian" },
155 { 10021, L"Mac Thai" },
156 { 10029, L"Mac Latin 2" },
157 { 10079, L"Mac Icelandic" },
158 { 10081, L"Mac Turkish" },
159 { 10082, L"Mac Croatian" },
160 { 20127, L"US-ASCII (7bit)" },
161 { 20866, L"Russian KOI8" },
162 { 20932, L"EUC-JP" },
163 { 20949, L"Korean Wansung" },
164 { 21866, L"Ukrainian KOI8" },
165 { 28591, L"ISO 8859-1 Latin 1" },
166 { 28592, L"ISO 8859-2 Latin 2 (East European)" },
167 { 28593, L"ISO 8859-3 Latin 3 (South European)" },
168 { 28594, L"ISO 8859-4 Latin 4 (Baltic old)" },
169 { 28595, L"ISO 8859-5 Cyrillic" },
170 { 28596, L"ISO 8859-6 Arabic" },
171 { 28597, L"ISO 8859-7 Greek" },
172 { 28598, L"ISO 8859-8 Hebrew" },
173 { 28599, L"ISO 8859-9 Latin 5 (Turkish)" },
174 { 28600, L"ISO 8859-10 Latin 6 (Nordic)" },
175 { 28601, L"ISO 8859-11 Latin (Thai)" },
176 { 28603, L"ISO 8859-13 Latin 7 (Baltic)" },
177 { 28604, L"ISO 8859-14 Latin 8 (Celtic)" },
178 { 28605, L"ISO 8859-15 Latin 9 (Euro)" },
179 { 28606, L"ISO 8859-16 Latin 10 (Balkan)" },
180 { 65000, L"Unicode (UTF-7)" },
181 { 65001, L"Unicode (UTF-8)" }
184 /* Unicode expanded ligatures */
185 static const WCHAR ligatures[][5] =
187 { 0x00c6, 'A','E',0 },
188 { 0x00de, 'T','H',0 },
189 { 0x00df, 's','s',0 },
190 { 0x00e6, 'a','e',0 },
191 { 0x00fe, 't','h',0 },
192 { 0x0132, 'I','J',0 },
193 { 0x0133, 'i','j',0 },
194 { 0x0152, 'O','E',0 },
195 { 0x0153, 'o','e',0 },
196 { 0x01c4, 'D',0x017d,0 },
197 { 0x01c5, 'D',0x017e,0 },
198 { 0x01c6, 'd',0x017e,0 },
199 { 0x01c7, 'L','J',0 },
200 { 0x01c8, 'L','j',0 },
201 { 0x01c9, 'l','j',0 },
202 { 0x01ca, 'N','J',0 },
203 { 0x01cb, 'N','j',0 },
204 { 0x01cc, 'n','j',0 },
205 { 0x01e2, 0x0100,0x0112,0 },
206 { 0x01e3, 0x0101,0x0113,0 },
207 { 0x01f1, 'D','Z',0 },
208 { 0x01f2, 'D','z',0 },
209 { 0x01f3, 'd','z',0 },
210 { 0x01fc, 0x00c1,0x00c9,0 },
211 { 0x01fd, 0x00e1,0x00e9,0 },
212 { 0x05f0, 0x05d5,0x05d5,0 },
213 { 0x05f1, 0x05d5,0x05d9,0 },
214 { 0x05f2, 0x05d9,0x05d9,0 },
215 { 0xfb00, 'f','f',0 },
216 { 0xfb01, 'f','i',0 },
217 { 0xfb02, 'f','l',0 },
218 { 0xfb03, 'f','f','i',0 },
219 { 0xfb04, 'f','f','l',0 },
220 { 0xfb05, 0x017f,'t',0 },
221 { 0xfb06, 's','t',0 },
224 enum locationkind { LOCATION_NATION = 0, LOCATION_REGION, LOCATION_BOTH };
226 struct geoinfo
228 GEOID id;
229 WCHAR iso2W[3];
230 WCHAR iso3W[4];
231 GEOID parent;
232 int uncode;
233 enum locationkind kind;
236 static const struct geoinfo geoinfodata[] =
238 { 2, L"AG", L"ATG", 10039880, 28 }, /* Antigua and Barbuda */
239 { 3, L"AF", L"AFG", 47614, 4 }, /* Afghanistan */
240 { 4, L"DZ", L"DZA", 42487, 12 }, /* Algeria */
241 { 5, L"AZ", L"AZE", 47611, 31 }, /* Azerbaijan */
242 { 6, L"AL", L"ALB", 47610, 8 }, /* Albania */
243 { 7, L"AM", L"ARM", 47611, 51 }, /* Armenia */
244 { 8, L"AD", L"AND", 47610, 20 }, /* Andorra */
245 { 9, L"AO", L"AGO", 42484, 24 }, /* Angola */
246 { 10, L"AS", L"ASM", 26286, 16 }, /* American Samoa */
247 { 11, L"AR", L"ARG", 31396, 32 }, /* Argentina */
248 { 12, L"AU", L"AUS", 10210825, 36 }, /* Australia */
249 { 14, L"AT", L"AUT", 10210824, 40 }, /* Austria */
250 { 17, L"BH", L"BHR", 47611, 48 }, /* Bahrain */
251 { 18, L"BB", L"BRB", 10039880, 52 }, /* Barbados */
252 { 19, L"BW", L"BWA", 10039883, 72 }, /* Botswana */
253 { 20, L"BM", L"BMU", 23581, 60 }, /* Bermuda */
254 { 21, L"BE", L"BEL", 10210824, 56 }, /* Belgium */
255 { 22, L"BS", L"BHS", 10039880, 44 }, /* Bahamas, The */
256 { 23, L"BD", L"BGD", 47614, 50 }, /* Bangladesh */
257 { 24, L"BZ", L"BLZ", 27082, 84 }, /* Belize */
258 { 25, L"BA", L"BIH", 47610, 70 }, /* Bosnia and Herzegovina */
259 { 26, L"BO", L"BOL", 31396, 68 }, /* Bolivia */
260 { 27, L"MM", L"MMR", 47599, 104 }, /* Myanmar */
261 { 28, L"BJ", L"BEN", 42483, 204 }, /* Benin */
262 { 29, L"BY", L"BLR", 47609, 112 }, /* Belarus */
263 { 30, L"SB", L"SLB", 20900, 90 }, /* Solomon Islands */
264 { 32, L"BR", L"BRA", 31396, 76 }, /* Brazil */
265 { 34, L"BT", L"BTN", 47614, 64 }, /* Bhutan */
266 { 35, L"BG", L"BGR", 47609, 100 }, /* Bulgaria */
267 { 37, L"BN", L"BRN", 47599, 96 }, /* Brunei */
268 { 38, L"BI", L"BDI", 47603, 108 }, /* Burundi */
269 { 39, L"CA", L"CAN", 23581, 124 }, /* Canada */
270 { 40, L"KH", L"KHM", 47599, 116 }, /* Cambodia */
271 { 41, L"TD", L"TCD", 42484, 148 }, /* Chad */
272 { 42, L"LK", L"LKA", 47614, 144 }, /* Sri Lanka */
273 { 43, L"CG", L"COG", 42484, 178 }, /* Congo */
274 { 44, L"CD", L"COD", 42484, 180 }, /* Congo (DRC) */
275 { 45, L"CN", L"CHN", 47600, 156 }, /* China */
276 { 46, L"CL", L"CHL", 31396, 152 }, /* Chile */
277 { 49, L"CM", L"CMR", 42484, 120 }, /* Cameroon */
278 { 50, L"KM", L"COM", 47603, 174 }, /* Comoros */
279 { 51, L"CO", L"COL", 31396, 170 }, /* Colombia */
280 { 54, L"CR", L"CRI", 27082, 188 }, /* Costa Rica */
281 { 55, L"CF", L"CAF", 42484, 140 }, /* Central African Republic */
282 { 56, L"CU", L"CUB", 10039880, 192 }, /* Cuba */
283 { 57, L"CV", L"CPV", 42483, 132 }, /* Cape Verde */
284 { 59, L"CY", L"CYP", 47611, 196 }, /* Cyprus */
285 { 61, L"DK", L"DNK", 10039882, 208 }, /* Denmark */
286 { 62, L"DJ", L"DJI", 47603, 262 }, /* Djibouti */
287 { 63, L"DM", L"DMA", 10039880, 212 }, /* Dominica */
288 { 65, L"DO", L"DOM", 10039880, 214 }, /* Dominican Republic */
289 { 66, L"EC", L"ECU", 31396, 218 }, /* Ecuador */
290 { 67, L"EG", L"EGY", 42487, 818 }, /* Egypt */
291 { 68, L"IE", L"IRL", 10039882, 372 }, /* Ireland */
292 { 69, L"GQ", L"GNQ", 42484, 226 }, /* Equatorial Guinea */
293 { 70, L"EE", L"EST", 10039882, 233 }, /* Estonia */
294 { 71, L"ER", L"ERI", 47603, 232 }, /* Eritrea */
295 { 72, L"SV", L"SLV", 27082, 222 }, /* El Salvador */
296 { 73, L"ET", L"ETH", 47603, 231 }, /* Ethiopia */
297 { 75, L"CZ", L"CZE", 47609, 203 }, /* Czech Republic */
298 { 77, L"FI", L"FIN", 10039882, 246 }, /* Finland */
299 { 78, L"FJ", L"FJI", 20900, 242 }, /* Fiji Islands */
300 { 80, L"FM", L"FSM", 21206, 583 }, /* Micronesia */
301 { 81, L"FO", L"FRO", 10039882, 234 }, /* Faroe Islands */
302 { 84, L"FR", L"FRA", 10210824, 250 }, /* France */
303 { 86, L"GM", L"GMB", 42483, 270 }, /* Gambia, The */
304 { 87, L"GA", L"GAB", 42484, 266 }, /* Gabon */
305 { 88, L"GE", L"GEO", 47611, 268 }, /* Georgia */
306 { 89, L"GH", L"GHA", 42483, 288 }, /* Ghana */
307 { 90, L"GI", L"GIB", 47610, 292 }, /* Gibraltar */
308 { 91, L"GD", L"GRD", 10039880, 308 }, /* Grenada */
309 { 93, L"GL", L"GRL", 23581, 304 }, /* Greenland */
310 { 94, L"DE", L"DEU", 10210824, 276 }, /* Germany */
311 { 98, L"GR", L"GRC", 47610, 300 }, /* Greece */
312 { 99, L"GT", L"GTM", 27082, 320 }, /* Guatemala */
313 { 100, L"GN", L"GIN", 42483, 324 }, /* Guinea */
314 { 101, L"GY", L"GUY", 31396, 328 }, /* Guyana */
315 { 103, L"HT", L"HTI", 10039880, 332 }, /* Haiti */
316 { 104, L"HK", L"HKG", 47600, 344 }, /* Hong Kong S.A.R. */
317 { 106, L"HN", L"HND", 27082, 340 }, /* Honduras */
318 { 108, L"HR", L"HRV", 47610, 191 }, /* Croatia */
319 { 109, L"HU", L"HUN", 47609, 348 }, /* Hungary */
320 { 110, L"IS", L"ISL", 10039882, 352 }, /* Iceland */
321 { 111, L"ID", L"IDN", 47599, 360 }, /* Indonesia */
322 { 113, L"IN", L"IND", 47614, 356 }, /* India */
323 { 114, L"IO", L"IOT", 39070, 86 }, /* British Indian Ocean Territory */
324 { 116, L"IR", L"IRN", 47614, 364 }, /* Iran */
325 { 117, L"IL", L"ISR", 47611, 376 }, /* Israel */
326 { 118, L"IT", L"ITA", 47610, 380 }, /* Italy */
327 { 119, L"CI", L"CIV", 42483, 384 }, /* Côte d'Ivoire */
328 { 121, L"IQ", L"IRQ", 47611, 368 }, /* Iraq */
329 { 122, L"JP", L"JPN", 47600, 392 }, /* Japan */
330 { 124, L"JM", L"JAM", 10039880, 388 }, /* Jamaica */
331 { 125, L"SJ", L"SJM", 10039882, 744 }, /* Jan Mayen */
332 { 126, L"JO", L"JOR", 47611, 400 }, /* Jordan */
333 { 127, L"XX", L"XX", 161832256 }, /* Johnston Atoll */
334 { 129, L"KE", L"KEN", 47603, 404 }, /* Kenya */
335 { 130, L"KG", L"KGZ", 47590, 417 }, /* Kyrgyzstan */
336 { 131, L"KP", L"PRK", 47600, 408 }, /* North Korea */
337 { 133, L"KI", L"KIR", 21206, 296 }, /* Kiribati */
338 { 134, L"KR", L"KOR", 47600, 410 }, /* Korea */
339 { 136, L"KW", L"KWT", 47611, 414 }, /* Kuwait */
340 { 137, L"KZ", L"KAZ", 47590, 398 }, /* Kazakhstan */
341 { 138, L"LA", L"LAO", 47599, 418 }, /* Laos */
342 { 139, L"LB", L"LBN", 47611, 422 }, /* Lebanon */
343 { 140, L"LV", L"LVA", 10039882, 428 }, /* Latvia */
344 { 141, L"LT", L"LTU", 10039882, 440 }, /* Lithuania */
345 { 142, L"LR", L"LBR", 42483, 430 }, /* Liberia */
346 { 143, L"SK", L"SVK", 47609, 703 }, /* Slovakia */
347 { 145, L"LI", L"LIE", 10210824, 438 }, /* Liechtenstein */
348 { 146, L"LS", L"LSO", 10039883, 426 }, /* Lesotho */
349 { 147, L"LU", L"LUX", 10210824, 442 }, /* Luxembourg */
350 { 148, L"LY", L"LBY", 42487, 434 }, /* Libya */
351 { 149, L"MG", L"MDG", 47603, 450 }, /* Madagascar */
352 { 151, L"MO", L"MAC", 47600, 446 }, /* Macao S.A.R. */
353 { 152, L"MD", L"MDA", 47609, 498 }, /* Moldova */
354 { 154, L"MN", L"MNG", 47600, 496 }, /* Mongolia */
355 { 156, L"MW", L"MWI", 47603, 454 }, /* Malawi */
356 { 157, L"ML", L"MLI", 42483, 466 }, /* Mali */
357 { 158, L"MC", L"MCO", 10210824, 492 }, /* Monaco */
358 { 159, L"MA", L"MAR", 42487, 504 }, /* Morocco */
359 { 160, L"MU", L"MUS", 47603, 480 }, /* Mauritius */
360 { 162, L"MR", L"MRT", 42483, 478 }, /* Mauritania */
361 { 163, L"MT", L"MLT", 47610, 470 }, /* Malta */
362 { 164, L"OM", L"OMN", 47611, 512 }, /* Oman */
363 { 165, L"MV", L"MDV", 47614, 462 }, /* Maldives */
364 { 166, L"MX", L"MEX", 27082, 484 }, /* Mexico */
365 { 167, L"MY", L"MYS", 47599, 458 }, /* Malaysia */
366 { 168, L"MZ", L"MOZ", 47603, 508 }, /* Mozambique */
367 { 173, L"NE", L"NER", 42483, 562 }, /* Niger */
368 { 174, L"VU", L"VUT", 20900, 548 }, /* Vanuatu */
369 { 175, L"NG", L"NGA", 42483, 566 }, /* Nigeria */
370 { 176, L"NL", L"NLD", 10210824, 528 }, /* Netherlands */
371 { 177, L"NO", L"NOR", 10039882, 578 }, /* Norway */
372 { 178, L"NP", L"NPL", 47614, 524 }, /* Nepal */
373 { 180, L"NR", L"NRU", 21206, 520 }, /* Nauru */
374 { 181, L"SR", L"SUR", 31396, 740 }, /* Suriname */
375 { 182, L"NI", L"NIC", 27082, 558 }, /* Nicaragua */
376 { 183, L"NZ", L"NZL", 10210825, 554 }, /* New Zealand */
377 { 184, L"PS", L"PSE", 47611, 275 }, /* Palestinian Authority */
378 { 185, L"PY", L"PRY", 31396, 600 }, /* Paraguay */
379 { 187, L"PE", L"PER", 31396, 604 }, /* Peru */
380 { 190, L"PK", L"PAK", 47614, 586 }, /* Pakistan */
381 { 191, L"PL", L"POL", 47609, 616 }, /* Poland */
382 { 192, L"PA", L"PAN", 27082, 591 }, /* Panama */
383 { 193, L"PT", L"PRT", 47610, 620 }, /* Portugal */
384 { 194, L"PG", L"PNG", 20900, 598 }, /* Papua New Guinea */
385 { 195, L"PW", L"PLW", 21206, 585 }, /* Palau */
386 { 196, L"GW", L"GNB", 42483, 624 }, /* Guinea-Bissau */
387 { 197, L"QA", L"QAT", 47611, 634 }, /* Qatar */
388 { 198, L"RE", L"REU", 47603, 638 }, /* Reunion */
389 { 199, L"MH", L"MHL", 21206, 584 }, /* Marshall Islands */
390 { 200, L"RO", L"ROU", 47609, 642 }, /* Romania */
391 { 201, L"PH", L"PHL", 47599, 608 }, /* Philippines */
392 { 202, L"PR", L"PRI", 10039880, 630 }, /* Puerto Rico */
393 { 203, L"RU", L"RUS", 47609, 643 }, /* Russia */
394 { 204, L"RW", L"RWA", 47603, 646 }, /* Rwanda */
395 { 205, L"SA", L"SAU", 47611, 682 }, /* Saudi Arabia */
396 { 206, L"PM", L"SPM", 23581, 666 }, /* St. Pierre and Miquelon */
397 { 207, L"KN", L"KNA", 10039880, 659 }, /* St. Kitts and Nevis */
398 { 208, L"SC", L"SYC", 47603, 690 }, /* Seychelles */
399 { 209, L"ZA", L"ZAF", 10039883, 710 }, /* South Africa */
400 { 210, L"SN", L"SEN", 42483, 686 }, /* Senegal */
401 { 212, L"SI", L"SVN", 47610, 705 }, /* Slovenia */
402 { 213, L"SL", L"SLE", 42483, 694 }, /* Sierra Leone */
403 { 214, L"SM", L"SMR", 47610, 674 }, /* San Marino */
404 { 215, L"SG", L"SGP", 47599, 702 }, /* Singapore */
405 { 216, L"SO", L"SOM", 47603, 706 }, /* Somalia */
406 { 217, L"ES", L"ESP", 47610, 724 }, /* Spain */
407 { 218, L"LC", L"LCA", 10039880, 662 }, /* St. Lucia */
408 { 219, L"SD", L"SDN", 42487, 736 }, /* Sudan */
409 { 220, L"SJ", L"SJM", 10039882, 744 }, /* Svalbard */
410 { 221, L"SE", L"SWE", 10039882, 752 }, /* Sweden */
411 { 222, L"SY", L"SYR", 47611, 760 }, /* Syria */
412 { 223, L"CH", L"CHE", 10210824, 756 }, /* Switzerland */
413 { 224, L"AE", L"ARE", 47611, 784 }, /* United Arab Emirates */
414 { 225, L"TT", L"TTO", 10039880, 780 }, /* Trinidad and Tobago */
415 { 227, L"TH", L"THA", 47599, 764 }, /* Thailand */
416 { 228, L"TJ", L"TJK", 47590, 762 }, /* Tajikistan */
417 { 231, L"TO", L"TON", 26286, 776 }, /* Tonga */
418 { 232, L"TG", L"TGO", 42483, 768 }, /* Togo */
419 { 233, L"ST", L"STP", 42484, 678 }, /* São Tomé and Príncipe */
420 { 234, L"TN", L"TUN", 42487, 788 }, /* Tunisia */
421 { 235, L"TR", L"TUR", 47611, 792 }, /* Turkey */
422 { 236, L"TV", L"TUV", 26286, 798 }, /* Tuvalu */
423 { 237, L"TW", L"TWN", 47600, 158 }, /* Taiwan */
424 { 238, L"TM", L"TKM", 47590, 795 }, /* Turkmenistan */
425 { 239, L"TZ", L"TZA", 47603, 834 }, /* Tanzania */
426 { 240, L"UG", L"UGA", 47603, 800 }, /* Uganda */
427 { 241, L"UA", L"UKR", 47609, 804 }, /* Ukraine */
428 { 242, L"GB", L"GBR", 10039882, 826 }, /* United Kingdom */
429 { 244, L"US", L"USA", 23581, 840 }, /* United States */
430 { 245, L"BF", L"BFA", 42483, 854 }, /* Burkina Faso */
431 { 246, L"UY", L"URY", 31396, 858 }, /* Uruguay */
432 { 247, L"UZ", L"UZB", 47590, 860 }, /* Uzbekistan */
433 { 248, L"VC", L"VCT", 10039880, 670 }, /* St. Vincent and the Grenadines */
434 { 249, L"VE", L"VEN", 31396, 862 }, /* Bolivarian Republic of Venezuela */
435 { 251, L"VN", L"VNM", 47599, 704 }, /* Vietnam */
436 { 252, L"VI", L"VIR", 10039880, 850 }, /* Virgin Islands */
437 { 253, L"VA", L"VAT", 47610, 336 }, /* Vatican City */
438 { 254, L"NA", L"NAM", 10039883, 516 }, /* Namibia */
439 { 257, L"EH", L"ESH", 42487, 732 }, /* Western Sahara (disputed) */
440 { 258, L"XX", L"XX", 161832256 }, /* Wake Island */
441 { 259, L"WS", L"WSM", 26286, 882 }, /* Samoa */
442 { 260, L"SZ", L"SWZ", 10039883, 748 }, /* Swaziland */
443 { 261, L"YE", L"YEM", 47611, 887 }, /* Yemen */
444 { 263, L"ZM", L"ZMB", 47603, 894 }, /* Zambia */
445 { 264, L"ZW", L"ZWE", 47603, 716 }, /* Zimbabwe */
446 { 269, L"CS", L"SCG", 47610, 891 }, /* Serbia and Montenegro (Former) */
447 { 270, L"ME", L"MNE", 47610, 499 }, /* Montenegro */
448 { 271, L"RS", L"SRB", 47610, 688 }, /* Serbia */
449 { 273, L"CW", L"CUW", 10039880, 531 }, /* Curaçao */
450 { 276, L"SS", L"SSD", 42487, 728 }, /* South Sudan */
451 { 300, L"AI", L"AIA", 10039880, 660 }, /* Anguilla */
452 { 301, L"AQ", L"ATA", 39070, 10 }, /* Antarctica */
453 { 302, L"AW", L"ABW", 10039880, 533 }, /* Aruba */
454 { 303, L"XX", L"XX", 343 }, /* Ascension Island */
455 { 304, L"XX", L"XX", 10210825 }, /* Ashmore and Cartier Islands */
456 { 305, L"XX", L"XX", 161832256 }, /* Baker Island */
457 { 306, L"BV", L"BVT", 39070, 74 }, /* Bouvet Island */
458 { 307, L"KY", L"CYM", 10039880, 136 }, /* Cayman Islands */
459 { 308, L"XX", L"XX", 10210824, 830, LOCATION_BOTH }, /* Channel Islands */
460 { 309, L"CX", L"CXR", 12, 162 }, /* Christmas Island */
461 { 310, L"XX", L"XX", 27114 }, /* Clipperton Island */
462 { 311, L"CC", L"CCK", 10210825, 166 }, /* Cocos (Keeling) Islands */
463 { 312, L"CK", L"COK", 26286, 184 }, /* Cook Islands */
464 { 313, L"XX", L"XX", 10210825 }, /* Coral Sea Islands */
465 { 314, L"XX", L"XX", 114 }, /* Diego Garcia */
466 { 315, L"FK", L"FLK", 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
467 { 317, L"GF", L"GUF", 31396, 254 }, /* French Guiana */
468 { 318, L"PF", L"PYF", 26286, 258 }, /* French Polynesia */
469 { 319, L"TF", L"ATF", 39070, 260 }, /* French Southern and Antarctic Lands */
470 { 321, L"GP", L"GLP", 10039880, 312 }, /* Guadeloupe */
471 { 322, L"GU", L"GUM", 21206, 316 }, /* Guam */
472 { 323, L"XX", L"XX", 39070 }, /* Guantanamo Bay */
473 { 324, L"GG", L"GGY", 308, 831 }, /* Guernsey */
474 { 325, L"HM", L"HMD", 39070, 334 }, /* Heard Island and McDonald Islands */
475 { 326, L"XX", L"XX", 161832256 }, /* Howland Island */
476 { 327, L"XX", L"XX", 161832256 }, /* Jarvis Island */
477 { 328, L"JE", L"JEY", 308, 832 }, /* Jersey */
478 { 329, L"XX", L"XX", 161832256 }, /* Kingman Reef */
479 { 330, L"MQ", L"MTQ", 10039880, 474 }, /* Martinique */
480 { 331, L"YT", L"MYT", 47603, 175 }, /* Mayotte */
481 { 332, L"MS", L"MSR", 10039880, 500 }, /* Montserrat */
482 { 333, L"AN", L"ANT", 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
483 { 334, L"NC", L"NCL", 20900, 540 }, /* New Caledonia */
484 { 335, L"NU", L"NIU", 26286, 570 }, /* Niue */
485 { 336, L"NF", L"NFK", 10210825, 574 }, /* Norfolk Island */
486 { 337, L"MP", L"MNP", 21206, 580 }, /* Northern Mariana Islands */
487 { 338, L"XX", L"XX", 161832256 }, /* Palmyra Atoll */
488 { 339, L"PN", L"PCN", 26286, 612 }, /* Pitcairn Islands */
489 { 340, L"XX", L"XX", 337 }, /* Rota Island */
490 { 341, L"XX", L"XX", 337 }, /* Saipan */
491 { 342, L"GS", L"SGS", 39070, 239 }, /* South Georgia and the South Sandwich Islands */
492 { 343, L"SH", L"SHN", 42483, 654 }, /* St. Helena */
493 { 346, L"XX", L"XX", 337 }, /* Tinian Island */
494 { 347, L"TK", L"TKL", 26286, 772 }, /* Tokelau */
495 { 348, L"XX", L"XX", 343 }, /* Tristan da Cunha */
496 { 349, L"TC", L"TCA", 10039880, 796 }, /* Turks and Caicos Islands */
497 { 351, L"VG", L"VGB", 10039880, 92 }, /* Virgin Islands, British */
498 { 352, L"WF", L"WLF", 26286, 876 }, /* Wallis and Futuna */
499 { 742, L"XX", L"XX", 39070, 2, LOCATION_REGION }, /* Africa */
500 { 2129, L"XX", L"XX", 39070, 142, LOCATION_REGION }, /* Asia */
501 { 10541, L"XX", L"XX", 39070, 150, LOCATION_REGION }, /* Europe */
502 { 15126, L"IM", L"IMN", 10039882, 833 }, /* Man, Isle of */
503 { 19618, L"MK", L"MKD", 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
504 { 20900, L"XX", L"XX", 27114, 54, LOCATION_REGION }, /* Melanesia */
505 { 21206, L"XX", L"XX", 27114, 57, LOCATION_REGION }, /* Micronesia */
506 { 21242, L"XX", L"XX", 161832256 }, /* Midway Islands */
507 { 23581, L"XX", L"XX", 10026358, 21, LOCATION_REGION }, /* Northern America */
508 { 26286, L"XX", L"XX", 27114, 61, LOCATION_REGION }, /* Polynesia */
509 { 27082, L"XX", L"XX", 161832257, 13, LOCATION_REGION }, /* Central America */
510 { 27114, L"XX", L"XX", 39070, 9, LOCATION_REGION }, /* Oceania */
511 { 30967, L"SX", L"SXM", 10039880, 534 }, /* Sint Maarten (Dutch part) */
512 { 31396, L"XX", L"XX", 161832257, 5, LOCATION_REGION }, /* South America */
513 { 31706, L"MF", L"MAF", 10039880, 663 }, /* Saint Martin (French part) */
514 { 39070, L"XX", L"XX", 39070, 1, LOCATION_REGION }, /* World */
515 { 42483, L"XX", L"XX", 742, 11, LOCATION_REGION }, /* Western Africa */
516 { 42484, L"XX", L"XX", 742, 17, LOCATION_REGION }, /* Middle Africa */
517 { 42487, L"XX", L"XX", 742, 15, LOCATION_REGION }, /* Northern Africa */
518 { 47590, L"XX", L"XX", 2129, 143, LOCATION_REGION }, /* Central Asia */
519 { 47599, L"XX", L"XX", 2129, 35, LOCATION_REGION }, /* South-Eastern Asia */
520 { 47600, L"XX", L"XX", 2129, 30, LOCATION_REGION }, /* Eastern Asia */
521 { 47603, L"XX", L"XX", 742, 14, LOCATION_REGION }, /* Eastern Africa */
522 { 47609, L"XX", L"XX", 10541, 151, LOCATION_REGION }, /* Eastern Europe */
523 { 47610, L"XX", L"XX", 10541, 39, LOCATION_REGION }, /* Southern Europe */
524 { 47611, L"XX", L"XX", 2129, 145, LOCATION_REGION }, /* Middle East */
525 { 47614, L"XX", L"XX", 2129, 34, LOCATION_REGION }, /* Southern Asia */
526 { 7299303, L"TL", L"TLS", 47599, 626 }, /* Democratic Republic of Timor-Leste */
527 { 9914689, L"XK", L"XKS", 47610, 906 }, /* Kosovo */
528 { 10026358, L"XX", L"XX", 39070, 19, LOCATION_REGION }, /* Americas */
529 { 10028789, L"AX", L"ALA", 10039882, 248 }, /* Åland Islands */
530 { 10039880, L"XX", L"XX", 161832257, 29, LOCATION_REGION }, /* Caribbean */
531 { 10039882, L"XX", L"XX", 10541, 154, LOCATION_REGION }, /* Northern Europe */
532 { 10039883, L"XX", L"XX", 742, 18, LOCATION_REGION }, /* Southern Africa */
533 { 10210824, L"XX", L"XX", 10541, 155, LOCATION_REGION }, /* Western Europe */
534 { 10210825, L"XX", L"XX", 27114, 53, LOCATION_REGION }, /* Australia and New Zealand */
535 { 161832015, L"BL", L"BLM", 10039880, 652 }, /* Saint Barthélemy */
536 { 161832256, L"UM", L"UMI", 27114, 581 }, /* U.S. Minor Outlying Islands */
537 { 161832257, L"XX", L"XX", 10026358, 419, LOCATION_REGION }, /* Latin America and the Caribbean */
538 { 161832258, L"BG", L"BES", 10039880, 535 }, /* Bonaire, Sint Eustatius and Saba */
541 /* NLS normalization file */
542 struct norm_table
544 WCHAR name[13]; /* 00 file name */
545 USHORT checksum[3]; /* 1a checksum? */
546 USHORT version[4]; /* 20 Unicode version */
547 USHORT form; /* 28 normalization form */
548 USHORT len_factor; /* 2a factor for length estimates */
549 USHORT unknown1; /* 2c */
550 USHORT decomp_size; /* 2e decomposition hash size */
551 USHORT comp_size; /* 30 composition hash size */
552 USHORT unknown2; /* 32 */
553 USHORT classes; /* 34 combining classes table offset */
554 USHORT props_level1; /* 36 char properties table level 1 offset */
555 USHORT props_level2; /* 38 char properties table level 2 offset */
556 USHORT decomp_hash; /* 3a decomposition hash table offset */
557 USHORT decomp_map; /* 3c decomposition character map table offset */
558 USHORT decomp_seq; /* 3e decomposition character sequences offset */
559 USHORT comp_hash; /* 40 composition hash table offset */
560 USHORT comp_seq; /* 42 composition character sequences offset */
561 /* BYTE[] combining class values */
562 /* BYTE[0x2200] char properties index level 1 */
563 /* BYTE[] char properties index level 2 */
564 /* WORD[] decomposition hash table */
565 /* WORD[] decomposition character map */
566 /* WORD[] decomposition character sequences */
567 /* WORD[] composition hash table */
568 /* WORD[] composition character sequences */
571 static NLSTABLEINFO nls_info;
572 static UINT unix_cp = CP_UTF8;
573 static UINT mac_cp = 10000;
574 static LCID system_lcid;
575 static LCID user_lcid;
576 static HKEY intl_key;
577 static HKEY nls_key;
578 static HKEY tz_key;
579 static const NLS_LOCALE_LCID_INDEX *lcids_index;
580 static const NLS_LOCALE_LCNAME_INDEX *lcnames_index;
581 static const NLS_LOCALE_HEADER *locale_table;
582 static const WCHAR *locale_strings;
583 static const NLS_LOCALE_DATA *system_locale;
584 static const NLS_LOCALE_DATA *user_locale;
586 static CPTABLEINFO codepages[128];
587 static unsigned int nb_codepages;
589 static struct norm_table *norm_info;
591 struct sortguid
593 GUID id; /* sort GUID */
594 DWORD flags; /* flags */
595 DWORD compr; /* offset to compression table */
596 DWORD except; /* exception table offset in sortkey table */
597 DWORD ling_except; /* exception table offset for linguistic casing */
598 DWORD casemap; /* linguistic casemap table offset */
601 #define FLAG_HAS_3_BYTE_WEIGHTS 0x01
602 #define FLAG_REVERSEDIACRITICS 0x10
603 #define FLAG_DOUBLECOMPRESSION 0x20
604 #define FLAG_INVERSECASING 0x40
606 static const struct sortguid *current_locale_sort;
608 static const GUID default_sort_guid = { 0x00000001, 0x57ee, 0x1e5c, { 0x00, 0xb4, 0xd0, 0x00, 0x0b, 0xb1, 0xe1, 0x1e }};
610 static struct
612 DWORD *keys; /* sortkey table, indexed by char */
613 USHORT *casemap; /* casemap table, in l_intl.nls format */
614 WORD *ctypes; /* CT_CTYPE1,2,3 values */
615 BYTE *ctype_idx; /* index to map char to ctypes array entry */
616 DWORD version; /* NLS version */
617 DWORD guid_count; /* number of sort GUIDs */
618 struct sortguid *guids; /* table of sort GUIDs */
619 } sort;
621 static CRITICAL_SECTION locale_section;
622 static CRITICAL_SECTION_DEBUG critsect_debug =
624 0, 0, &locale_section,
625 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
626 0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") }
628 static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
631 static void load_locale_nls(void)
633 struct
635 UINT ctypes;
636 UINT unknown1;
637 UINT unknown2;
638 UINT unknown3;
639 UINT locales;
640 UINT charmaps;
641 UINT geoids;
642 UINT scripts;
643 } *header;
644 LCID lcid;
645 LARGE_INTEGER dummy;
647 RtlGetLocaleFileMappingAddress( (void **)&header, &lcid, &dummy );
648 locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales);
649 lcids_index = (const NLS_LOCALE_LCID_INDEX *)((char *)locale_table + locale_table->lcids_offset);
650 lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset);
651 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
655 static void init_sortkeys( DWORD *ptr )
657 WORD *ctype;
658 DWORD *table;
660 sort.keys = (DWORD *)((char *)ptr + ptr[0]);
661 sort.casemap = (USHORT *)((char *)ptr + ptr[1]);
663 ctype = (WORD *)((char *)ptr + ptr[2]);
664 sort.ctypes = ctype + 2;
665 sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2;
667 table = (DWORD *)((char *)ptr + ptr[3]);
668 sort.version = table[0];
669 sort.guid_count = table[1];
670 sort.guids = (struct sortguid *)(table + 2);
674 static const struct sortguid *find_sortguid( const GUID *guid )
676 int pos, ret, min = 0, max = sort.guid_count - 1;
678 while (min <= max)
680 pos = (min + max) / 2;
681 ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) );
682 if (!ret) return &sort.guids[pos];
683 if (ret > 0) min = pos + 1;
684 else max = pos - 1;
686 ERR( "no sort found for %s\n", debugstr_guid( guid ));
687 return NULL;
691 static const struct sortguid *get_language_sort( const WCHAR *locale )
693 WCHAR *p, *end, buffer[LOCALE_NAME_MAX_LENGTH], guidstr[39];
694 const struct sortguid *ret;
695 UNICODE_STRING str;
696 GUID guid;
697 HKEY key = 0;
698 DWORD size, type;
700 if (locale == LOCALE_NAME_USER_DEFAULT)
702 if (current_locale_sort) return current_locale_sort;
703 GetUserDefaultLocaleName( buffer, ARRAY_SIZE( buffer ));
705 else lstrcpynW( buffer, locale, LOCALE_NAME_MAX_LENGTH );
707 if (buffer[0] && !RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key ))
709 for (;;)
711 size = sizeof(guidstr);
712 if (!RegQueryValueExW( key, buffer, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ)
714 RtlInitUnicodeString( &str, guidstr );
715 if (!RtlGUIDFromString( &str, &guid ))
717 ret = find_sortguid( &guid );
718 goto done;
720 break;
722 for (p = end = buffer; *p; p++) if (*p == '-' || *p == '_') end = p;
723 if (end == buffer) break;
724 *end = 0;
727 ret = find_sortguid( &default_sort_guid );
728 done:
729 RegCloseKey( key );
730 return ret;
734 static const NLS_LOCALE_DATA *get_locale_data( UINT idx )
736 ULONG offset = locale_table->locales_offset + idx * locale_table->locale_size;
737 return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
741 static int compare_locale_names( const WCHAR *n1, const WCHAR *n2 )
743 for (;;)
745 WCHAR ch1 = *n1++;
746 WCHAR ch2 = *n2++;
747 if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A';
748 else if (ch1 == '_') ch1 = '-';
749 if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A';
750 else if (ch2 == '_') ch2 = '-';
751 if (!ch1 || ch1 != ch2) return ch1 - ch2;
756 static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry( const WCHAR *name )
758 int min = 0, max = locale_table->nb_lcnames - 1;
760 while (min <= max)
762 int res, pos = (min + max) / 2;
763 const WCHAR *str = locale_strings + lcnames_index[pos].name;
764 res = compare_locale_names( name, str + 1 );
765 if (res < 0) max = pos - 1;
766 else if (res > 0) min = pos + 1;
767 else return &lcnames_index[pos];
769 return NULL;
773 static const NLS_LOCALE_LCID_INDEX *find_lcid_entry( LCID lcid )
775 int min = 0, max = locale_table->nb_lcids - 1;
777 while (min <= max)
779 int pos = (min + max) / 2;
780 if (lcid < lcids_index[pos].id) max = pos - 1;
781 else if (lcid > lcids_index[pos].id) min = pos + 1;
782 else return &lcids_index[pos];
784 return NULL;
788 static const NLS_LOCALE_DATA *get_locale_by_name( const WCHAR *name, LCID *lcid )
790 const NLS_LOCALE_LCNAME_INDEX *entry;
792 if (name == LOCALE_NAME_USER_DEFAULT)
794 *lcid = user_lcid;
795 return user_locale;
797 if (!(entry = find_lcname_entry( name ))) return NULL;
798 *lcid = entry->id;
799 return get_locale_data( entry->idx );
803 static const NLS_LOCALE_DATA *get_locale_by_id( LCID *lcid, DWORD flags )
805 const NLS_LOCALE_LCID_INDEX *entry;
806 const NLS_LOCALE_DATA *locale;
808 switch (*lcid)
810 case LOCALE_SYSTEM_DEFAULT:
811 *lcid = system_lcid;
812 return system_locale;
813 case LOCALE_NEUTRAL:
814 case LOCALE_USER_DEFAULT:
815 case LOCALE_CUSTOM_DEFAULT:
816 *lcid = user_lcid;
817 return user_locale;
818 default:
819 if (!(entry = find_lcid_entry( *lcid ))) return NULL;
820 locale = get_locale_data( entry->idx );
821 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
822 locale = get_locale_by_name( locale_strings + locale->ssortlocale + 1, lcid );
823 return locale;
828 static int locale_return_data( const WCHAR *data, int datalen, LCTYPE type, WCHAR *buffer, int len )
830 if (type & LOCALE_RETURN_NUMBER)
832 SetLastError( ERROR_INVALID_FLAGS );
833 return 0;
836 if (!len) return datalen;
837 if (datalen > len)
839 SetLastError( ERROR_INSUFFICIENT_BUFFER );
840 return 0;
842 memcpy( buffer, data, datalen * sizeof(WCHAR) );
843 return datalen;
847 static int locale_return_string( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
849 return locale_return_data( locale_strings + pos + 1, locale_strings[pos] + 1, type, buffer, len );
853 static int locale_return_number( UINT val, LCTYPE type, WCHAR *buffer, int len )
855 int ret;
856 WCHAR tmp[80];
858 if (!(type & LOCALE_RETURN_NUMBER))
860 switch (LOWORD(type))
862 case LOCALE_ILANGUAGE:
863 case LOCALE_IDEFAULTLANGUAGE:
864 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%04x", val ) + 1;
865 break;
866 case LOCALE_IDEFAULTEBCDICCODEPAGE:
867 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", val ) + 1;
868 break;
869 default:
870 ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val ) + 1;
871 break;
874 else ret = sizeof(UINT) / sizeof(WCHAR);
876 if (!len) return ret;
877 if (ret > len)
879 SetLastError( ERROR_INSUFFICIENT_BUFFER );
880 return 0;
883 if (type & LOCALE_RETURN_NUMBER) memcpy( buffer, &val, sizeof(val) );
884 else wcscpy( buffer, tmp );
886 return ret;
890 static int locale_return_strarray( DWORD pos, WORD idx, LCTYPE type, WCHAR *buffer, int len )
892 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
893 WORD count = locale_strings[pos];
895 return locale_return_string( idx < count ? array[idx] : 0, type, buffer, len );
899 static int locale_return_strarray_concat( DWORD pos, LCTYPE type, WCHAR *buffer, int len )
901 WORD i, count = locale_strings[pos];
902 const DWORD *array = (const DWORD *)(locale_strings + pos + 1);
903 int ret;
905 if (type & LOCALE_RETURN_NUMBER)
907 SetLastError( ERROR_INVALID_FLAGS );
908 return 0;
910 for (i = 0, ret = 1; i < count; i++) ret += locale_strings[array[i]];
912 if (!len) return ret;
913 if (ret > len)
915 SetLastError( ERROR_INSUFFICIENT_BUFFER );
916 return 0;
918 for (i = 0; i < count; i++)
920 memcpy( buffer, locale_strings + array[i] + 1, locale_strings[array[i]] * sizeof(WCHAR) );
921 buffer += locale_strings[array[i]];
923 *buffer = 0;
924 return ret;
928 /* get locale information from the locale.nls file */
929 static int get_locale_info( const NLS_LOCALE_DATA *locale, LCID lcid, LCTYPE type,
930 WCHAR *buffer, int len )
932 static const WCHAR spermille[] = { 0x2030, 0 }; /* this one seems hardcoded */
933 UINT val;
935 if (locale != user_locale) type |= LOCALE_NOUSEROVERRIDE;
937 switch (LOWORD(type))
939 case LOCALE_ILANGUAGE:
940 /* return default language for neutral locales */
941 val = locale->inotneutral ? locale->ilanguage : locale->idefaultlanguage;
942 return locale_return_number( val, type, buffer, len );
944 case LOCALE_SLOCALIZEDDISPLAYNAME:
945 /* FIXME: localization */
946 return locale_return_string( locale->sengdisplayname, type, buffer, len );
948 case LOCALE_SABBREVLANGNAME:
949 return locale_return_string( locale->sabbrevlangname, type, buffer, len );
951 case LOCALE_SNATIVELANGNAME:
952 return locale_return_string( locale->snativelangname, type, buffer, len );
954 case LOCALE_ICOUNTRY:
955 return locale_return_number( locale->icountry, type, buffer, len );
957 case LOCALE_SLOCALIZEDCOUNTRYNAME:
958 /* FIXME: localization */
959 return locale_return_string( locale->sengcountry, type, buffer, len );
961 case LOCALE_SABBREVCTRYNAME:
962 return locale_return_string( locale->sabbrevctryname, type, buffer, len );
964 case LOCALE_SNATIVECTRYNAME:
965 return locale_return_string( locale->snativectryname, type, buffer, len );
967 case LOCALE_IDEFAULTLANGUAGE:
968 return locale_return_number( locale->idefaultlanguage, type, buffer, len );
970 case LOCALE_IDEFAULTCOUNTRY:
971 return locale_return_number( locale->icountry, type, buffer, len );
973 case LOCALE_IDEFAULTCODEPAGE:
974 val = locale->idefaultcodepage == CP_UTF8 ? CP_OEMCP : locale->idefaultcodepage;
975 return locale_return_number( val, type, buffer, len );
977 case LOCALE_SLIST:
978 return -1;
980 case LOCALE_IMEASURE:
981 return locale_return_number( locale->imeasure, type, buffer, len );
983 case LOCALE_SDECIMAL:
984 return -1;
986 case LOCALE_STHOUSAND:
987 return -1;
989 case LOCALE_SGROUPING:
990 return -1;
992 case LOCALE_IDIGITS:
993 return -1;
995 case LOCALE_ILZERO:
996 return -1;
998 case LOCALE_SNATIVEDIGITS:
999 return locale_return_strarray_concat( locale->snativedigits, type, buffer, len );
1001 case LOCALE_SCURRENCY:
1002 return locale_return_string( locale->scurrency, type, buffer, len );
1004 case LOCALE_SINTLSYMBOL:
1005 return locale_return_string( locale->sintlsymbol, type, buffer, len );
1007 case LOCALE_SMONDECIMALSEP:
1008 return -1;
1010 case LOCALE_SMONTHOUSANDSEP:
1011 return -1;
1013 case LOCALE_SMONGROUPING:
1014 return -1;
1016 case LOCALE_ICURRDIGITS:
1017 case LOCALE_IINTLCURRDIGITS:
1018 return -1;
1020 case LOCALE_ICURRENCY:
1021 return -1;
1023 case LOCALE_INEGCURR:
1024 return -1;
1026 case LOCALE_SDATE:
1027 return -1;
1029 case LOCALE_STIME:
1030 return -1;
1032 case LOCALE_SSHORTDATE:
1033 return locale_return_strarray( locale->sshortdate, 0, type, buffer, len );
1035 case LOCALE_SLONGDATE:
1036 return locale_return_strarray( locale->slongdate, 0, type, buffer, len );
1038 case LOCALE_IDATE:
1039 return -1;
1041 case LOCALE_ILDATE:
1042 return -1;
1044 case LOCALE_ITIME:
1045 return -1;
1047 case LOCALE_ICENTURY:
1048 return -1;
1050 case LOCALE_ITLZERO:
1051 return -1;
1053 case LOCALE_IDAYLZERO:
1054 return -1;
1056 case LOCALE_IMONLZERO:
1057 return -1;
1059 case LOCALE_S1159:
1060 return locale_return_string( locale->s1159, type, buffer, len );
1062 case LOCALE_S2359:
1063 return locale_return_string( locale->s2359, type, buffer, len );
1065 case LOCALE_SDAYNAME1:
1066 case LOCALE_SDAYNAME2:
1067 case LOCALE_SDAYNAME3:
1068 case LOCALE_SDAYNAME4:
1069 case LOCALE_SDAYNAME5:
1070 case LOCALE_SDAYNAME6:
1071 case LOCALE_SDAYNAME7:
1072 return locale_return_strarray( locale->sdayname,
1073 LOWORD(type - LOCALE_SDAYNAME1 + 1) % 7, type, buffer, len );
1075 case LOCALE_SABBREVDAYNAME1:
1076 case LOCALE_SABBREVDAYNAME2:
1077 case LOCALE_SABBREVDAYNAME3:
1078 case LOCALE_SABBREVDAYNAME4:
1079 case LOCALE_SABBREVDAYNAME5:
1080 case LOCALE_SABBREVDAYNAME6:
1081 case LOCALE_SABBREVDAYNAME7:
1082 return locale_return_strarray( locale->sabbrevdayname,
1083 LOWORD(type - LOCALE_SABBREVDAYNAME1 + 1) % 7, type, buffer, len );
1085 case LOCALE_SMONTHNAME1:
1086 case LOCALE_SMONTHNAME2:
1087 case LOCALE_SMONTHNAME3:
1088 case LOCALE_SMONTHNAME4:
1089 case LOCALE_SMONTHNAME5:
1090 case LOCALE_SMONTHNAME6:
1091 case LOCALE_SMONTHNAME7:
1092 case LOCALE_SMONTHNAME8:
1093 case LOCALE_SMONTHNAME9:
1094 case LOCALE_SMONTHNAME10:
1095 case LOCALE_SMONTHNAME11:
1096 case LOCALE_SMONTHNAME12:
1097 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1098 locale->sgenitivemonth : locale->smonthname,
1099 type - LOCALE_SMONTHNAME1, type, buffer, len );
1101 case LOCALE_SABBREVMONTHNAME1:
1102 case LOCALE_SABBREVMONTHNAME2:
1103 case LOCALE_SABBREVMONTHNAME3:
1104 case LOCALE_SABBREVMONTHNAME4:
1105 case LOCALE_SABBREVMONTHNAME5:
1106 case LOCALE_SABBREVMONTHNAME6:
1107 case LOCALE_SABBREVMONTHNAME7:
1108 case LOCALE_SABBREVMONTHNAME8:
1109 case LOCALE_SABBREVMONTHNAME9:
1110 case LOCALE_SABBREVMONTHNAME10:
1111 case LOCALE_SABBREVMONTHNAME11:
1112 case LOCALE_SABBREVMONTHNAME12:
1113 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1114 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1115 type - LOCALE_SABBREVMONTHNAME1, type, buffer, len );
1117 case LOCALE_SPOSITIVESIGN:
1118 return -1;
1120 case LOCALE_SNEGATIVESIGN:
1121 return -1;
1123 case LOCALE_IPOSSIGNPOSN:
1124 return -1;
1126 case LOCALE_INEGSIGNPOSN:
1127 return -1;
1129 case LOCALE_IPOSSYMPRECEDES:
1130 return -1;
1132 case LOCALE_IPOSSEPBYSPACE:
1133 return -1;
1135 case LOCALE_INEGSYMPRECEDES:
1136 return -1;
1138 case LOCALE_INEGSEPBYSPACE:
1139 return -1;
1141 case LOCALE_FONTSIGNATURE:
1142 return locale_return_data( locale_strings + locale->fontsignature + 1,
1143 locale_strings[locale->fontsignature], type, buffer, len );
1145 case LOCALE_SISO639LANGNAME:
1146 return locale_return_string( locale->siso639langname, type, buffer, len );
1148 case LOCALE_SISO3166CTRYNAME:
1149 return locale_return_string( locale->siso3166ctryname, type, buffer, len );
1151 case LOCALE_IGEOID:
1152 return locale_return_number( locale->igeoid, type, buffer, len );
1154 case LOCALE_SNAME:
1155 if (SORTIDFROMLCID(lcid)) /* custom sort locale */
1157 const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 );
1158 if (entry) return locale_return_string( entry->name, type, buffer, len );
1160 return locale_return_string( locale->sname, type, buffer, len );
1162 case LOCALE_SDURATION:
1163 return locale_return_strarray( locale->sduration, 0, type, buffer, len );
1165 case LOCALE_SKEYBOARDSTOINSTALL:
1166 return -1;
1168 case LOCALE_SSHORTESTDAYNAME1:
1169 case LOCALE_SSHORTESTDAYNAME2:
1170 case LOCALE_SSHORTESTDAYNAME3:
1171 case LOCALE_SSHORTESTDAYNAME4:
1172 case LOCALE_SSHORTESTDAYNAME5:
1173 case LOCALE_SSHORTESTDAYNAME6:
1174 case LOCALE_SSHORTESTDAYNAME7:
1175 return locale_return_strarray( locale->sshortestdayname,
1176 LOWORD(type - LOCALE_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len );
1178 case LOCALE_SISO639LANGNAME2:
1179 return locale_return_string( locale->siso639langname2, type, buffer, len );
1181 case LOCALE_SISO3166CTRYNAME2:
1182 return locale_return_string( locale->siso3166ctryname2, type, buffer, len );
1184 case LOCALE_SNAN:
1185 return locale_return_string( locale->snan, type, buffer, len );
1187 case LOCALE_SPOSINFINITY:
1188 return locale_return_string( locale->sposinfinity, type, buffer, len );
1190 case LOCALE_SNEGINFINITY:
1191 return locale_return_string( locale->sneginfinity, type, buffer, len );
1193 case LOCALE_SSCRIPTS:
1194 return -1;
1196 case LOCALE_SPARENT:
1197 return locale_return_string( locale->sparent, type, buffer, len );
1199 case LOCALE_SCONSOLEFALLBACKNAME:
1200 return -1;
1202 case LOCALE_SLOCALIZEDLANGUAGENAME:
1203 /* FIXME: localization */
1204 return locale_return_string( locale->senglanguage, type, buffer, len );
1206 case LOCALE_IREADINGLAYOUT:
1207 return locale_return_number( locale->ireadinglayout, type, buffer, len );
1209 case LOCALE_INEUTRAL:
1210 return -1;
1212 case LOCALE_SENGLISHDISPLAYNAME:
1213 return locale_return_string( locale->sengdisplayname, type, buffer, len );
1215 case LOCALE_SNATIVEDISPLAYNAME:
1216 return locale_return_string( locale->snativedisplayname, type, buffer, len );
1218 case LOCALE_INEGATIVEPERCENT:
1219 return locale_return_number( locale->inegativepercent, type, buffer, len );
1221 case LOCALE_IPOSITIVEPERCENT:
1222 return locale_return_number( locale->ipositivepercent, type, buffer, len );
1224 case LOCALE_SPERCENT:
1225 return locale_return_string( locale->spercent, type, buffer, len );
1227 case LOCALE_SPERMILLE:
1228 return locale_return_data( spermille, ARRAY_SIZE(spermille), type, buffer, len );
1230 case LOCALE_SMONTHDAY:
1231 return locale_return_strarray( locale->smonthday, 0, type, buffer, len );
1233 case LOCALE_SSHORTTIME:
1234 return locale_return_strarray( locale->sshorttime, 0, type, buffer, len );
1236 case LOCALE_SOPENTYPELANGUAGETAG:
1237 return locale_return_string( locale->sopentypelanguagetag, type, buffer, len );
1239 case LOCALE_SSORTLOCALE:
1240 return -1;
1242 case LOCALE_SRELATIVELONGDATE:
1243 return locale_return_string( locale->srelativelongdate, type, buffer, len );
1245 case 0x007d: /* undocumented */
1246 return -1;
1248 case LOCALE_SSHORTESTAM:
1249 return locale_return_string( locale->sshortestam, type, buffer, len );
1251 case LOCALE_SSHORTESTPM:
1252 return locale_return_string( locale->sshortestpm, type, buffer, len );
1254 case LOCALE_SENGLANGUAGE:
1255 return locale_return_string( locale->senglanguage, type, buffer, len );
1257 case LOCALE_SENGCOUNTRY:
1258 return locale_return_string( locale->sengcountry, type, buffer, len );
1260 case LOCALE_STIMEFORMAT:
1261 return locale_return_strarray( locale->stimeformat, 0, type, buffer, len );
1263 case LOCALE_IDEFAULTANSICODEPAGE:
1264 val = locale->idefaultansicodepage == CP_UTF8 ? CP_ACP : locale->idefaultansicodepage;
1265 return locale_return_number( val, type, buffer, len );
1267 case LOCALE_ITIMEMARKPOSN:
1268 return -1;
1270 case LOCALE_SYEARMONTH:
1271 return locale_return_strarray( locale->syearmonth, 0, type, buffer, len );
1273 case LOCALE_SENGCURRNAME:
1274 return locale_return_string( locale->sengcurrname, type, buffer, len );
1276 case LOCALE_SNATIVECURRNAME:
1277 return locale_return_string( locale->snativecurrname, type, buffer, len );
1279 case LOCALE_ICALENDARTYPE:
1280 return locale_return_number( locale_strings[locale->scalendartype + 1], type, buffer, len );
1282 case LOCALE_IPAPERSIZE:
1283 return locale_return_number( locale->ipapersize, type, buffer, len );
1285 case LOCALE_IOPTIONALCALENDAR:
1286 return locale_return_number( locale_strings[locale->scalendartype + 2], type, buffer, len );
1288 case LOCALE_IFIRSTDAYOFWEEK:
1289 return locale_return_number( (locale->ifirstdayofweek + 6) % 7, type, buffer, len );
1291 case LOCALE_IFIRSTWEEKOFYEAR:
1292 return locale_return_number( locale->ifirstweekofyear, type, buffer, len );
1294 case LOCALE_SMONTHNAME13:
1295 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ?
1296 locale->sgenitivemonth : locale->smonthname,
1297 12, type, buffer, len );
1299 case LOCALE_SABBREVMONTHNAME13:
1300 return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ?
1301 locale->sabbrevgenitivemonth : locale->sabbrevmonthname,
1302 12, type, buffer, len );
1304 case LOCALE_INEGNUMBER:
1305 return -1;
1307 case LOCALE_IDEFAULTMACCODEPAGE:
1308 val = locale->idefaultmaccodepage == CP_UTF8 ? CP_MACCP : locale->idefaultmaccodepage;
1309 return locale_return_number( val, type, buffer, len );
1311 case LOCALE_IDEFAULTEBCDICCODEPAGE:
1312 return locale_return_number( locale->idefaultebcdiccodepage, type, buffer, len );
1314 case LOCALE_SSORTNAME:
1315 return -1;
1317 case LOCALE_IDIGITSUBSTITUTION:
1318 return locale_return_number( locale->idigitsubstitution, type, buffer, len );
1320 SetLastError( ERROR_INVALID_FLAGS );
1321 return 0;
1325 /***********************************************************************
1326 * init_locale
1328 void init_locale(void)
1330 UINT ansi_cp = 0, oem_cp = 0;
1331 USHORT *ansi_ptr, *oem_ptr;
1332 void *sort_ptr;
1333 WCHAR bufferW[LOCALE_NAME_MAX_LENGTH];
1334 DYNAMIC_TIME_ZONE_INFORMATION timezone;
1335 GEOID geoid = GEOID_NOT_AVAILABLE;
1336 DWORD count, dispos, i;
1337 SIZE_T size;
1338 HKEY hkey;
1340 load_locale_nls();
1341 NtQueryDefaultLocale( FALSE, &system_lcid );
1342 NtQueryDefaultLocale( FALSE, &user_lcid );
1343 system_locale = get_locale_by_id( &system_lcid, 0 );
1344 user_locale = get_locale_by_id( &user_lcid, 0 );
1346 if (GetEnvironmentVariableW( L"WINEUNIXCP", bufferW, ARRAY_SIZE(bufferW) ))
1347 unix_cp = wcstoul( bufferW, NULL, 10 );
1349 kernel32_handle = GetModuleHandleW( L"kernel32.dll" );
1351 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
1352 (WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) );
1353 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
1354 (WCHAR *)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
1355 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
1356 (WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
1358 NtGetNlsSectionPtr( 9, 0, NULL, &sort_ptr, &size );
1359 NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size );
1360 init_sortkeys( sort_ptr );
1362 if (!ansi_cp || NtGetNlsSectionPtr( 11, ansi_cp, NULL, (void **)&ansi_ptr, &size ))
1363 NtGetNlsSectionPtr( 11, 1252, NULL, (void **)&ansi_ptr, &size );
1364 if (!oem_cp || NtGetNlsSectionPtr( 11, oem_cp, 0, (void **)&oem_ptr, &size ))
1365 NtGetNlsSectionPtr( 11, 437, NULL, (void **)&oem_ptr, &size );
1366 NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr;
1367 NtCurrentTeb()->Peb->OemCodePageData = oem_ptr;
1368 NtCurrentTeb()->Peb->UnicodeCaseTableData = sort.casemap;
1369 RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info );
1370 RtlResetRtlTranslations( &nls_info );
1372 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
1373 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
1374 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1375 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tz_key, NULL );
1376 RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International",
1377 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL );
1379 current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT );
1381 if (GetDynamicTimeZoneInformation( &timezone ) != TIME_ZONE_ID_INVALID &&
1382 !RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\TimeZoneInformation",
1383 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1385 RegSetValueExW( hkey, L"StandardName", 0, REG_SZ, (BYTE *)timezone.StandardName,
1386 (lstrlenW(timezone.StandardName) + 1) * sizeof(WCHAR) );
1387 RegSetValueExW( hkey, L"TimeZoneKeyName", 0, REG_SZ, (BYTE *)timezone.TimeZoneKeyName,
1388 (lstrlenW(timezone.TimeZoneKeyName) + 1) * sizeof(WCHAR) );
1389 RegCloseKey( hkey );
1392 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, &dispos ))
1394 if (dispos == REG_CREATED_NEW_KEY)
1396 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IGEOID | LOCALE_RETURN_NUMBER,
1397 (WCHAR *)&geoid, sizeof(geoid) / sizeof(WCHAR) );
1398 SetUserGeoID( geoid );
1400 RegCloseKey( hkey );
1403 /* Update registry contents if the user locale has changed.
1404 * This simulates the action of the Windows control panel. */
1406 count = sizeof(bufferW);
1407 if (!RegQueryValueExW( intl_key, L"Locale", NULL, NULL, (BYTE *)bufferW, &count ))
1409 if (wcstoul( bufferW, NULL, 16 ) == user_lcid) return; /* already set correctly */
1410 TRACE( "updating registry, locale changed %s -> %08lx\n", debugstr_w(bufferW), user_lcid );
1412 else TRACE( "updating registry, locale changed none -> %08lx\n", user_lcid );
1413 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%08x", user_lcid );
1414 RegSetValueExW( intl_key, L"Locale", 0, REG_SZ,
1415 (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
1417 for (i = 0; i < ARRAY_SIZE(registry_values); i++)
1419 GetLocaleInfoW( LOCALE_USER_DEFAULT, registry_values[i].lctype | LOCALE_NOUSEROVERRIDE,
1420 bufferW, ARRAY_SIZE( bufferW ));
1421 RegSetValueExW( intl_key, registry_values[i].name, 0, REG_SZ,
1422 (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
1425 if (geoid == GEOID_NOT_AVAILABLE)
1427 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IGEOID | LOCALE_RETURN_NUMBER,
1428 (WCHAR *)&geoid, sizeof(geoid) / sizeof(WCHAR) );
1429 SetUserGeoID( geoid );
1432 if (!RegCreateKeyExW( nls_key, L"Codepage",
1433 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1435 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", ansi_cp );
1436 RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1437 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", oem_cp );
1438 RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1439 count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", mac_cp );
1440 RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
1441 RegCloseKey( hkey );
1446 static inline USHORT get_table_entry( const USHORT *table, WCHAR ch )
1448 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
1452 static inline WCHAR casemap( const USHORT *table, WCHAR ch )
1454 return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
1458 static inline WORD get_char_type( DWORD type, WCHAR ch )
1460 const BYTE *ptr = sort.ctype_idx + ((const WORD *)sort.ctype_idx)[ch >> 8];
1461 ptr = sort.ctype_idx + ((const WORD *)ptr)[(ch >> 4) & 0x0f] + (ch & 0x0f);
1462 return sort.ctypes[*ptr * 3 + type / 2];
1466 static BYTE rol( BYTE val, BYTE count )
1468 return (val << count) | (val >> (8 - count));
1472 static BYTE get_char_props( const struct norm_table *info, unsigned int ch )
1474 const BYTE *level1 = (const BYTE *)((const USHORT *)info + info->props_level1);
1475 const BYTE *level2 = (const BYTE *)((const USHORT *)info + info->props_level2);
1476 BYTE off = level1[ch / 128];
1478 if (!off || off >= 0xfb) return rol( off, 5 );
1479 return level2[(off - 1) * 128 + ch % 128];
1483 static const WCHAR *get_decomposition( WCHAR ch, unsigned int *ret_len )
1485 const struct pair { WCHAR src; USHORT dst; } *pairs;
1486 const USHORT *hash_table = (const USHORT *)norm_info + norm_info->decomp_hash;
1487 const WCHAR *ret;
1488 unsigned int i, pos, end, len, hash;
1490 *ret_len = 1;
1491 hash = ch % norm_info->decomp_size;
1492 pos = hash_table[hash];
1493 if (pos >> 13)
1495 if (get_char_props( norm_info, ch ) != 0xbf) return NULL;
1496 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pos & 0x1fff);
1497 len = pos >> 13;
1499 else
1501 pairs = (const struct pair *)((const USHORT *)norm_info + norm_info->decomp_map);
1503 /* find the end of the hash bucket */
1504 for (i = hash + 1; i < norm_info->decomp_size; i++) if (!(hash_table[i] >> 13)) break;
1505 if (i < norm_info->decomp_size) end = hash_table[i];
1506 else for (end = pos; pairs[end].src; end++) ;
1508 for ( ; pos < end; pos++)
1510 if (pairs[pos].src != (WCHAR)ch) continue;
1511 ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pairs[pos].dst & 0x1fff);
1512 len = pairs[pos].dst >> 13;
1513 break;
1515 if (pos >= end) return NULL;
1518 if (len == 7) while (ret[len]) len++;
1519 if (!ret[0]) len = 0; /* ignored char */
1520 *ret_len = len;
1521 return ret;
1525 static WCHAR compose_chars( WCHAR ch1, WCHAR ch2 )
1527 const USHORT *table = (const USHORT *)norm_info + norm_info->comp_hash;
1528 const WCHAR *chars = (const USHORT *)norm_info + norm_info->comp_seq;
1529 unsigned int hash, start, end, i;
1530 WCHAR ch[3];
1532 hash = (ch1 + 95 * ch2) % norm_info->comp_size;
1533 start = table[hash];
1534 end = table[hash + 1];
1535 while (start < end)
1537 for (i = 0; i < 3; i++, start++)
1539 ch[i] = chars[start];
1540 if (IS_HIGH_SURROGATE( ch[i] )) start++;
1542 if (ch[0] == ch1 && ch[1] == ch2) return ch[2];
1544 return 0;
1548 static UINT get_lcid_codepage( LCID lcid, ULONG flags )
1550 UINT ret = GetACP();
1552 if (!(flags & LOCALE_USE_CP_ACP) && lcid != GetSystemDefaultLCID())
1553 GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
1554 (WCHAR *)&ret, sizeof(ret)/sizeof(WCHAR) );
1555 return ret;
1559 static int get_value_base_by_lctype( LCTYPE lctype )
1561 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1565 static const struct registry_value *get_locale_registry_value( DWORD lctype )
1567 unsigned int i;
1569 for (i = 0; i < ARRAY_SIZE( registry_values ); i++)
1570 if (registry_values[i].lctype == lctype) return &registry_values[i];
1571 return NULL;
1575 static INT get_registry_locale_info( const struct registry_value *registry_value, LPWSTR buffer, INT len )
1577 DWORD size, index = registry_value - registry_values;
1578 INT ret;
1580 RtlEnterCriticalSection( &locale_section );
1582 if (!registry_cache[index])
1584 size = len * sizeof(WCHAR);
1585 ret = RegQueryValueExW( intl_key, registry_value->name, NULL, NULL, (BYTE *)buffer, &size );
1586 if (!ret)
1588 if (buffer && (registry_cache[index] = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) )))
1590 memcpy( registry_cache[index], buffer, size );
1591 registry_cache[index][size / sizeof(WCHAR)] = 0;
1593 RtlLeaveCriticalSection( &locale_section );
1594 return size / sizeof(WCHAR);
1596 else
1598 RtlLeaveCriticalSection( &locale_section );
1599 if (ret == ERROR_FILE_NOT_FOUND) return -1;
1600 if (ret == ERROR_MORE_DATA) SetLastError( ERROR_INSUFFICIENT_BUFFER );
1601 else SetLastError( ret );
1602 return 0;
1606 ret = lstrlenW( registry_cache[index] ) + 1;
1607 if (buffer)
1609 if (ret > len)
1611 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1612 ret = 0;
1614 else lstrcpyW( buffer, registry_cache[index] );
1616 RtlLeaveCriticalSection( &locale_section );
1617 return ret;
1621 static const CPTABLEINFO *get_codepage_table( UINT codepage )
1623 unsigned int i;
1624 USHORT *ptr;
1625 SIZE_T size;
1627 switch (codepage)
1629 case CP_ACP:
1630 return &nls_info.AnsiTableInfo;
1631 case CP_OEMCP:
1632 return &nls_info.OemTableInfo;
1633 case CP_MACCP:
1634 codepage = mac_cp;
1635 break;
1636 case CP_THREAD_ACP:
1637 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return &nls_info.AnsiTableInfo;
1638 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 );
1639 if (!codepage) return &nls_info.AnsiTableInfo;
1640 break;
1641 default:
1642 if (codepage == nls_info.AnsiTableInfo.CodePage) return &nls_info.AnsiTableInfo;
1643 if (codepage == nls_info.OemTableInfo.CodePage) return &nls_info.OemTableInfo;
1644 break;
1647 RtlEnterCriticalSection( &locale_section );
1649 for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done;
1651 if (i == ARRAY_SIZE( codepages ))
1653 RtlLeaveCriticalSection( &locale_section );
1654 ERR( "too many codepages\n" );
1655 return NULL;
1657 if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size ))
1659 RtlLeaveCriticalSection( &locale_section );
1660 SetLastError( ERROR_INVALID_PARAMETER );
1661 return NULL;
1663 RtlInitCodePageTable( ptr, &codepages[i] );
1664 nb_codepages++;
1665 done:
1666 RtlLeaveCriticalSection( &locale_section );
1667 return &codepages[i];
1671 static const WCHAR *get_ligature( WCHAR wc )
1673 int low = 0, high = ARRAY_SIZE( ligatures ) -1;
1674 while (low <= high)
1676 int pos = (low + high) / 2;
1677 if (ligatures[pos][0] < wc) low = pos + 1;
1678 else if (ligatures[pos][0] > wc) high = pos - 1;
1679 else return ligatures[pos] + 1;
1681 return NULL;
1685 static NTSTATUS expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1687 int i, len, pos = 0;
1688 NTSTATUS ret = STATUS_SUCCESS;
1689 const WCHAR *expand;
1691 for (i = 0; i < srclen; i++)
1693 if (!(expand = get_ligature( src[i] )))
1695 expand = src + i;
1696 len = 1;
1698 else len = lstrlenW( expand );
1700 if (*dstlen && ret == STATUS_SUCCESS)
1702 if (pos + len <= *dstlen) memcpy( dst + pos, expand, len * sizeof(WCHAR) );
1703 else ret = STATUS_BUFFER_TOO_SMALL;
1705 pos += len;
1707 *dstlen = pos;
1708 return ret;
1712 static NTSTATUS fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1714 extern const WCHAR wine_digitmap[] DECLSPEC_HIDDEN;
1715 int i, len = *dstlen;
1717 *dstlen = srclen;
1718 if (!len) return STATUS_SUCCESS;
1719 if (srclen > len) return STATUS_BUFFER_TOO_SMALL;
1720 for (i = 0; i < srclen; i++)
1722 WCHAR digit = get_table_entry( wine_digitmap, src[i] );
1723 dst[i] = digit ? digit : src[i];
1725 return STATUS_SUCCESS;
1729 static NTSTATUS fold_string( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int *dstlen )
1731 NTSTATUS ret;
1732 WCHAR *tmp;
1734 switch (flags)
1736 case MAP_PRECOMPOSED:
1737 return RtlNormalizeString( NormalizationC, src, srclen, dst, dstlen );
1738 case MAP_FOLDCZONE:
1739 case MAP_PRECOMPOSED | MAP_FOLDCZONE:
1740 return RtlNormalizeString( NormalizationKC, src, srclen, dst, dstlen );
1741 case MAP_COMPOSITE:
1742 return RtlNormalizeString( NormalizationD, src, srclen, dst, dstlen );
1743 case MAP_COMPOSITE | MAP_FOLDCZONE:
1744 return RtlNormalizeString( NormalizationKD, src, srclen, dst, dstlen );
1745 case MAP_FOLDDIGITS:
1746 return fold_digits( src, srclen, dst, dstlen );
1747 case MAP_EXPAND_LIGATURES:
1748 case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
1749 return expand_ligatures( src, srclen, dst, dstlen );
1750 case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
1751 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1752 return STATUS_NO_MEMORY;
1753 fold_digits( src, srclen, tmp, &srclen );
1754 ret = RtlNormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
1755 break;
1756 case MAP_FOLDDIGITS | MAP_FOLDCZONE:
1757 case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
1758 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1759 return STATUS_NO_MEMORY;
1760 fold_digits( src, srclen, tmp, &srclen );
1761 ret = RtlNormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
1762 break;
1763 case MAP_FOLDDIGITS | MAP_COMPOSITE:
1764 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1765 return STATUS_NO_MEMORY;
1766 fold_digits( src, srclen, tmp, &srclen );
1767 ret = RtlNormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
1768 break;
1769 case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
1770 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1771 return STATUS_NO_MEMORY;
1772 fold_digits( src, srclen, tmp, &srclen );
1773 ret = RtlNormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
1774 break;
1775 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
1776 case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
1777 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) )))
1778 return STATUS_NO_MEMORY;
1779 fold_digits( src, srclen, tmp, &srclen );
1780 ret = expand_ligatures( tmp, srclen, dst, dstlen );
1781 break;
1782 default:
1783 return STATUS_INVALID_PARAMETER_1;
1785 RtlFreeHeap( GetProcessHeap(), 0, tmp );
1786 return ret;
1790 static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1792 int len, i;
1794 if (flags)
1796 SetLastError( ERROR_INVALID_FLAGS );
1797 return 0;
1799 if (!dstlen) return srclen;
1800 len = min( srclen, dstlen );
1801 for (i = 0; i < len; i++)
1803 unsigned char c = src[i];
1804 dst[i] = (c < 0x20) ? c : c + 0xf000;
1806 if (len < srclen)
1808 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1809 return 0;
1811 return len;
1815 static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1817 static const signed char base64_decoding_table[] =
1819 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1820 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1821 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1822 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1823 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1824 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1825 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1826 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1829 const char *source_end = src + srclen;
1830 int offset = 0, pos = 0;
1831 DWORD byte_pair = 0;
1833 if (flags)
1835 SetLastError( ERROR_INVALID_FLAGS );
1836 return 0;
1838 #define OUTPUT(ch) \
1839 do { \
1840 if (dstlen > 0) \
1842 if (pos >= dstlen) goto overflow; \
1843 dst[pos] = (ch); \
1845 pos++; \
1846 } while(0)
1848 while (src < source_end)
1850 if (*src == '+')
1852 src++;
1853 if (src >= source_end) break;
1854 if (*src == '-')
1856 /* just a plus sign escaped as +- */
1857 OUTPUT( '+' );
1858 src++;
1859 continue;
1864 signed char sextet = *src;
1865 if (sextet == '-')
1867 /* skip over the dash and end base64 decoding
1868 * the current, unfinished byte pair is discarded */
1869 src++;
1870 offset = 0;
1871 break;
1873 if (sextet < 0)
1875 /* the next character of src is < 0 and therefore not part of a base64 sequence
1876 * the current, unfinished byte pair is NOT discarded in this case
1877 * this is probably a bug in Windows */
1878 break;
1880 sextet = base64_decoding_table[sextet];
1881 if (sextet == -1)
1883 /* -1 means that the next character of src is not part of a base64 sequence
1884 * in other words, all sextets in this base64 sequence have been processed
1885 * the current, unfinished byte pair is discarded */
1886 offset = 0;
1887 break;
1890 byte_pair = (byte_pair << 6) | sextet;
1891 offset += 6;
1892 if (offset >= 16)
1894 /* this byte pair is done */
1895 OUTPUT( byte_pair >> (offset - 16) );
1896 offset -= 16;
1898 src++;
1900 while (src < source_end);
1902 else
1904 OUTPUT( (unsigned char)*src );
1905 src++;
1908 return pos;
1910 overflow:
1911 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1912 return 0;
1913 #undef OUTPUT
1917 static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
1919 DWORD reslen;
1920 NTSTATUS status;
1922 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
1924 SetLastError( ERROR_INVALID_FLAGS );
1925 return 0;
1927 if (!dstlen) dst = NULL;
1928 status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen );
1929 if (status == STATUS_SOME_NOT_MAPPED)
1931 if (flags & MB_ERR_INVALID_CHARS)
1933 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
1934 return 0;
1937 else if (!set_ntstatus( status )) reslen = 0;
1939 return reslen / sizeof(WCHAR);
1943 static inline int is_private_use_area_char( WCHAR code )
1945 return (code >= 0xe000 && code <= 0xf8ff);
1949 static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen )
1951 if (info->DBCSOffsets)
1953 for ( ; srclen; src++, srclen-- )
1955 USHORT off = info->DBCSOffsets[*src];
1956 if (off)
1958 if (srclen == 1) break; /* partial char, error */
1959 if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar &&
1960 ((src[0] << 8) | src[1]) != info->TransUniDefaultChar) break;
1961 src++;
1962 srclen--;
1963 continue;
1965 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
1966 break;
1967 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
1970 else
1972 for ( ; srclen; src++, srclen-- )
1974 if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar)
1975 break;
1976 if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
1979 return !!srclen;
1984 static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen,
1985 WCHAR *dst, int dstlen )
1987 WCHAR ch;
1988 USHORT off;
1989 int len;
1990 const WCHAR *decomp;
1991 unsigned int decomp_len;
1993 if (info->DBCSOffsets)
1995 if (!dstlen) /* compute length */
1997 for (len = 0; srclen; srclen--, src++, len += decomp_len)
1999 if ((off = info->DBCSOffsets[*src]))
2001 if (srclen > 1 && src[1])
2003 src++;
2004 srclen--;
2005 ch = info->DBCSOffsets[off + *src];
2007 else ch = info->UniDefaultChar;
2009 else ch = info->MultiByteTable[*src];
2010 get_decomposition( ch, &decomp_len );
2012 return len;
2015 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2017 if ((off = info->DBCSOffsets[*src]))
2019 if (srclen > 1 && src[1])
2021 src++;
2022 srclen--;
2023 ch = info->DBCSOffsets[off + *src];
2025 else ch = info->UniDefaultChar;
2027 else ch = info->MultiByteTable[*src];
2029 if ((decomp = get_decomposition( ch, &decomp_len )))
2031 if (len < decomp_len) break;
2032 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2034 else *dst = ch;
2037 else
2039 if (!dstlen) /* compute length */
2041 for (len = 0; srclen; srclen--, src++, len += decomp_len)
2042 get_decomposition( info->MultiByteTable[*src], &decomp_len );
2043 return len;
2046 for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len)
2048 ch = info->MultiByteTable[*src];
2049 if ((decomp = get_decomposition( ch, &decomp_len )))
2051 if (len < decomp_len) break;
2052 memcpy( dst, decomp, decomp_len * sizeof(WCHAR) );
2054 else *dst = ch;
2058 if (srclen)
2060 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2061 return 0;
2063 return dstlen - len;
2067 static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2068 WCHAR *dst, int dstlen )
2070 const USHORT *table = info->MultiByteTable;
2071 int ret = srclen;
2073 if (!dstlen) return srclen;
2075 if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */
2077 srclen = dstlen;
2078 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2079 ret = 0;
2082 while (srclen >= 16)
2084 dst[0] = table[src[0]];
2085 dst[1] = table[src[1]];
2086 dst[2] = table[src[2]];
2087 dst[3] = table[src[3]];
2088 dst[4] = table[src[4]];
2089 dst[5] = table[src[5]];
2090 dst[6] = table[src[6]];
2091 dst[7] = table[src[7]];
2092 dst[8] = table[src[8]];
2093 dst[9] = table[src[9]];
2094 dst[10] = table[src[10]];
2095 dst[11] = table[src[11]];
2096 dst[12] = table[src[12]];
2097 dst[13] = table[src[13]];
2098 dst[14] = table[src[14]];
2099 dst[15] = table[src[15]];
2100 src += 16;
2101 dst += 16;
2102 srclen -= 16;
2105 /* now handle the remaining characters */
2106 src += srclen;
2107 dst += srclen;
2108 switch (srclen)
2110 case 15: dst[-15] = table[src[-15]];
2111 case 14: dst[-14] = table[src[-14]];
2112 case 13: dst[-13] = table[src[-13]];
2113 case 12: dst[-12] = table[src[-12]];
2114 case 11: dst[-11] = table[src[-11]];
2115 case 10: dst[-10] = table[src[-10]];
2116 case 9: dst[-9] = table[src[-9]];
2117 case 8: dst[-8] = table[src[-8]];
2118 case 7: dst[-7] = table[src[-7]];
2119 case 6: dst[-6] = table[src[-6]];
2120 case 5: dst[-5] = table[src[-5]];
2121 case 4: dst[-4] = table[src[-4]];
2122 case 3: dst[-3] = table[src[-3]];
2123 case 2: dst[-2] = table[src[-2]];
2124 case 1: dst[-1] = table[src[-1]];
2125 case 0: break;
2127 return ret;
2131 static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
2132 WCHAR *dst, int dstlen )
2134 USHORT off;
2135 int i;
2137 if (!dstlen)
2139 for (i = 0; srclen; i++, src++, srclen--)
2140 if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; }
2141 return i;
2144 for (i = dstlen; srclen && i; i--, srclen--, src++, dst++)
2146 if ((off = info->DBCSOffsets[*src]))
2148 if (srclen > 1 && src[1])
2150 src++;
2151 srclen--;
2152 *dst = info->DBCSOffsets[off + *src];
2154 else *dst = info->UniDefaultChar;
2156 else *dst = info->MultiByteTable[*src];
2158 if (srclen)
2160 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2161 return 0;
2163 return dstlen - i;
2167 static int mbstowcs_codepage( UINT codepage, DWORD flags, const char *src, int srclen,
2168 WCHAR *dst, int dstlen )
2170 CPTABLEINFO local_info;
2171 const CPTABLEINFO *info = get_codepage_table( codepage );
2172 const unsigned char *str = (const unsigned char *)src;
2174 if (!info)
2176 SetLastError( ERROR_INVALID_PARAMETER );
2177 return 0;
2179 if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
2181 SetLastError( ERROR_INVALID_FLAGS );
2182 return 0;
2185 if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256)
2187 local_info = *info;
2188 local_info.MultiByteTable += 257;
2189 info = &local_info;
2191 if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen ))
2193 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2194 return 0;
2197 if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen );
2199 if (info->DBCSOffsets)
2200 return mbstowcs_dbcs( info, str, srclen, dst, dstlen );
2201 else
2202 return mbstowcs_sbcs( info, str, srclen, dst, dstlen );
2206 static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2207 const char *defchar, BOOL *used )
2209 int len, i;
2211 if (flags)
2213 SetLastError( ERROR_INVALID_FLAGS );
2214 return 0;
2216 if (defchar || used)
2218 SetLastError( ERROR_INVALID_PARAMETER );
2219 return 0;
2221 if (!dstlen) return srclen;
2222 len = min( srclen, dstlen );
2223 for (i = 0; i < len; i++)
2225 if (src[i] < 0x20) dst[i] = src[i];
2226 else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000;
2227 else
2229 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2230 return 0;
2233 if (srclen > len)
2235 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2236 return 0;
2238 return len;
2242 static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2243 const char *defchar, BOOL *used )
2245 static const char directly_encodable[] =
2247 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
2248 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
2249 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
2250 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
2251 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
2252 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
2253 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
2254 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
2256 #define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
2258 static const char base64_encoding_table[] =
2259 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2261 const WCHAR *source_end = src + srclen;
2262 int pos = 0;
2264 if (defchar || used)
2266 SetLastError( ERROR_INVALID_PARAMETER );
2267 return 0;
2269 if (flags)
2271 SetLastError( ERROR_INVALID_FLAGS );
2272 return 0;
2275 #define OUTPUT(ch) \
2276 do { \
2277 if (dstlen > 0) \
2279 if (pos >= dstlen) goto overflow; \
2280 dst[pos] = (ch); \
2282 pos++; \
2283 } while (0)
2285 while (src < source_end)
2287 if (*src == '+')
2289 OUTPUT( '+' );
2290 OUTPUT( '-' );
2291 src++;
2293 else if (ENCODABLE(*src))
2295 OUTPUT( *src );
2296 src++;
2298 else
2300 unsigned int offset = 0, byte_pair = 0;
2302 OUTPUT( '+' );
2303 while (src < source_end && !ENCODABLE(*src))
2305 byte_pair = (byte_pair << 16) | *src;
2306 offset += 16;
2307 while (offset >= 6)
2309 offset -= 6;
2310 OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] );
2312 src++;
2314 if (offset)
2316 /* Windows won't create a padded base64 character if there's no room for the - sign
2317 * as well ; this is probably a bug in Windows */
2318 if (dstlen > 0 && pos + 1 >= dstlen) goto overflow;
2319 byte_pair <<= (6 - offset);
2320 OUTPUT( base64_encoding_table[byte_pair & 0x3f] );
2322 /* Windows always explicitly terminates the base64 sequence
2323 even though RFC 2152 (page 3, rule 2) does not require this */
2324 OUTPUT( '-' );
2327 return pos;
2329 overflow:
2330 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2331 return 0;
2332 #undef OUTPUT
2333 #undef ENCODABLE
2337 static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
2338 const char *defchar, BOOL *used )
2340 DWORD reslen;
2341 NTSTATUS status;
2343 if (defchar || used)
2345 SetLastError( ERROR_INVALID_PARAMETER );
2346 return 0;
2348 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
2349 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
2351 SetLastError( ERROR_INVALID_FLAGS );
2352 return 0;
2354 if (!dstlen) dst = NULL;
2355 status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
2356 if (status == STATUS_SOME_NOT_MAPPED)
2358 if (flags & WC_ERR_INVALID_CHARS)
2360 SetLastError( ERROR_NO_UNICODE_TRANSLATION );
2361 return 0;
2364 else if (!set_ntstatus( status )) reslen = 0;
2365 return reslen;
2369 static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2370 char *dst, unsigned int dstlen )
2372 const char *table = info->WideCharTable;
2373 int ret = srclen;
2375 if (!dstlen) return srclen;
2377 if (dstlen < srclen)
2379 /* buffer too small: fill it up to dstlen and return error */
2380 srclen = dstlen;
2381 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2382 ret = 0;
2385 while (srclen >= 16)
2387 dst[0] = table[src[0]];
2388 dst[1] = table[src[1]];
2389 dst[2] = table[src[2]];
2390 dst[3] = table[src[3]];
2391 dst[4] = table[src[4]];
2392 dst[5] = table[src[5]];
2393 dst[6] = table[src[6]];
2394 dst[7] = table[src[7]];
2395 dst[8] = table[src[8]];
2396 dst[9] = table[src[9]];
2397 dst[10] = table[src[10]];
2398 dst[11] = table[src[11]];
2399 dst[12] = table[src[12]];
2400 dst[13] = table[src[13]];
2401 dst[14] = table[src[14]];
2402 dst[15] = table[src[15]];
2403 src += 16;
2404 dst += 16;
2405 srclen -= 16;
2408 /* now handle remaining characters */
2409 src += srclen;
2410 dst += srclen;
2411 switch(srclen)
2413 case 15: dst[-15] = table[src[-15]];
2414 case 14: dst[-14] = table[src[-14]];
2415 case 13: dst[-13] = table[src[-13]];
2416 case 12: dst[-12] = table[src[-12]];
2417 case 11: dst[-11] = table[src[-11]];
2418 case 10: dst[-10] = table[src[-10]];
2419 case 9: dst[-9] = table[src[-9]];
2420 case 8: dst[-8] = table[src[-8]];
2421 case 7: dst[-7] = table[src[-7]];
2422 case 6: dst[-6] = table[src[-6]];
2423 case 5: dst[-5] = table[src[-5]];
2424 case 4: dst[-4] = table[src[-4]];
2425 case 3: dst[-3] = table[src[-3]];
2426 case 2: dst[-2] = table[src[-2]];
2427 case 1: dst[-1] = table[src[-1]];
2428 case 0: break;
2430 return ret;
2434 static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
2435 char *dst, unsigned int dstlen )
2437 const USHORT *table = info->WideCharTable;
2438 int i;
2440 if (!dstlen)
2442 for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++;
2443 return i;
2446 for (i = dstlen; srclen && i; i--, srclen--, src++)
2448 if (table[*src] & 0xff00)
2450 if (i == 1) break; /* do not output a partial char */
2451 i--;
2452 *dst++ = table[*src] >> 8;
2454 *dst++ = (char)table[*src];
2456 if (srclen)
2458 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2459 return 0;
2461 return dstlen - i;
2465 static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2467 const unsigned char *table = info->WideCharTable;
2469 if (wch >= 0x10000) return 0;
2470 if ((flags & WC_NO_BEST_FIT_CHARS) || table[wch] == info->DefaultChar)
2471 return (info->MultiByteTable[table[wch]] == wch);
2472 return 1;
2476 static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch )
2478 const unsigned short *table = info->WideCharTable;
2479 unsigned short ch;
2481 if (wch >= 0x10000) return 0;
2482 ch = table[wch];
2483 if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
2485 if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch;
2486 return info->MultiByteTable[ch] == wch;
2488 return 1;
2492 static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2493 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2495 const char *table = info->WideCharTable;
2496 const char def = defchar ? *defchar : (char)info->DefaultChar;
2497 int i;
2498 BOOL tmp;
2499 WCHAR wch;
2500 unsigned int composed;
2502 if (!used) used = &tmp; /* avoid checking on every char */
2503 *used = FALSE;
2505 if (!dstlen)
2507 for (i = 0; srclen; i++, src++, srclen--)
2509 wch = *src;
2510 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2512 /* now check if we can use the composed char */
2513 if (is_valid_sbcs_mapping( info, flags, composed ))
2515 /* we have a good mapping, use it */
2516 src++;
2517 srclen--;
2518 continue;
2520 /* no mapping for the composed char, check the other flags */
2521 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2523 *used = TRUE;
2524 src++; /* skip the non-spacing char */
2525 srclen--;
2526 continue;
2528 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2530 src++;
2531 srclen--;
2533 /* WC_SEPCHARS is the default */
2535 if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch );
2537 return i;
2540 for (i = dstlen; srclen && i; dst++, i--, src++, srclen--)
2542 wch = *src;
2543 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2545 /* now check if we can use the composed char */
2546 if (is_valid_sbcs_mapping( info, flags, composed ))
2548 /* we have a good mapping, use it */
2549 *dst = table[composed];
2550 src++;
2551 srclen--;
2552 continue;
2554 /* no mapping for the composed char, check the other flags */
2555 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2557 *dst = def;
2558 *used = TRUE;
2559 src++; /* skip the non-spacing char */
2560 srclen--;
2561 continue;
2563 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2565 src++;
2566 srclen--;
2568 /* WC_SEPCHARS is the default */
2571 *dst = table[wch];
2572 if (!is_valid_sbcs_mapping( info, flags, wch ))
2574 *dst = def;
2575 *used = TRUE;
2578 if (srclen)
2580 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2581 return 0;
2583 return dstlen - i;
2587 static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
2588 char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
2590 const USHORT *table = info->WideCharTable;
2591 WCHAR wch, defchar_value;
2592 unsigned int composed;
2593 unsigned short res;
2594 BOOL tmp;
2595 int i;
2597 if (!defchar[1]) defchar_value = (unsigned char)defchar[0];
2598 else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1];
2600 if (!used) used = &tmp; /* avoid checking on every char */
2601 *used = FALSE;
2603 if (!dstlen)
2605 if (!defchar && !used && !(flags & WC_COMPOSITECHECK))
2607 for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++;
2608 return i;
2610 for (i = 0; srclen; srclen--, src++, i++)
2612 wch = *src;
2613 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2615 /* now check if we can use the composed char */
2616 if (is_valid_dbcs_mapping( info, flags, composed ))
2618 /* we have a good mapping for the composed char, use it */
2619 res = table[composed];
2620 if (res & 0xff00) i++;
2621 src++;
2622 srclen--;
2623 continue;
2625 /* no mapping for the composed char, check the other flags */
2626 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2628 if (defchar_value & 0xff00) i++;
2629 *used = TRUE;
2630 src++; /* skip the non-spacing char */
2631 srclen--;
2632 continue;
2634 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2636 src++;
2637 srclen--;
2639 /* WC_SEPCHARS is the default */
2642 res = table[wch];
2643 if (!is_valid_dbcs_mapping( info, flags, wch ))
2645 res = defchar_value;
2646 *used = TRUE;
2648 if (res & 0xff00) i++;
2650 return i;
2654 for (i = dstlen; srclen && i; i--, srclen--, src++)
2656 wch = *src;
2657 if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] )))
2659 /* now check if we can use the composed char */
2660 if (is_valid_dbcs_mapping( info, flags, composed ))
2662 /* we have a good mapping for the composed char, use it */
2663 res = table[composed];
2664 src++;
2665 srclen--;
2666 goto output_char;
2668 /* no mapping for the composed char, check the other flags */
2669 if (flags & WC_DEFAULTCHAR) /* use the default char instead */
2671 res = defchar_value;
2672 *used = TRUE;
2673 src++; /* skip the non-spacing char */
2674 srclen--;
2675 goto output_char;
2677 if (flags & WC_DISCARDNS) /* skip the second char of the composition */
2679 src++;
2680 srclen--;
2682 /* WC_SEPCHARS is the default */
2685 res = table[wch];
2686 if (!is_valid_dbcs_mapping( info, flags, wch ))
2688 res = defchar_value;
2689 *used = TRUE;
2692 output_char:
2693 if (res & 0xff00)
2695 if (i == 1) break; /* do not output a partial char */
2696 i--;
2697 *dst++ = res >> 8;
2699 *dst++ = (char)res;
2701 if (srclen)
2703 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2704 return 0;
2706 return dstlen - i;
2710 static int wcstombs_codepage( UINT codepage, DWORD flags, const WCHAR *src, int srclen,
2711 char *dst, int dstlen, const char *defchar, BOOL *used )
2713 const CPTABLEINFO *info = get_codepage_table( codepage );
2715 if (!info)
2717 SetLastError( ERROR_INVALID_PARAMETER );
2718 return 0;
2720 if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
2721 WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
2723 SetLastError( ERROR_INVALID_FLAGS );
2724 return 0;
2726 if (flags || defchar || used)
2728 if (!defchar) defchar = (const char *)&info->DefaultChar;
2729 if (info->DBCSOffsets)
2730 return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
2731 else
2732 return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
2734 if (info->DBCSOffsets)
2735 return wcstombs_dbcs( info, src, srclen, dst, dstlen );
2736 else
2737 return wcstombs_sbcs( info, src, srclen, dst, dstlen );
2741 static int get_sortkey( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen )
2743 WCHAR dummy[4]; /* no decomposition is larger than 4 chars */
2744 int key_len[4];
2745 char *key_ptr[4];
2746 const WCHAR *src_save = src;
2747 int srclen_save = srclen;
2749 key_len[0] = key_len[1] = key_len[2] = key_len[3] = 0;
2750 for (; srclen; srclen--, src++)
2752 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
2753 dummy[0] = *src;
2754 if (decomposed_len)
2756 for (i = 0; i < decomposed_len; i++)
2758 WCHAR wch = dummy[i];
2759 unsigned int ce;
2761 if ((flags & NORM_IGNORESYMBOLS) &&
2762 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
2763 continue;
2765 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
2767 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
2768 if (ce != (unsigned int)-1)
2770 if (ce >> 16) key_len[0] += 2;
2771 if ((ce >> 8) & 0xff) key_len[1]++;
2772 if ((ce >> 4) & 0x0f) key_len[2]++;
2773 if (ce & 1)
2775 if (wch >> 8) key_len[3]++;
2776 key_len[3]++;
2779 else
2781 key_len[0] += 2;
2782 if (wch >> 8) key_len[0]++;
2783 if (wch & 0xff) key_len[0]++;
2789 if (!dstlen) /* compute length */
2790 /* 4 * '\1' + key length */
2791 return key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4;
2793 if (dstlen < key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4 + 1)
2794 return 0; /* overflow */
2796 src = src_save;
2797 srclen = srclen_save;
2799 key_ptr[0] = dst;
2800 key_ptr[1] = key_ptr[0] + key_len[0] + 1;
2801 key_ptr[2] = key_ptr[1] + key_len[1] + 1;
2802 key_ptr[3] = key_ptr[2] + key_len[2] + 1;
2804 for (; srclen; srclen--, src++)
2806 unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
2807 dummy[0] = *src;
2808 if (decomposed_len)
2810 for (i = 0; i < decomposed_len; i++)
2812 WCHAR wch = dummy[i];
2813 unsigned int ce;
2815 if ((flags & NORM_IGNORESYMBOLS) &&
2816 (get_char_type( CT_CTYPE1, wch ) & (C1_PUNCT | C1_SPACE)))
2817 continue;
2819 if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
2821 ce = collation_table[collation_table[collation_table[wch >> 8] + ((wch >> 4) & 0x0f)] + (wch & 0xf)];
2822 if (ce != (unsigned int)-1)
2824 WCHAR key;
2825 if ((key = ce >> 16))
2827 *key_ptr[0]++ = key >> 8;
2828 *key_ptr[0]++ = key & 0xff;
2830 /* make key 1 start from 2 */
2831 if ((key = (ce >> 8) & 0xff)) *key_ptr[1]++ = key + 1;
2832 /* make key 2 start from 2 */
2833 if ((key = (ce >> 4) & 0x0f)) *key_ptr[2]++ = key + 1;
2834 /* key 3 is always a character code */
2835 if (ce & 1)
2837 if (wch >> 8) *key_ptr[3]++ = wch >> 8;
2838 if (wch & 0xff) *key_ptr[3]++ = wch & 0xff;
2841 else
2843 *key_ptr[0]++ = 0xff;
2844 *key_ptr[0]++ = 0xfe;
2845 if (wch >> 8) *key_ptr[0]++ = wch >> 8;
2846 if (wch & 0xff) *key_ptr[0]++ = wch & 0xff;
2852 *key_ptr[0] = 1;
2853 *key_ptr[1] = 1;
2854 *key_ptr[2] = 1;
2855 *key_ptr[3]++ = 1;
2856 *key_ptr[3] = 0;
2857 return key_ptr[3] - dst;
2861 /* compose a full-width katakana. return consumed source characters. */
2862 static int compose_katakana( const WCHAR *src, int srclen, WCHAR *dst )
2864 static const BYTE katakana_map[] =
2866 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
2867 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
2868 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
2869 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
2870 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
2871 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
2872 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
2873 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
2875 WCHAR dummy;
2876 int shift;
2878 if (!dst) dst = &dummy;
2880 switch (*src)
2882 case 0x309b:
2883 case 0x309c:
2884 *dst = *src - 2;
2885 return 1;
2886 case 0x30f0:
2887 case 0x30f1:
2888 case 0x30fd:
2889 *dst = *src;
2890 break;
2891 default:
2892 shift = *src - 0xff61;
2893 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
2894 *dst = katakana_map[shift] | 0x3000;
2895 break;
2898 if (srclen <= 1) return 1;
2900 switch (src[1])
2902 case 0xff9e: /* datakuten (voiced sound) */
2903 if ((*src >= 0xff76 && *src <= 0xff84) || (*src >= 0xff8a && *src <= 0xff8e) || *src == 0x30fd)
2904 *dst += 1;
2905 else if (*src == 0xff73)
2906 *dst = 0x30f4; /* KATAKANA LETTER VU */
2907 else if (*src == 0xff9c)
2908 *dst = 0x30f7; /* KATAKANA LETTER VA */
2909 else if (*src == 0x30f0)
2910 *dst = 0x30f8; /* KATAKANA LETTER VI */
2911 else if (*src == 0x30f1)
2912 *dst = 0x30f9; /* KATAKANA LETTER VE */
2913 else if (*src == 0xff66)
2914 *dst = 0x30fa; /* KATAKANA LETTER VO */
2915 else
2916 return 1;
2917 break;
2918 case 0xff9f: /* handakuten (semi-voiced sound) */
2919 if (*src >= 0xff8a && *src <= 0xff8e)
2920 *dst += 2;
2921 else
2922 return 1;
2923 break;
2924 default:
2925 return 1;
2927 return 2;
2930 /* map one or two half-width characters to one full-width character */
2931 static int map_to_fullwidth( const WCHAR *src, int srclen, WCHAR *dst )
2933 INT n;
2935 if (*src <= '~' && *src > ' ' && *src != '\\')
2936 *dst = *src - 0x20 + 0xff00;
2937 else if (*src == ' ')
2938 *dst = 0x3000;
2939 else if (*src <= 0x00af && *src >= 0x00a2)
2941 static const BYTE misc_symbols_table[] =
2943 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
2944 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
2946 if (misc_symbols_table[*src - 0x00a2])
2947 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
2948 else
2949 *dst = *src;
2951 else if (*src == 0x20a9) /* WON SIGN */
2952 *dst = 0xffe6;
2953 else if ((n = compose_katakana(src, srclen, dst)) > 0)
2954 return n;
2955 else if (*src >= 0xffa0 && *src <= 0xffdc)
2957 static const BYTE hangul_mapping_table[] =
2959 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
2960 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
2961 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
2962 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
2963 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
2964 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
2965 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
2966 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
2969 if (hangul_mapping_table[*src - 0xffa0])
2970 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
2971 else
2972 *dst = *src;
2974 else
2975 *dst = *src;
2977 return 1;
2980 /* decompose a full-width katakana character into one or two half-width characters. */
2981 static int decompose_katakana( WCHAR c, WCHAR *dst, int dstlen )
2983 static const BYTE katakana_map[] =
2985 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
2986 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
2987 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
2988 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
2989 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
2990 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
2991 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
2992 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
2993 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
2994 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
2995 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
2996 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
2997 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
2999 int len = 0, shift = c - 0x3099;
3000 BYTE k;
3002 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
3004 k = katakana_map[shift];
3005 if (!k)
3007 if (dstlen > 0) *dst = c;
3008 len++;
3010 else if (k > 0x60)
3012 if (dstlen > 0) *dst = k | 0xff00;
3013 len++;
3015 else
3017 if (dstlen >= 2)
3019 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3020 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3022 len += 2;
3024 return len;
3027 /* map single full-width character to single or double half-width characters. */
3028 static int map_to_halfwidth( WCHAR c, WCHAR *dst, int dstlen )
3030 int n = decompose_katakana( c, dst, dstlen );
3031 if (n > 0) return n;
3033 if (c == 0x3000)
3034 *dst = ' ';
3035 else if (c == 0x3001)
3036 *dst = 0xff64;
3037 else if (c == 0x3002)
3038 *dst = 0xff61;
3039 else if (c == 0x300c || c == 0x300d)
3040 *dst = (c - 0x300c) + 0xff62;
3041 else if (c >= 0x3131 && c <= 0x3163)
3043 *dst = c - 0x3131 + 0xffa1;
3044 if (*dst >= 0xffbf) *dst += 3;
3045 if (*dst >= 0xffc8) *dst += 2;
3046 if (*dst >= 0xffd0) *dst += 2;
3047 if (*dst >= 0xffd8) *dst += 2;
3049 else if (c == 0x3164)
3050 *dst = 0xffa0;
3051 else if (c == 0x2019)
3052 *dst = '\'';
3053 else if (c == 0x201d)
3054 *dst = '"';
3055 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3056 *dst = c - 0xff00 + 0x20;
3057 else if (c >= 0xffe0 && c <= 0xffe6)
3059 static const WCHAR misc_symbol_map[] = { 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9 };
3060 *dst = misc_symbol_map[c - 0xffe0];
3062 else
3063 *dst = c;
3065 return 1;
3069 /* 32-bit collation element table format:
3070 * unicode weight - high 16 bit, diacritic weight - high 8 bit of low 16 bit,
3071 * case weight - high 4 bit of low 8 bit.
3074 enum weight { UNICODE_WEIGHT, DIACRITIC_WEIGHT, CASE_WEIGHT };
3076 static unsigned int get_weight( WCHAR ch, enum weight type )
3078 unsigned int ret;
3080 ret = collation_table[collation_table[collation_table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
3081 if (ret == ~0u) return ch;
3083 switch (type)
3085 case UNICODE_WEIGHT: return ret >> 16;
3086 case DIACRITIC_WEIGHT: return (ret >> 8) & 0xff;
3087 case CASE_WEIGHT: return (ret >> 4) & 0x0f;
3088 default: return 0;
3093 static void inc_str_pos( const WCHAR **str, int *len, unsigned int *dpos, unsigned int *dlen )
3095 (*dpos)++;
3096 if (*dpos == *dlen)
3098 *dpos = *dlen = 0;
3099 (*str)++;
3100 (*len)--;
3105 static int compare_weights(int flags, const WCHAR *str1, int len1,
3106 const WCHAR *str2, int len2, enum weight type )
3108 unsigned int ce1, ce2, dpos1 = 0, dpos2 = 0, dlen1 = 0, dlen2 = 0;
3109 const WCHAR *dstr1 = NULL, *dstr2 = NULL;
3111 while (len1 > 0 && len2 > 0)
3113 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3114 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3116 if (flags & NORM_IGNORESYMBOLS)
3118 int skip = 0;
3119 /* FIXME: not tested */
3120 if (get_char_type( CT_CTYPE1, dstr1[dpos1] ) & (C1_PUNCT | C1_SPACE))
3122 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3123 skip = 1;
3125 if (get_char_type( CT_CTYPE1, dstr2[dpos2] ) & (C1_PUNCT | C1_SPACE))
3127 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3128 skip = 1;
3130 if (skip) continue;
3133 /* hyphen and apostrophe are treated differently depending on
3134 * whether SORT_STRINGSORT specified or not
3136 if (type == UNICODE_WEIGHT && !(flags & SORT_STRINGSORT))
3138 if (dstr1[dpos1] == '-' || dstr1[dpos1] == '\'')
3140 if (dstr2[dpos2] != '-' && dstr2[dpos2] != '\'')
3142 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3143 continue;
3146 else if (dstr2[dpos2] == '-' || dstr2[dpos2] == '\'')
3148 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3149 continue;
3153 ce1 = get_weight( dstr1[dpos1], type );
3154 if (!ce1)
3156 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3157 continue;
3159 ce2 = get_weight( dstr2[dpos2], type );
3160 if (!ce2)
3162 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3163 continue;
3166 if (ce1 - ce2) return ce1 - ce2;
3168 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3169 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3171 while (len1)
3173 if (!dlen1 && !(dstr1 = get_decomposition( *str1, &dlen1 ))) dstr1 = str1;
3174 ce1 = get_weight( dstr1[dpos1], type );
3175 if (ce1) break;
3176 inc_str_pos( &str1, &len1, &dpos1, &dlen1 );
3178 while (len2)
3180 if (!dlen2 && !(dstr2 = get_decomposition( *str2, &dlen2 ))) dstr2 = str2;
3181 ce2 = get_weight( dstr2[dpos2], type );
3182 if (ce2) break;
3183 inc_str_pos( &str2, &len2, &dpos2, &dlen2 );
3185 return len1 - len2;
3189 static const struct geoinfo *get_geoinfo_ptr( GEOID geoid )
3191 int min = 0, max = ARRAY_SIZE( geoinfodata )-1;
3193 while (min <= max)
3195 int n = (min + max)/2;
3196 const struct geoinfo *ptr = &geoinfodata[n];
3197 if (geoid == ptr->id) /* we don't need empty entries */
3198 return *ptr->iso2W ? ptr : NULL;
3199 if (ptr->id > geoid) max = n-1;
3200 else min = n+1;
3202 return NULL;
3206 static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare )
3208 static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
3209 int first, last, limit, dayinsecs;
3211 if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */
3212 if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */
3214 /* if year is 0 then date is in day-of-week format, otherwise
3215 * it's absolute date.
3217 if (!compare->wYear)
3219 /* wDay is interpreted as number of the week in the month
3220 * 5 means: the last week in the month */
3221 /* calculate the day of the first DayOfWeek in the month */
3222 first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1;
3223 /* check needed for the 5th weekday of the month */
3224 last = month_lengths[tf->Month - 1] +
3225 (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400))));
3226 limit = first + 7 * (compare->wDay - 1);
3227 if (limit > last) limit -= 7;
3229 else limit = compare->wDay;
3231 limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60;
3232 dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second;
3233 return dayinsecs - limit;
3237 static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local )
3239 int year;
3240 BOOL before_standard_date, after_daylight_date;
3241 LARGE_INTEGER t2;
3242 TIME_FIELDS tf;
3244 if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN;
3246 /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */
3247 if (info->StandardDate.wMonth == 0 ||
3248 (info->StandardDate.wYear == 0 &&
3249 (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 ||
3250 info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5)))
3252 SetLastError( ERROR_INVALID_PARAMETER );
3253 return TIME_ZONE_ID_INVALID;
3256 if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000;
3257 RtlTimeToTimeFields( &time, &tf );
3258 year = tf.Year;
3259 if (!is_local)
3261 t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000;
3262 RtlTimeToTimeFields( &t2, &tf );
3264 if (tf.Year == year)
3265 before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0;
3266 else
3267 before_standard_date = tf.Year < year;
3269 if (!is_local)
3271 t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000;
3272 RtlTimeToTimeFields( &t2, &tf );
3274 if (tf.Year == year)
3275 after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0;
3276 else
3277 after_daylight_date = tf.Year > year;
3279 if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */
3281 if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3283 else /* Down south */
3285 if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT;
3287 return TIME_ZONE_ID_STANDARD;
3291 /* Note: the Internal_ functions are not documented. The number of parameters
3292 * should be correct, but their exact meaning may not.
3295 /******************************************************************************
3296 * Internal_EnumCalendarInfo (kernelbase.@)
3298 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc, LCID lcid, CALID id,
3299 CALTYPE type, BOOL unicode, BOOL ex,
3300 BOOL exex, LPARAM lparam )
3302 WCHAR buffer[256];
3303 CALID calendars[2] = { id };
3304 INT ret, i;
3306 if (!proc)
3308 SetLastError( ERROR_INVALID_PARAMETER );
3309 return FALSE;
3312 if (id == ENUM_ALL_CALENDARS)
3314 if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
3315 (WCHAR *)&calendars[0], sizeof(calendars[0]) / sizeof(WCHAR) )) return FALSE;
3316 if (!GetLocaleInfoW( lcid, LOCALE_IOPTIONALCALENDAR | LOCALE_RETURN_NUMBER,
3317 (WCHAR *)&calendars[1], sizeof(calendars[1]) / sizeof(WCHAR) )) calendars[1] = 0;
3320 for (i = 0; i < ARRAY_SIZE(calendars) && calendars[i]; i++)
3322 id = calendars[i];
3323 if (type & CAL_RETURN_NUMBER)
3324 ret = GetCalendarInfoW( lcid, id, type, NULL, 0, (LPDWORD)buffer );
3325 else if (unicode)
3326 ret = GetCalendarInfoW( lcid, id, type, buffer, ARRAY_SIZE(buffer), NULL );
3327 else
3329 WCHAR bufW[256];
3330 ret = GetCalendarInfoW( lcid, id, type, bufW, ARRAY_SIZE(bufW), NULL );
3331 if (ret) WideCharToMultiByte( CP_ACP, 0, bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
3334 if (ret)
3336 if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
3337 else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
3338 else ret = proc( buffer );
3340 if (!ret) break;
3342 return TRUE;
3346 /**************************************************************************
3347 * Internal_EnumDateFormats (kernelbase.@)
3349 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
3350 BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
3352 WCHAR buffer[256];
3353 LCTYPE lctype;
3354 CALID cal_id;
3355 INT ret;
3357 if (!proc)
3359 SetLastError( ERROR_INVALID_PARAMETER );
3360 return FALSE;
3362 if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER,
3363 (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR) ))
3364 return FALSE;
3366 switch (flags & ~LOCALE_USE_CP_ACP)
3368 case 0:
3369 case DATE_SHORTDATE:
3370 lctype = LOCALE_SSHORTDATE;
3371 break;
3372 case DATE_LONGDATE:
3373 lctype = LOCALE_SLONGDATE;
3374 break;
3375 case DATE_YEARMONTH:
3376 lctype = LOCALE_SYEARMONTH;
3377 break;
3378 default:
3379 FIXME( "unknown date format 0x%08lx\n", flags );
3380 SetLastError( ERROR_INVALID_PARAMETER );
3381 return FALSE;
3384 lctype |= flags & LOCALE_USE_CP_ACP;
3385 if (unicode)
3386 ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
3387 else
3388 ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
3390 if (ret)
3392 if (exex) ((DATEFMT_ENUMPROCEXEX)proc)( buffer, cal_id, lparam );
3393 else if (ex) ((DATEFMT_ENUMPROCEXW)proc)( buffer, cal_id );
3394 else proc( buffer );
3396 return TRUE;
3400 /******************************************************************************
3401 * Internal_EnumLanguageGroupLocales (kernelbase.@)
3403 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
3404 DWORD flags, LONG_PTR param, BOOL unicode )
3406 WCHAR name[10], value[10];
3407 DWORD name_len, value_len, type, index = 0, alt = 0;
3408 HKEY key, altkey;
3409 LCID lcid;
3411 if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
3413 SetLastError( ERROR_INVALID_PARAMETER );
3414 return FALSE;
3417 if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
3418 if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
3420 for (;;)
3422 name_len = ARRAY_SIZE(name);
3423 value_len = sizeof(value);
3424 if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
3425 &type, (BYTE *)value, &value_len ))
3427 if (alt++) break;
3428 index = 0;
3429 continue;
3431 if (type != REG_SZ) continue;
3432 if (id != wcstoul( value, NULL, 16 )) continue;
3433 lcid = wcstoul( name, NULL, 16 );
3434 if (!unicode)
3436 char nameA[10];
3437 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3438 if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
3440 else if (!proc( id, lcid, name, param )) break;
3442 RegCloseKey( altkey );
3443 RegCloseKey( key );
3444 return TRUE;
3448 /***********************************************************************
3449 * Internal_EnumSystemCodePages (kernelbase.@)
3451 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
3452 BOOL unicode )
3454 WCHAR name[10];
3455 DWORD name_len, type, index = 0;
3456 HKEY key;
3458 if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
3460 for (;;)
3462 name_len = ARRAY_SIZE(name);
3463 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
3464 if (type != REG_SZ) continue;
3465 if (!wcstoul( name, NULL, 10 )) continue;
3466 if (!unicode)
3468 char nameA[10];
3469 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3470 if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
3472 else if (!proc( name )) break;
3474 RegCloseKey( key );
3475 return TRUE;
3479 /******************************************************************************
3480 * Internal_EnumSystemLanguageGroups (kernelbase.@)
3482 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
3483 DWORD flags, LONG_PTR param, BOOL unicode )
3485 WCHAR name[10], value[10], descr[80];
3486 DWORD name_len, value_len, type, index = 0;
3487 HKEY key;
3488 LGRPID id;
3490 if (!proc)
3492 SetLastError( ERROR_INVALID_PARAMETER );
3493 return FALSE;
3496 switch (flags)
3498 case 0:
3499 flags = LGRPID_INSTALLED;
3500 break;
3501 case LGRPID_INSTALLED:
3502 case LGRPID_SUPPORTED:
3503 break;
3504 default:
3505 SetLastError( ERROR_INVALID_FLAGS );
3506 return FALSE;
3509 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
3511 for (;;)
3513 name_len = ARRAY_SIZE(name);
3514 value_len = sizeof(value);
3515 if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
3516 if (type != REG_SZ) continue;
3518 id = wcstoul( name, NULL, 16 );
3520 if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
3521 if (!LoadStringW( kernel32_handle, 0x2000 + id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
3522 TRACE( "%p: %lu %s %s %lx %Ix\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
3523 if (!unicode)
3525 char nameA[10], descrA[80];
3526 WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
3527 WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
3528 if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
3530 else if (!proc( id, name, descr, flags, param )) break;
3532 RegCloseKey( key );
3533 return TRUE;
3537 /**************************************************************************
3538 * Internal_EnumTimeFormats (kernelbase.@)
3540 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
3541 BOOL unicode, BOOL ex, LPARAM lparam )
3543 WCHAR buffer[256];
3544 LCTYPE lctype;
3545 INT ret;
3547 if (!proc)
3549 SetLastError( ERROR_INVALID_PARAMETER );
3550 return FALSE;
3552 switch (flags & ~LOCALE_USE_CP_ACP)
3554 case 0:
3555 lctype = LOCALE_STIMEFORMAT;
3556 break;
3557 case TIME_NOSECONDS:
3558 lctype = LOCALE_SSHORTTIME;
3559 break;
3560 default:
3561 FIXME( "Unknown time format %lx\n", flags );
3562 SetLastError( ERROR_INVALID_PARAMETER );
3563 return FALSE;
3566 lctype |= flags & LOCALE_USE_CP_ACP;
3567 if (unicode)
3568 ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
3569 else
3570 ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
3572 if (ret)
3574 if (ex) ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
3575 else proc( buffer );
3577 return TRUE;
3581 /******************************************************************************
3582 * Internal_EnumUILanguages (kernelbase.@)
3584 BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
3585 LONG_PTR param, BOOL unicode )
3587 WCHAR nameW[LOCALE_NAME_MAX_LENGTH];
3588 char nameA[LOCALE_NAME_MAX_LENGTH];
3589 DWORD i;
3591 if (!proc)
3593 SetLastError( ERROR_INVALID_PARAMETER );
3594 return FALSE;
3596 if (flags & ~(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))
3598 SetLastError( ERROR_INVALID_FLAGS );
3599 return FALSE;
3602 for (i = 0; i < locale_table->nb_lcnames; i++)
3604 if (!lcnames_index[i].name) continue; /* skip invariant locale */
3605 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
3606 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
3607 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
3608 continue; /* skip alternate sorts */
3609 if (flags & MUI_LANGUAGE_NAME)
3611 const WCHAR *str = locale_strings + lcnames_index[i].name;
3613 if (unicode)
3615 memcpy( nameW, str + 1, (*str + 1) * sizeof(WCHAR) );
3616 if (!proc( nameW, param )) break;
3618 else
3620 WideCharToMultiByte( CP_ACP, 0, str + 1, -1, nameA, sizeof(nameA), NULL, NULL );
3621 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
3624 else
3626 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
3627 if (unicode)
3629 swprintf( nameW, ARRAY_SIZE(nameW), L"%04lx", lcnames_index[i].id );
3630 if (!proc( nameW, param )) break;
3632 else
3634 sprintf( nameA, "%04x", lcnames_index[i].id );
3635 if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
3639 return TRUE;
3643 /******************************************************************************
3644 * CompareStringEx (kernelbase.@)
3646 INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1,
3647 const WCHAR *str2, int len2, NLSVERSIONINFO *version,
3648 void *reserved, LPARAM handle )
3650 DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | SORT_STRINGSORT |
3651 NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH | LOCALE_USE_CP_ACP;
3652 DWORD semistub_flags = NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE | LINGUISTIC_IGNOREDIACRITIC |
3653 SORT_DIGITSASNUMBERS | 0x10000000;
3654 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3655 INT ret;
3656 static int once;
3658 if (version) FIXME( "unexpected version parameter\n" );
3659 if (reserved) FIXME( "unexpected reserved value\n" );
3660 if (handle) FIXME( "unexpected handle\n" );
3662 if (!str1 || !str2)
3664 SetLastError( ERROR_INVALID_PARAMETER );
3665 return 0;
3668 if (flags & ~(supported_flags | semistub_flags))
3670 SetLastError( ERROR_INVALID_FLAGS );
3671 return 0;
3674 if (flags & semistub_flags)
3676 if (!once++) FIXME( "semi-stub behavior for flag(s) 0x%lx\n", flags & semistub_flags );
3679 if (len1 < 0) len1 = lstrlenW(str1);
3680 if (len2 < 0) len2 = lstrlenW(str2);
3682 ret = compare_weights( flags, str1, len1, str2, len2, UNICODE_WEIGHT );
3683 if (!ret)
3685 if (!(flags & NORM_IGNORENONSPACE))
3686 ret = compare_weights( flags, str1, len1, str2, len2, DIACRITIC_WEIGHT );
3687 if (!ret && !(flags & NORM_IGNORECASE))
3688 ret = compare_weights( flags, str1, len1, str2, len2, CASE_WEIGHT );
3690 if (!ret) return CSTR_EQUAL;
3691 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3695 /******************************************************************************
3696 * CompareStringA (kernelbase.@)
3698 INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1,
3699 const char *str2, int len2 )
3701 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3702 WCHAR *buf2W = buf1W + 130;
3703 LPWSTR str1W, str2W;
3704 INT len1W = 0, len2W = 0, ret;
3705 UINT locale_cp = CP_ACP;
3707 if (!str1 || !str2)
3709 SetLastError( ERROR_INVALID_PARAMETER );
3710 return 0;
3713 if (flags & SORT_DIGITSASNUMBERS)
3715 SetLastError( ERROR_INVALID_FLAGS );
3716 return 0;
3719 if (len1 < 0) len1 = strlen(str1);
3720 if (len2 < 0) len2 = strlen(str2);
3722 locale_cp = get_lcid_codepage( lcid, flags );
3723 if (len1)
3725 if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 );
3726 if (len1W) str1W = buf1W;
3727 else
3729 len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 );
3730 str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) );
3731 if (!str1W)
3733 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3734 return 0;
3736 MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W );
3739 else
3741 len1W = 0;
3742 str1W = buf1W;
3745 if (len2)
3747 if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 );
3748 if (len2W) str2W = buf2W;
3749 else
3751 len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 );
3752 str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) );
3753 if (!str2W)
3755 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
3756 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
3757 return 0;
3759 MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W );
3762 else
3764 len2W = 0;
3765 str2W = buf2W;
3768 ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W );
3770 if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W );
3771 if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W );
3772 return ret;
3776 /******************************************************************************
3777 * CompareStringW (kernelbase.@)
3779 INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1,
3780 const WCHAR *str2, int len2 )
3782 return CompareStringEx( NULL, flags, str1, len1, str2, len2, NULL, NULL, 0 );
3786 /******************************************************************************
3787 * CompareStringOrdinal (kernelbase.@)
3789 INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
3790 const WCHAR *str2, INT len2, BOOL ignore_case )
3792 int ret;
3794 if (!str1 || !str2)
3796 SetLastError( ERROR_INVALID_PARAMETER );
3797 return 0;
3799 if (len1 < 0) len1 = lstrlenW( str1 );
3800 if (len2 < 0) len2 = lstrlenW( str2 );
3802 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
3803 if (ret < 0) return CSTR_LESS_THAN;
3804 if (ret > 0) return CSTR_GREATER_THAN;
3805 return CSTR_EQUAL;
3809 /******************************************************************************
3810 * ConvertDefaultLocale (kernelbase.@)
3812 LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
3814 get_locale_by_id( &lcid, 0 );
3815 return lcid;
3819 /******************************************************************************
3820 * EnumCalendarInfoW (kernelbase.@)
3822 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
3823 CALID id, CALTYPE type )
3825 return Internal_EnumCalendarInfo( proc, lcid, id, type, TRUE, FALSE, FALSE, 0 );
3829 /******************************************************************************
3830 * EnumCalendarInfoExW (kernelbase.@)
3832 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
3833 CALID id, CALTYPE type )
3835 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, FALSE, 0 );
3838 /******************************************************************************
3839 * EnumCalendarInfoExEx (kernelbase.@)
3841 BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
3842 LPCWSTR reserved, CALTYPE type, LPARAM lparam )
3844 LCID lcid = LocaleNameToLCID( locale, 0 );
3845 return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, TRUE, lparam );
3849 /**************************************************************************
3850 * EnumDateFormatsW (kernelbase.@)
3852 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
3854 return Internal_EnumDateFormats( proc, lcid, flags, TRUE, FALSE, FALSE, 0 );
3858 /**************************************************************************
3859 * EnumDateFormatsExW (kernelbase.@)
3861 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
3863 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, FALSE, 0 );
3867 /**************************************************************************
3868 * EnumDateFormatsExEx (kernelbase.@)
3870 BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
3871 DWORD flags, LPARAM lparam )
3873 LCID lcid = LocaleNameToLCID( locale, 0 );
3874 return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, TRUE, lparam );
3879 /******************************************************************************
3880 * EnumDynamicTimeZoneInformation (kernelbase.@)
3882 DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index,
3883 DYNAMIC_TIME_ZONE_INFORMATION *info )
3885 DYNAMIC_TIME_ZONE_INFORMATION tz;
3886 LSTATUS ret;
3887 DWORD size;
3889 if (!info) return ERROR_INVALID_PARAMETER;
3891 size = ARRAY_SIZE(tz.TimeZoneKeyName);
3892 ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL );
3893 if (ret) return ret;
3895 tz.DynamicDaylightTimeDisabled = TRUE;
3896 if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError();
3898 lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName );
3899 info->DynamicDaylightTimeDisabled = FALSE;
3900 return 0;
3904 /******************************************************************************
3905 * EnumLanguageGroupLocalesW (kernelbase.@)
3907 BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
3908 DWORD flags, LONG_PTR param )
3910 return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
3914 /******************************************************************************
3915 * EnumUILanguagesW (kernelbase.@)
3917 BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
3919 return Internal_EnumUILanguages( proc, flags, param, TRUE );
3923 /***********************************************************************
3924 * EnumSystemCodePagesW (kernelbase.@)
3926 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
3928 return Internal_EnumSystemCodePages( proc, flags, TRUE );
3932 /******************************************************************************
3933 * EnumSystemGeoID (kernelbase.@)
3935 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc )
3937 INT i;
3939 TRACE( "(%ld, %ld, %p)\n", class, parent, proc );
3941 if (!proc)
3943 SetLastError( ERROR_INVALID_PARAMETER );
3944 return FALSE;
3946 if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL)
3948 SetLastError( ERROR_INVALID_FLAGS );
3949 return FALSE;
3952 for (i = 0; i < ARRAY_SIZE(geoinfodata); i++)
3954 const struct geoinfo *ptr = &geoinfodata[i];
3956 if (class == GEOCLASS_NATION && (ptr->kind != LOCATION_NATION)) continue;
3957 /* LOCATION_BOTH counts as region */
3958 if (class == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION)) continue;
3959 if (parent && ptr->parent != parent) continue;
3960 if (!proc( ptr->id )) break;
3962 return TRUE;
3966 /******************************************************************************
3967 * EnumSystemLanguageGroupsW (kernelbase.@)
3969 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
3970 DWORD flags, LONG_PTR param )
3972 return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
3976 /******************************************************************************
3977 * EnumSystemLocalesA (kernelbase.@)
3979 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
3981 char name[10];
3982 DWORD i;
3984 for (i = 0; i < locale_table->nb_lcnames; i++)
3986 if (!lcnames_index[i].name) continue; /* skip invariant locale */
3987 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
3988 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
3989 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
3990 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
3991 continue; /* skip alternate sorts */
3992 sprintf( name, "%08x", lcnames_index[i].id );
3993 if (!proc( name )) break;
3995 return TRUE;
3999 /******************************************************************************
4000 * EnumSystemLocalesW (kernelbase.@)
4002 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
4004 WCHAR name[10];
4005 DWORD i;
4007 for (i = 0; i < locale_table->nb_lcnames; i++)
4009 if (!lcnames_index[i].name) continue; /* skip invariant locale */
4010 if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */
4011 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4012 if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */
4013 if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS))
4014 continue; /* skip alternate sorts */
4015 swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id );
4016 if (!proc( name )) break;
4018 return TRUE;
4022 /******************************************************************************
4023 * EnumSystemLocalesEx (kernelbase.@)
4025 BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
4026 LPARAM param, void *reserved )
4028 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
4029 DWORD i, flags;
4031 if (reserved)
4033 SetLastError( ERROR_INVALID_PARAMETER );
4034 return FALSE;
4037 for (i = 0; i < locale_table->nb_lcnames; i++)
4039 const NLS_LOCALE_DATA *locale = get_locale_data( lcnames_index[i].idx );
4040 const WCHAR *str = locale_strings + lcnames_index[i].name;
4042 if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */
4043 memcpy( buffer, str + 1, (*str + 1) * sizeof(WCHAR) );
4044 if (SORTIDFROMLCID( lcnames_index[i].id ) || wcschr( str + 1, '_' ))
4045 flags = LOCALE_ALTERNATE_SORTS;
4046 else
4047 flags = LOCALE_WINDOWS | (locale->inotneutral ? LOCALE_SPECIFICDATA : LOCALE_NEUTRALDATA);
4048 if (wanted_flags && !(flags & wanted_flags)) continue;
4049 if (!proc( buffer, flags, param )) break;
4051 return TRUE;
4055 /**************************************************************************
4056 * EnumTimeFormatsW (kernelbase.@)
4058 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
4060 return Internal_EnumTimeFormats( proc, lcid, flags, TRUE, FALSE, 0 );
4064 /**************************************************************************
4065 * EnumTimeFormatsEx (kernelbase.@)
4067 BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
4068 DWORD flags, LPARAM lparam )
4070 LCID lcid = LocaleNameToLCID( locale, 0 );
4071 return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, lparam );
4075 /**************************************************************************
4076 * FindNLSString (kernelbase.@)
4078 INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src,
4079 int srclen, const WCHAR *value, int valuelen, int *found )
4081 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
4083 LCIDToLocaleName( lcid, locale, ARRAY_SIZE(locale), 0 );
4084 return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 );
4088 /**************************************************************************
4089 * FindNLSStringEx (kernelbase.@)
4091 INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src,
4092 int srclen, const WCHAR *value, int valuelen, int *found,
4093 NLSVERSIONINFO *version, void *reserved, LPARAM handle )
4095 /* FIXME: this function should normalize strings before calling CompareStringEx() */
4096 DWORD mask = flags;
4097 int offset, inc, count;
4099 TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale), flags,
4100 wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found,
4101 version, reserved, handle );
4103 if (version || reserved || handle || !IsValidLocaleName(locale) ||
4104 !src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1)
4106 SetLastError( ERROR_INVALID_PARAMETER );
4107 return -1;
4109 if (srclen == -1) srclen = lstrlenW(src);
4110 if (valuelen == -1) valuelen = lstrlenW(value);
4112 srclen -= valuelen;
4113 if (srclen < 0) return -1;
4115 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
4116 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? srclen + 1 : 1;
4117 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : srclen;
4118 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4119 while (count--)
4121 if (CompareStringEx( locale, mask, src + offset, valuelen,
4122 value, valuelen, NULL, NULL, 0 ) == CSTR_EQUAL)
4124 if (found) *found = valuelen;
4125 return offset;
4127 offset += inc;
4129 return -1;
4133 /******************************************************************************
4134 * FindStringOrdinal (kernelbase.@)
4136 INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
4137 const WCHAR *val, INT val_size, BOOL ignore_case )
4139 INT offset, inc, count;
4141 TRACE( "%#lx %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
4142 wine_dbgstr_w(val), val_size, ignore_case );
4144 if (!src || !val)
4146 SetLastError( ERROR_INVALID_PARAMETER );
4147 return -1;
4150 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
4152 SetLastError( ERROR_INVALID_FLAGS );
4153 return -1;
4156 if (src_size == -1) src_size = lstrlenW( src );
4157 if (val_size == -1) val_size = lstrlenW( val );
4159 SetLastError( ERROR_SUCCESS );
4160 src_size -= val_size;
4161 if (src_size < 0) return -1;
4163 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
4164 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
4165 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
4166 while (count--)
4168 if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
4169 return offset;
4170 offset += inc;
4172 return -1;
4176 /******************************************************************************
4177 * FoldStringW (kernelbase.@)
4179 INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
4181 NTSTATUS status;
4182 WCHAR *buf = dst;
4183 int len = dstlen;
4185 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
4187 SetLastError( ERROR_INVALID_PARAMETER );
4188 return 0;
4190 if (srclen == -1) srclen = lstrlenW(src) + 1;
4192 if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE)))
4194 len = srclen * 4;
4195 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4197 SetLastError( ERROR_OUTOFMEMORY );
4198 return 0;
4202 for (;;)
4204 status = fold_string( flags, src, srclen, buf, &len );
4205 if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf );
4206 if (status != STATUS_BUFFER_TOO_SMALL) break;
4207 if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4209 SetLastError( ERROR_OUTOFMEMORY );
4210 return 0;
4213 if (status == STATUS_INVALID_PARAMETER_1)
4215 SetLastError( ERROR_INVALID_FLAGS );
4216 return 0;
4218 if (!set_ntstatus( status )) return 0;
4220 if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
4221 return len;
4225 static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang,
4226 BOOL ansi, WCHAR **buffer )
4228 DWORD len;
4230 if (!(flags & FORMAT_MESSAGE_FROM_STRING))
4232 const MESSAGE_RESOURCE_ENTRY *entry;
4233 NTSTATUS status = STATUS_INVALID_PARAMETER;
4235 if (flags & FORMAT_MESSAGE_FROM_HMODULE)
4237 HMODULE module = (HMODULE)src;
4238 if (!module) module = GetModuleHandleW( 0 );
4239 status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry );
4241 if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
4243 /* Fold win32 hresult to its embedded error code. */
4244 if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32)
4245 id = HRESULT_CODE( id );
4246 status = RtlFindMessage( kernel32_handle, RT_MESSAGETABLE, lang, id, &entry );
4248 if (!set_ntstatus( status )) return NULL;
4250 src = entry->Text;
4251 ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE);
4254 if (!ansi) return src;
4255 len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
4256 if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
4257 MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len );
4258 return *buffer;
4262 /***********************************************************************
4263 * FormatMessageA (kernelbase.@)
4265 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4266 char *buffer, DWORD size, va_list *args )
4268 DWORD ret = 0;
4269 ULONG len, retsize = 0;
4270 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4271 const WCHAR *src;
4272 WCHAR *result, *message = NULL;
4273 NTSTATUS status;
4275 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4277 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4279 if (!buffer)
4281 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4282 return 0;
4284 *(char **)buffer = NULL;
4286 if (size >= 32768)
4288 SetLastError( ERROR_INVALID_PARAMETER );
4289 return 0;
4292 if (width == 0xff) width = ~0u;
4294 if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0;
4296 if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 )))
4297 status = STATUS_NO_MEMORY;
4298 else
4299 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4300 TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4301 result, 65536, &retsize );
4303 HeapFree( GetProcessHeap(), 0, message );
4305 if (status == STATUS_BUFFER_OVERFLOW)
4307 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4308 goto done;
4310 if (!set_ntstatus( status )) goto done;
4312 len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL );
4313 if (len <= 1)
4315 SetLastError( ERROR_NO_WORK_DONE );
4316 goto done;
4319 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4321 char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len ));
4322 if (!buf)
4324 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4325 goto done;
4327 *(char **)buffer = buf;
4328 WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL );
4330 else if (len > size)
4332 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4333 goto done;
4335 else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL );
4337 ret = len - 1;
4339 done:
4340 HeapFree( GetProcessHeap(), 0, result );
4341 return ret;
4345 /***********************************************************************
4346 * FormatMessageW (kernelbase.@)
4348 DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid,
4349 WCHAR *buffer, DWORD size, va_list *args )
4351 ULONG retsize = 0;
4352 ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
4353 const WCHAR *src;
4354 WCHAR *message = NULL;
4355 NTSTATUS status;
4357 TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args );
4359 if (!buffer)
4361 SetLastError( ERROR_INVALID_PARAMETER );
4362 return 0;
4365 if (width == 0xff) width = ~0u;
4367 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL;
4369 if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0;
4371 if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
4373 WCHAR *result;
4374 ULONG alloc = max( size * sizeof(WCHAR), 65536 );
4376 for (;;)
4378 if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc )))
4380 status = STATUS_NO_MEMORY;
4381 break;
4383 status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4384 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4385 result, alloc, &retsize );
4386 if (!status)
4388 if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );
4389 else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
4390 result, max( retsize, size * sizeof(WCHAR) ));
4391 break;
4393 HeapFree( GetProcessHeap(), 0, result );
4394 if (status != STATUS_BUFFER_OVERFLOW) break;
4395 alloc *= 2;
4398 else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
4399 FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
4400 buffer, size * sizeof(WCHAR), &retsize );
4402 HeapFree( GetProcessHeap(), 0, message );
4404 if (status == STATUS_BUFFER_OVERFLOW)
4406 if (size) buffer[size - 1] = 0;
4407 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4408 return 0;
4410 if (!set_ntstatus( status )) return 0;
4411 if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE );
4412 return retsize / sizeof(WCHAR) - 1;
4416 /******************************************************************************
4417 * GetACP (kernelbase.@)
4419 UINT WINAPI GetACP(void)
4421 return nls_info.AnsiTableInfo.CodePage;
4425 /***********************************************************************
4426 * GetCPInfo (kernelbase.@)
4428 BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
4430 const CPTABLEINFO *table;
4432 if (!cpinfo)
4434 SetLastError( ERROR_INVALID_PARAMETER );
4435 return FALSE;
4437 switch (codepage)
4439 case CP_UTF7:
4440 case CP_UTF8:
4441 cpinfo->DefaultChar[0] = 0x3f;
4442 cpinfo->DefaultChar[1] = 0;
4443 memset( cpinfo->LeadByte, 0, sizeof(cpinfo->LeadByte) );
4444 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
4445 break;
4446 default:
4447 if (!(table = get_codepage_table( codepage ))) return FALSE;
4448 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4449 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4450 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4451 break;
4453 return TRUE;
4457 /***********************************************************************
4458 * GetCPInfoExW (kernelbase.@)
4460 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
4462 const CPTABLEINFO *table;
4463 int min, max, pos;
4465 if (!cpinfo)
4467 SetLastError( ERROR_INVALID_PARAMETER );
4468 return FALSE;
4470 switch (codepage)
4472 case CP_UTF7:
4473 cpinfo->DefaultChar[0] = 0x3f;
4474 cpinfo->DefaultChar[1] = 0;
4475 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
4476 cpinfo->MaxCharSize = 5;
4477 cpinfo->CodePage = CP_UTF7;
4478 cpinfo->UnicodeDefaultChar = 0x3f;
4479 break;
4480 case CP_UTF8:
4481 cpinfo->DefaultChar[0] = 0x3f;
4482 cpinfo->DefaultChar[1] = 0;
4483 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
4484 cpinfo->MaxCharSize = 4;
4485 cpinfo->CodePage = CP_UTF8;
4486 cpinfo->UnicodeDefaultChar = 0x3f;
4487 break;
4488 default:
4489 if (!(table = get_codepage_table( codepage ))) return FALSE;
4490 cpinfo->MaxCharSize = table->MaximumCharacterSize;
4491 memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
4492 memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
4493 cpinfo->CodePage = table->CodePage;
4494 cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
4495 break;
4498 min = 0;
4499 max = ARRAY_SIZE(codepage_names) - 1;
4500 cpinfo->CodePageName[0] = 0;
4501 while (min <= max)
4503 pos = (min + max) / 2;
4504 if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
4505 else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
4506 else
4508 wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
4509 break;
4512 return TRUE;
4516 /***********************************************************************
4517 * GetCalendarInfoW (kernelbase.@)
4519 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
4520 WCHAR *data, INT count, DWORD *value )
4522 static const LCTYPE lctype_map[] =
4524 0, /* not used */
4525 0, /* CAL_ICALINTVALUE */
4526 0, /* CAL_SCALNAME */
4527 0, /* CAL_IYEAROFFSETRANGE */
4528 0, /* CAL_SERASTRING */
4529 LOCALE_SSHORTDATE,
4530 LOCALE_SLONGDATE,
4531 LOCALE_SDAYNAME1,
4532 LOCALE_SDAYNAME2,
4533 LOCALE_SDAYNAME3,
4534 LOCALE_SDAYNAME4,
4535 LOCALE_SDAYNAME5,
4536 LOCALE_SDAYNAME6,
4537 LOCALE_SDAYNAME7,
4538 LOCALE_SABBREVDAYNAME1,
4539 LOCALE_SABBREVDAYNAME2,
4540 LOCALE_SABBREVDAYNAME3,
4541 LOCALE_SABBREVDAYNAME4,
4542 LOCALE_SABBREVDAYNAME5,
4543 LOCALE_SABBREVDAYNAME6,
4544 LOCALE_SABBREVDAYNAME7,
4545 LOCALE_SMONTHNAME1,
4546 LOCALE_SMONTHNAME2,
4547 LOCALE_SMONTHNAME3,
4548 LOCALE_SMONTHNAME4,
4549 LOCALE_SMONTHNAME5,
4550 LOCALE_SMONTHNAME6,
4551 LOCALE_SMONTHNAME7,
4552 LOCALE_SMONTHNAME8,
4553 LOCALE_SMONTHNAME9,
4554 LOCALE_SMONTHNAME10,
4555 LOCALE_SMONTHNAME11,
4556 LOCALE_SMONTHNAME12,
4557 LOCALE_SMONTHNAME13,
4558 LOCALE_SABBREVMONTHNAME1,
4559 LOCALE_SABBREVMONTHNAME2,
4560 LOCALE_SABBREVMONTHNAME3,
4561 LOCALE_SABBREVMONTHNAME4,
4562 LOCALE_SABBREVMONTHNAME5,
4563 LOCALE_SABBREVMONTHNAME6,
4564 LOCALE_SABBREVMONTHNAME7,
4565 LOCALE_SABBREVMONTHNAME8,
4566 LOCALE_SABBREVMONTHNAME9,
4567 LOCALE_SABBREVMONTHNAME10,
4568 LOCALE_SABBREVMONTHNAME11,
4569 LOCALE_SABBREVMONTHNAME12,
4570 LOCALE_SABBREVMONTHNAME13,
4571 LOCALE_SYEARMONTH,
4572 0, /* CAL_ITWODIGITYEARMAX */
4573 LOCALE_SSHORTESTDAYNAME1,
4574 LOCALE_SSHORTESTDAYNAME2,
4575 LOCALE_SSHORTESTDAYNAME3,
4576 LOCALE_SSHORTESTDAYNAME4,
4577 LOCALE_SSHORTESTDAYNAME5,
4578 LOCALE_SSHORTESTDAYNAME6,
4579 LOCALE_SSHORTESTDAYNAME7,
4580 LOCALE_SMONTHDAY,
4581 0, /* CAL_SABBREVERASTRING */
4583 DWORD flags = 0;
4584 CALTYPE calinfo = type & 0xffff;
4586 if (type & CAL_NOUSEROVERRIDE) FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
4587 if (type & CAL_USE_CP_ACP) FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
4589 if ((type & CAL_RETURN_NUMBER) && !value)
4591 SetLastError( ERROR_INVALID_PARAMETER );
4592 return 0;
4595 if (type & CAL_RETURN_GENITIVE_NAMES) flags |= LOCALE_RETURN_GENITIVE_NAMES;
4597 switch (calinfo)
4599 case CAL_ICALINTVALUE:
4600 if (type & CAL_RETURN_NUMBER)
4601 return GetLocaleInfoW( lcid, LOCALE_RETURN_NUMBER | LOCALE_ICALENDARTYPE,
4602 (WCHAR *)value, sizeof(*value) / sizeof(WCHAR) );
4603 return GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE, data, count );
4605 case CAL_SCALNAME:
4606 FIXME( "Unimplemented caltype %ld\n", calinfo );
4607 if (data) *data = 0;
4608 return 1;
4610 case CAL_IYEAROFFSETRANGE:
4611 case CAL_SERASTRING:
4612 case CAL_SABBREVERASTRING:
4613 FIXME( "Unimplemented caltype %ld\n", calinfo );
4614 return 0;
4616 case CAL_SSHORTDATE:
4617 case CAL_SLONGDATE:
4618 case CAL_SDAYNAME1:
4619 case CAL_SDAYNAME2:
4620 case CAL_SDAYNAME3:
4621 case CAL_SDAYNAME4:
4622 case CAL_SDAYNAME5:
4623 case CAL_SDAYNAME6:
4624 case CAL_SDAYNAME7:
4625 case CAL_SABBREVDAYNAME1:
4626 case CAL_SABBREVDAYNAME2:
4627 case CAL_SABBREVDAYNAME3:
4628 case CAL_SABBREVDAYNAME4:
4629 case CAL_SABBREVDAYNAME5:
4630 case CAL_SABBREVDAYNAME6:
4631 case CAL_SABBREVDAYNAME7:
4632 case CAL_SMONTHNAME1:
4633 case CAL_SMONTHNAME2:
4634 case CAL_SMONTHNAME3:
4635 case CAL_SMONTHNAME4:
4636 case CAL_SMONTHNAME5:
4637 case CAL_SMONTHNAME6:
4638 case CAL_SMONTHNAME7:
4639 case CAL_SMONTHNAME8:
4640 case CAL_SMONTHNAME9:
4641 case CAL_SMONTHNAME10:
4642 case CAL_SMONTHNAME11:
4643 case CAL_SMONTHNAME12:
4644 case CAL_SMONTHNAME13:
4645 case CAL_SABBREVMONTHNAME1:
4646 case CAL_SABBREVMONTHNAME2:
4647 case CAL_SABBREVMONTHNAME3:
4648 case CAL_SABBREVMONTHNAME4:
4649 case CAL_SABBREVMONTHNAME5:
4650 case CAL_SABBREVMONTHNAME6:
4651 case CAL_SABBREVMONTHNAME7:
4652 case CAL_SABBREVMONTHNAME8:
4653 case CAL_SABBREVMONTHNAME9:
4654 case CAL_SABBREVMONTHNAME10:
4655 case CAL_SABBREVMONTHNAME11:
4656 case CAL_SABBREVMONTHNAME12:
4657 case CAL_SABBREVMONTHNAME13:
4658 case CAL_SMONTHDAY:
4659 case CAL_SYEARMONTH:
4660 case CAL_SSHORTESTDAYNAME1:
4661 case CAL_SSHORTESTDAYNAME2:
4662 case CAL_SSHORTESTDAYNAME3:
4663 case CAL_SSHORTESTDAYNAME4:
4664 case CAL_SSHORTESTDAYNAME5:
4665 case CAL_SSHORTESTDAYNAME6:
4666 case CAL_SSHORTESTDAYNAME7:
4667 return GetLocaleInfoW( lcid, lctype_map[calinfo] | flags, data, count );
4669 case CAL_ITWODIGITYEARMAX:
4670 if (type & CAL_RETURN_NUMBER)
4672 *value = CALINFO_MAX_YEAR;
4673 return sizeof(DWORD) / sizeof(WCHAR);
4675 else
4677 WCHAR buffer[10];
4678 int ret = swprintf( buffer, ARRAY_SIZE(buffer), L"%u", CALINFO_MAX_YEAR ) + 1;
4679 if (!data) return ret;
4680 if (ret <= count)
4682 lstrcpyW( data, buffer );
4683 return ret;
4685 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4686 return 0;
4688 break;
4689 default:
4690 FIXME( "Unknown caltype %ld\n", calinfo );
4691 SetLastError( ERROR_INVALID_FLAGS );
4692 return 0;
4694 return 0;
4698 /***********************************************************************
4699 * GetCalendarInfoEx (kernelbase.@)
4701 INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *locale, CALID calendar, const WCHAR *reserved,
4702 CALTYPE type, WCHAR *data, INT count, DWORD *value )
4704 LCID lcid = LocaleNameToLCID( locale, 0 );
4705 return GetCalendarInfoW( lcid, calendar, type, data, count, value );
4708 static CRITICAL_SECTION tzname_section;
4709 static CRITICAL_SECTION_DEBUG tzname_section_debug =
4711 0, 0, &tzname_section,
4712 { &tzname_section_debug.ProcessLocksList, &tzname_section_debug.ProcessLocksList },
4713 0, 0, { (DWORD_PTR)(__FILE__ ": tzname_section") }
4715 static CRITICAL_SECTION tzname_section = { &tzname_section_debug, -1, 0, 0, 0, 0 };
4716 static struct {
4717 LCID lcid;
4718 WCHAR key_name[128];
4719 WCHAR standard_name[32];
4720 WCHAR daylight_name[32];
4721 } cached_tzname;
4723 /***********************************************************************
4724 * GetDynamicTimeZoneInformation (kernelbase.@)
4726 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info )
4728 HKEY key;
4729 LARGE_INTEGER now;
4731 if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info )))
4732 return TIME_ZONE_ID_INVALID;
4734 RtlEnterCriticalSection( &tzname_section );
4735 if (cached_tzname.lcid == GetThreadLocale() &&
4736 !wcscmp( info->TimeZoneKeyName, cached_tzname.key_name ))
4738 wcscpy( info->StandardName, cached_tzname.standard_name );
4739 wcscpy( info->DaylightName, cached_tzname.daylight_name );
4740 RtlLeaveCriticalSection( &tzname_section );
4742 else
4744 RtlLeaveCriticalSection( &tzname_section );
4745 if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))
4747 RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
4748 sizeof(info->StandardName), NULL, 0, system_dir );
4749 RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
4750 sizeof(info->DaylightName), NULL, 0, system_dir );
4751 RegCloseKey( key );
4753 else return TIME_ZONE_ID_INVALID;
4755 RtlEnterCriticalSection( &tzname_section );
4756 cached_tzname.lcid = GetThreadLocale();
4757 wcscpy( cached_tzname.key_name, info->TimeZoneKeyName );
4758 wcscpy( cached_tzname.standard_name, info->StandardName );
4759 wcscpy( cached_tzname.daylight_name, info->DaylightName );
4760 RtlLeaveCriticalSection( &tzname_section );
4763 NtQuerySystemTime( &now );
4764 return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE );
4768 /******************************************************************************
4769 * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@)
4771 DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info,
4772 DWORD *first, DWORD *last )
4774 HKEY key, dst_key = 0;
4775 DWORD type, count, ret = ERROR_FILE_NOT_FOUND;
4777 if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret;
4779 if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done;
4780 count = sizeof(DWORD);
4781 if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done;
4782 if (type != REG_DWORD) goto done;
4783 count = sizeof(DWORD);
4784 if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done;
4785 if (type != REG_DWORD) goto done;
4786 ret = 0;
4788 done:
4789 RegCloseKey( dst_key );
4790 RegCloseKey( key );
4791 return ret;
4795 /******************************************************************************
4796 * GetFileMUIInfo (kernelbase.@)
4798 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIInfo( DWORD flags, const WCHAR *path,
4799 FILEMUIINFO *info, DWORD *size )
4801 FIXME( "stub: %lu, %s, %p, %p\n", flags, debugstr_w(path), info, size );
4802 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4803 return FALSE;
4807 /******************************************************************************
4808 * GetFileMUIPath (kernelbase.@)
4810 BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags, const WCHAR *filepath,
4811 WCHAR *language, ULONG *languagelen,
4812 WCHAR *muipath, ULONG *muipathlen,
4813 ULONGLONG *enumerator )
4815 FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
4816 debugstr_w(language), languagelen, muipath, muipathlen, enumerator );
4817 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4818 return FALSE;
4822 /******************************************************************************
4823 * GetGeoInfoW (kernelbase.@)
4825 INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang )
4827 const struct geoinfo *ptr = get_geoinfo_ptr( id );
4828 WCHAR bufferW[12];
4829 const WCHAR *str = bufferW;
4830 int len;
4832 TRACE( "%ld %ld %p %d %d\n", id, type, data, count, lang );
4834 if (!ptr)
4836 SetLastError( ERROR_INVALID_PARAMETER );
4837 return 0;
4839 switch (type)
4841 case GEO_NATION:
4842 if (ptr->kind != LOCATION_NATION) return 0;
4843 /* fall through */
4844 case GEO_ID:
4845 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", ptr->id );
4846 break;
4847 case GEO_ISO_UN_NUMBER:
4848 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", ptr->uncode );
4849 break;
4850 case GEO_PARENT:
4851 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", ptr->parent );
4852 break;
4853 case GEO_ISO2:
4854 str = ptr->iso2W;
4855 break;
4856 case GEO_ISO3:
4857 str = ptr->iso3W;
4858 break;
4859 case GEO_RFC1766:
4860 case GEO_LCID:
4861 case GEO_FRIENDLYNAME:
4862 case GEO_OFFICIALNAME:
4863 case GEO_TIMEZONES:
4864 case GEO_OFFICIALLANGUAGES:
4865 case GEO_LATITUDE:
4866 case GEO_LONGITUDE:
4867 case GEO_DIALINGCODE:
4868 case GEO_CURRENCYCODE:
4869 case GEO_CURRENCYSYMBOL:
4870 case GEO_NAME:
4871 FIXME( "type %ld is not supported\n", type );
4872 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
4873 return 0;
4874 default:
4875 WARN( "unrecognized type %ld\n", type );
4876 SetLastError( ERROR_INVALID_FLAGS );
4877 return 0;
4880 len = lstrlenW(str) + 1;
4881 if (!data || !count) return len;
4883 memcpy( data, str, min(len, count) * sizeof(WCHAR) );
4884 if (count < len) SetLastError( ERROR_INSUFFICIENT_BUFFER );
4885 return count < len ? 0 : len;
4889 /******************************************************************************
4890 * GetLocaleInfoA (kernelbase.@)
4892 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
4894 WCHAR *bufferW;
4895 INT lenW, ret;
4897 TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid, lctype, buffer, len );
4899 if (len < 0 || (len && !buffer))
4901 SetLastError( ERROR_INVALID_PARAMETER );
4902 return 0;
4904 if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
4906 SetLastError( ERROR_INVALID_FLAGS );
4907 return 0;
4910 if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
4911 return GetLocaleInfoW( lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) ) * sizeof(WCHAR);
4913 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
4915 if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
4917 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4918 return 0;
4920 ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW );
4921 if (ret) ret = WideCharToMultiByte( get_lcid_codepage( lcid, lctype ), 0,
4922 bufferW, ret, buffer, len, NULL, NULL );
4923 RtlFreeHeap( GetProcessHeap(), 0, bufferW );
4924 return ret;
4928 /******************************************************************************
4929 * GetLocaleInfoW (kernelbase.@)
4931 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
4933 const NLS_LOCALE_DATA *locale;
4934 HRSRC hrsrc;
4935 HGLOBAL hmem;
4936 INT ret;
4937 UINT lcflags = lctype;
4938 const WCHAR *p;
4939 unsigned int i;
4941 if (len < 0 || (len && !buffer))
4943 SetLastError( ERROR_INVALID_PARAMETER );
4944 return 0;
4946 if (!(locale = get_locale_by_id( &lcid, 0 )))
4948 SetLastError( ERROR_INVALID_PARAMETER );
4949 return 0;
4951 ret = get_locale_info( locale, lcid, lctype, buffer, len );
4952 if (ret != -1) return ret;
4954 if (!len) buffer = NULL;
4956 lcid = ConvertDefaultLocale( lcid );
4957 lctype = LOWORD(lctype);
4959 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
4961 /* first check for overrides in the registry */
4963 if (!(lcflags & LOCALE_NOUSEROVERRIDE) && lcid == ConvertDefaultLocale( LOCALE_USER_DEFAULT ))
4965 const struct registry_value *value = get_locale_registry_value( lctype );
4967 if (value)
4969 if (lcflags & LOCALE_RETURN_NUMBER)
4971 WCHAR tmp[16];
4972 ret = get_registry_locale_info( value, tmp, ARRAY_SIZE( tmp ));
4973 if (ret > 0)
4975 WCHAR *end;
4976 UINT number = wcstol( tmp, &end, get_value_base_by_lctype( lctype ) );
4977 if (*end) /* invalid number */
4979 SetLastError( ERROR_INVALID_FLAGS );
4980 return 0;
4982 ret = sizeof(UINT) / sizeof(WCHAR);
4983 if (!len) return ret;
4984 if (ret > len)
4986 SetLastError( ERROR_INSUFFICIENT_BUFFER );
4987 return 0;
4989 memcpy( buffer, &number, sizeof(number) );
4992 else ret = get_registry_locale_info( value, buffer, len );
4994 if (ret != -1) return ret;
4998 /* now load it from kernel resources */
5000 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
5001 ULongToPtr((lctype >> 4) + 1), lcid )))
5003 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
5004 return 0;
5006 if (!(hmem = LoadResource( kernel32_handle, hrsrc ))) return 0;
5008 p = LockResource( hmem );
5009 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
5011 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT) / sizeof(WCHAR);
5012 else
5013 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
5015 if (!len) return ret;
5017 if (ret > len)
5019 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5020 return 0;
5023 if (lcflags & LOCALE_RETURN_NUMBER)
5025 UINT number;
5026 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
5027 if (!tmp) return 0;
5028 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
5029 tmp[*p] = 0;
5030 number = wcstol( tmp, &end, get_value_base_by_lctype( lctype ) );
5031 if (!*end)
5032 memcpy( buffer, &number, sizeof(number) );
5033 else /* invalid number */
5035 SetLastError( ERROR_INVALID_FLAGS );
5036 ret = 0;
5038 HeapFree( GetProcessHeap(), 0, tmp );
5040 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning number %d\n",
5041 lcid, lctype, buffer, len, number );
5043 else
5045 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
5046 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
5048 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning %d %s\n",
5049 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
5051 return ret;
5055 /******************************************************************************
5056 * GetLocaleInfoEx (kernelbase.@)
5058 INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *name, LCTYPE info, WCHAR *buffer, INT len )
5060 LCID lcid;
5061 int ret;
5062 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
5064 if (!locale)
5066 SetLastError( ERROR_INVALID_PARAMETER );
5067 return 0;
5069 ret = get_locale_info( locale, lcid, info, buffer, len );
5070 if (ret != -1) return ret;
5072 TRACE( "%s 0x%lx\n", debugstr_w(name), info );
5074 lcid = LocaleNameToLCID( name, 0 );
5075 if (!lcid) return 0;
5076 return GetLocaleInfoW( lcid, info, buffer, len );
5080 /******************************************************************************
5081 * GetNLSVersion (kernelbase.@)
5083 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info )
5085 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
5087 if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId ))
5089 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5090 return FALSE;
5092 if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES ))
5094 SetLastError( ERROR_INVALID_PARAMETER );
5095 return FALSE;
5097 return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info );
5101 /******************************************************************************
5102 * GetNLSVersionEx (kernelbase.@)
5104 BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale,
5105 NLSVERSIONINFOEX *info )
5107 LCID lcid = 0;
5109 if (func != COMPARE_STRING)
5111 SetLastError( ERROR_INVALID_FLAGS );
5112 return FALSE;
5114 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5115 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5117 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5118 return FALSE;
5121 if (!(lcid = LocaleNameToLCID( locale, 0 ))) return FALSE;
5123 info->dwNLSVersion = info->dwDefinedVersion = sort.version;
5124 if (info->dwNLSVersionInfoSize >= sizeof(*info))
5126 const struct sortguid *sortid = get_language_sort( locale );
5127 info->dwEffectiveId = lcid;
5128 info->guidCustomVersion = sortid ? sortid->id : default_sort_guid;
5130 return TRUE;
5134 /******************************************************************************
5135 * GetOEMCP (kernelbase.@)
5137 UINT WINAPI GetOEMCP(void)
5139 return nls_info.OemTableInfo.CodePage;
5143 /***********************************************************************
5144 * GetProcessPreferredUILanguages (kernelbase.@)
5146 BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPreferredUILanguages( DWORD flags, ULONG *count,
5147 WCHAR *buffer, ULONG *size )
5149 return set_ntstatus( RtlGetProcessPreferredUILanguages( flags, count, buffer, size ));
5153 /***********************************************************************
5154 * GetStringTypeA (kernelbase.@)
5156 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
5157 WORD *chartype )
5159 UINT cp;
5160 INT countW;
5161 LPWSTR srcW;
5162 BOOL ret = FALSE;
5164 if (count == -1) count = strlen(src) + 1;
5166 cp = get_lcid_codepage( locale, 0 );
5167 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
5168 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5170 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
5172 * NOTE: the target buffer has 1 word for each CHARACTER in the source
5173 * string, with multibyte characters there maybe be more bytes in count
5174 * than character space in the buffer!
5176 ret = GetStringTypeW(type, srcW, countW, chartype);
5177 HeapFree(GetProcessHeap(), 0, srcW);
5179 return ret;
5183 /***********************************************************************
5184 * GetStringTypeW (kernelbase.@)
5186 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
5188 if (!src)
5190 SetLastError( ERROR_INVALID_PARAMETER );
5191 return FALSE;
5193 if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3)
5195 SetLastError( ERROR_INVALID_PARAMETER );
5196 return FALSE;
5199 if (count == -1) count = lstrlenW(src) + 1;
5201 while (count--) *chartype++ = get_char_type( type, *src++ );
5203 return TRUE;
5207 /***********************************************************************
5208 * GetStringTypeExW (kernelbase.@)
5210 BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
5211 WORD *chartype )
5213 /* locale is ignored for Unicode */
5214 return GetStringTypeW( type, src, count, chartype );
5218 /***********************************************************************
5219 * GetSystemDefaultLCID (kernelbase.@)
5221 LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
5223 return system_lcid;
5227 /***********************************************************************
5228 * GetSystemDefaultLangID (kernelbase.@)
5230 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
5232 return LANGIDFROMLCID( GetSystemDefaultLCID() );
5236 /***********************************************************************
5237 * GetSystemDefaultLocaleName (kernelbase.@)
5239 INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT count )
5241 return get_locale_info( system_locale, system_lcid, LOCALE_SNAME, name, count );
5245 /***********************************************************************
5246 * GetSystemDefaultUILanguage (kernelbase.@)
5248 LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
5250 LANGID lang;
5251 NtQueryInstallUILanguage( &lang );
5252 return lang;
5256 /***********************************************************************
5257 * GetSystemPreferredUILanguages (kernelbase.@)
5259 BOOL WINAPI DECLSPEC_HOTPATCH GetSystemPreferredUILanguages( DWORD flags, ULONG *count,
5260 WCHAR *buffer, ULONG *size )
5262 return set_ntstatus( RtlGetSystemPreferredUILanguages( flags, 0, count, buffer, size ));
5266 /***********************************************************************
5267 * GetThreadPreferredUILanguages (kernelbase.@)
5269 BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPreferredUILanguages( DWORD flags, ULONG *count,
5270 WCHAR *buffer, ULONG *size )
5272 return set_ntstatus( RtlGetThreadPreferredUILanguages( flags, count, buffer, size ));
5276 /***********************************************************************
5277 * GetTimeZoneInformation (kernelbase.@)
5279 DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info )
5281 DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
5282 DWORD ret = GetDynamicTimeZoneInformation( &tzinfo );
5284 memcpy( info, &tzinfo, sizeof(*info) );
5285 return ret;
5289 /***********************************************************************
5290 * GetTimeZoneInformationForYear (kernelbase.@)
5292 BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year,
5293 DYNAMIC_TIME_ZONE_INFORMATION *dynamic,
5294 TIME_ZONE_INFORMATION *info )
5296 DYNAMIC_TIME_ZONE_INFORMATION local_info;
5297 HKEY key = 0, dst_key;
5298 DWORD count;
5299 LRESULT ret;
5300 struct
5302 LONG bias;
5303 LONG std_bias;
5304 LONG dlt_bias;
5305 SYSTEMTIME std_date;
5306 SYSTEMTIME dlt_date;
5307 } data;
5309 TRACE( "(%u,%p)\n", year, info );
5311 if (!dynamic)
5313 if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE;
5314 dynamic = &local_info;
5317 if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done;
5318 if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName,
5319 sizeof(info->StandardName), NULL, 0, system_dir ))
5321 count = sizeof(info->StandardName);
5322 if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count )))
5323 goto done;
5325 if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName,
5326 sizeof(info->DaylightName), NULL, 0, system_dir ))
5328 count = sizeof(info->DaylightName);
5329 if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count )))
5330 goto done;
5333 ret = ERROR_FILE_NOT_FOUND;
5334 if (!dynamic->DynamicDaylightTimeDisabled &&
5335 !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key ))
5337 WCHAR yearW[16];
5338 swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year );
5339 count = sizeof(data);
5340 ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count );
5341 RegCloseKey( dst_key );
5343 if (ret)
5345 count = sizeof(data);
5346 ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count );
5349 if (!ret)
5351 info->Bias = data.bias;
5352 info->StandardBias = data.std_bias;
5353 info->DaylightBias = data.dlt_bias;
5354 info->StandardDate = data.std_date;
5355 info->DaylightDate = data.dlt_date;
5358 done:
5359 RegCloseKey( key );
5360 if (ret) SetLastError( ret );
5361 return !ret;
5365 /***********************************************************************
5366 * GetUserDefaultLCID (kernelbase.@)
5368 LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
5370 return user_lcid;
5374 /***********************************************************************
5375 * GetUserDefaultLangID (kernelbase.@)
5377 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
5379 return LANGIDFROMLCID( GetUserDefaultLCID() );
5383 /***********************************************************************
5384 * GetUserDefaultLocaleName (kernelbase.@)
5386 INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
5388 return get_locale_info( user_locale, user_lcid, LOCALE_SNAME, name, len );
5392 /***********************************************************************
5393 * GetUserDefaultUILanguage (kernelbase.@)
5395 LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
5397 LANGID lang;
5398 NtQueryDefaultUILanguage( &lang );
5399 return lang;
5403 /******************************************************************************
5404 * GetUserGeoID (kernelbase.@)
5406 GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass )
5408 GEOID ret = 39070;
5409 const WCHAR *name;
5410 WCHAR bufferW[40];
5411 HKEY hkey;
5413 switch (geoclass)
5415 case GEOCLASS_NATION:
5416 name = L"Nation";
5417 break;
5418 case GEOCLASS_REGION:
5419 name = L"Region";
5420 break;
5421 default:
5422 WARN("Unknown geoclass %ld\n", geoclass);
5423 return GEOID_NOT_AVAILABLE;
5425 if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey ))
5427 DWORD count = sizeof(bufferW);
5428 if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count ))
5429 ret = wcstol( bufferW, NULL, 10 );
5430 RegCloseKey( hkey );
5432 return ret;
5436 /******************************************************************************
5437 * GetUserPreferredUILanguages (kernelbase.@)
5439 BOOL WINAPI DECLSPEC_HOTPATCH GetUserPreferredUILanguages( DWORD flags, ULONG *count,
5440 WCHAR *buffer, ULONG *size )
5442 return set_ntstatus( RtlGetUserPreferredUILanguages( flags, 0, count, buffer, size ));
5446 /******************************************************************************
5447 * IdnToAscii (kernelbase.@)
5449 INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen,
5450 WCHAR *dst, INT dstlen )
5452 NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
5453 if (!set_ntstatus( status )) return 0;
5454 return dstlen;
5458 /******************************************************************************
5459 * IdnToNameprepUnicode (kernelbase.@)
5461 INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
5462 WCHAR *dst, INT dstlen )
5464 NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen );
5465 if (!set_ntstatus( status )) return 0;
5466 return dstlen;
5470 /******************************************************************************
5471 * IdnToUnicode (kernelbase.@)
5473 INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen,
5474 WCHAR *dst, INT dstlen )
5476 NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen );
5477 if (!set_ntstatus( status )) return 0;
5478 return dstlen;
5482 /******************************************************************************
5483 * IsCharAlphaA (kernelbase.@)
5485 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
5487 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5488 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5492 /******************************************************************************
5493 * IsCharAlphaW (kernelbase.@)
5495 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
5497 return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA);
5501 /******************************************************************************
5502 * IsCharAlphaNumericA (kernelbase.@)
5504 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
5506 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5507 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5511 /******************************************************************************
5512 * IsCharAlphaNumericW (kernelbase.@)
5514 BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
5516 return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT));
5520 /******************************************************************************
5521 * IsCharBlankW (kernelbase.@)
5523 BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
5525 return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK);
5529 /******************************************************************************
5530 * IsCharCntrlW (kernelbase.@)
5532 BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
5534 return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL);
5538 /******************************************************************************
5539 * IsCharDigitW (kernelbase.@)
5541 BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
5543 return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT);
5547 /******************************************************************************
5548 * IsCharLowerA (kernelbase.@)
5550 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
5552 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5553 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5557 /******************************************************************************
5558 * IsCharLowerW (kernelbase.@)
5560 BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
5562 return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER);
5566 /******************************************************************************
5567 * IsCharPunctW (kernelbase.@)
5569 BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
5571 return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT);
5575 /******************************************************************************
5576 * IsCharSpaceA (kernelbase.@)
5578 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
5580 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5581 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5585 /******************************************************************************
5586 * IsCharSpaceW (kernelbase.@)
5588 BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
5590 return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE);
5594 /******************************************************************************
5595 * IsCharUpperA (kernelbase.@)
5597 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
5599 WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
5600 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5604 /******************************************************************************
5605 * IsCharUpperW (kernelbase.@)
5607 BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
5609 return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER);
5613 /******************************************************************************
5614 * IsCharXDigitW (kernelbase.@)
5616 BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
5618 return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT);
5622 /******************************************************************************
5623 * IsDBCSLeadByte (kernelbase.@)
5625 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
5627 return nls_info.AnsiTableInfo.DBCSCodePage && nls_info.AnsiTableInfo.DBCSOffsets[testchar];
5631 /******************************************************************************
5632 * IsDBCSLeadByteEx (kernelbase.@)
5634 BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
5636 const CPTABLEINFO *table = get_codepage_table( codepage );
5637 return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
5641 /******************************************************************************
5642 * IsNormalizedString (kernelbase.@)
5644 BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
5646 BOOLEAN res;
5647 if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
5648 return res;
5652 /******************************************************************************
5653 * IsValidCodePage (kernelbase.@)
5655 BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
5657 switch (codepage)
5659 case CP_ACP:
5660 case CP_OEMCP:
5661 case CP_MACCP:
5662 case CP_THREAD_ACP:
5663 return FALSE;
5664 case CP_UTF7:
5665 case CP_UTF8:
5666 return TRUE;
5667 default:
5668 return get_codepage_table( codepage ) != NULL;
5673 /******************************************************************************
5674 * IsValidLanguageGroup (kernelbase.@)
5676 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
5678 WCHAR name[10], value[10];
5679 DWORD type, value_len = sizeof(value);
5680 BOOL ret = FALSE;
5681 HKEY key;
5683 if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
5685 swprintf( name, ARRAY_SIZE(name), L"%x", id );
5686 if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
5687 ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
5689 RegCloseKey( key );
5690 return ret;
5694 /******************************************************************************
5695 * IsValidLocale (kernelbase.@)
5697 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
5699 return !!get_locale_by_id( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES );
5703 /******************************************************************************
5704 * IsValidLocaleName (kernelbase.@)
5706 BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
5708 if (locale == LOCALE_NAME_USER_DEFAULT) return FALSE;
5709 return !!find_lcname_entry( locale );
5713 /******************************************************************************
5714 * IsValidNLSVersion (kernelbase.@)
5716 DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale,
5717 NLSVERSIONINFOEX *info )
5719 static const GUID GUID_NULL;
5720 NLSVERSIONINFOEX infoex;
5721 DWORD ret;
5723 if (func != COMPARE_STRING)
5725 SetLastError( ERROR_INVALID_PARAMETER );
5726 return FALSE;
5728 if (info->dwNLSVersionInfoSize < sizeof(*info) &&
5729 (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId )))
5731 SetLastError( ERROR_INVALID_PARAMETER );
5732 return FALSE;
5734 infoex.dwNLSVersionInfoSize = sizeof(infoex);
5735 if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE;
5737 ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff);
5738 if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL ))
5739 ret = find_sortguid( &info->guidCustomVersion ) != NULL;
5741 if (!ret) SetLastError( ERROR_SUCCESS );
5742 return ret;
5746 /***********************************************************************
5747 * LCIDToLocaleName (kernelbase.@)
5749 INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
5751 const NLS_LOCALE_DATA *locale = get_locale_by_id( &lcid, flags );
5753 if (!locale)
5755 SetLastError( ERROR_INVALID_PARAMETER );
5756 return 0;
5758 return get_locale_info( locale, lcid, LOCALE_SNAME, name, count );
5762 /***********************************************************************
5763 * LCMapStringEx (kernelbase.@)
5765 INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
5766 WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
5767 void *reserved, LPARAM handle )
5769 const struct sortguid *sortid = NULL;
5770 LPWSTR dst_ptr;
5771 INT len;
5773 if (version) FIXME( "unsupported version structure %p\n", version );
5774 if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
5775 if (handle)
5777 static int once;
5778 if (!once++) FIXME( "unsupported lparam %Ix\n", handle );
5781 if (!src || !srclen || dstlen < 0)
5783 SetLastError( ERROR_INVALID_PARAMETER );
5784 return 0;
5787 /* mutually exclusive flags */
5788 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
5789 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
5790 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
5791 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
5792 !flags)
5794 SetLastError( ERROR_INVALID_FLAGS );
5795 return 0;
5798 if (!dstlen) dst = NULL;
5800 if (flags & LCMAP_LINGUISTIC_CASING && !(sortid = get_language_sort( locale )))
5802 FIXME( "unknown locale %s\n", debugstr_w(locale) );
5803 SetLastError( ERROR_INVALID_PARAMETER );
5804 return 0;
5807 if (flags & LCMAP_SORTKEY)
5809 INT ret;
5811 if (src == dst)
5813 SetLastError( ERROR_INVALID_FLAGS );
5814 return 0;
5816 if (srclen < 0) srclen = lstrlenW(src);
5818 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5819 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5821 if ((ret = get_sortkey( flags, src, srclen, (char *)dst, dstlen ))) ret++;
5822 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
5823 return ret;
5826 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
5827 if (flags & SORT_STRINGSORT)
5829 SetLastError( ERROR_INVALID_FLAGS );
5830 return 0;
5832 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
5833 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
5834 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
5835 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
5837 SetLastError( ERROR_INVALID_FLAGS );
5838 return 0;
5841 if (srclen < 0) srclen = lstrlenW(src) + 1;
5843 TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
5844 debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
5846 if (!dst) /* return required string length */
5848 if (flags & NORM_IGNORESYMBOLS)
5850 for (len = 0; srclen; src++, srclen--)
5851 if (!(get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE))) len++;
5853 else if (flags & LCMAP_FULLWIDTH)
5855 for (len = 0; srclen; src++, srclen--, len++)
5857 if (compose_katakana( src, srclen, NULL ) == 2)
5859 src++;
5860 srclen--;
5864 else if (flags & LCMAP_HALFWIDTH)
5866 for (len = 0; srclen; src++, srclen--, len++)
5868 WCHAR wch = *src;
5869 /* map Hiragana to Katakana before decomposition if needed */
5870 if ((flags & LCMAP_KATAKANA) &&
5871 ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E))
5872 wch += 0x60;
5874 if (decompose_katakana( wch, NULL, 0 ) == 2) len++;
5877 else len = srclen;
5878 return len;
5881 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
5883 SetLastError( ERROR_INVALID_FLAGS );
5884 return 0;
5887 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
5889 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
5891 if ((flags & NORM_IGNORESYMBOLS) && (get_char_type( CT_CTYPE1, *src ) & (C1_PUNCT | C1_SPACE)))
5892 continue;
5893 *dst_ptr++ = *src;
5894 len--;
5896 goto done;
5899 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
5901 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
5903 WCHAR wch;
5904 if (flags & LCMAP_FULLWIDTH)
5906 /* map half-width character to full-width one,
5907 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
5908 if (map_to_fullwidth( src, srclen, &wch ) == 2)
5910 src++;
5911 srclen--;
5914 else wch = *src;
5916 if (flags & LCMAP_KATAKANA)
5918 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
5919 we can't use C3_HIRAGANA as some characters can't map to katakana */
5920 if ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E) wch += 0x60;
5922 else if (flags & LCMAP_HIRAGANA)
5924 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
5925 we can't use C3_KATAKANA as some characters can't map to hiragana */
5926 if ((wch >= 0x30A1 && wch <= 0x30F6) || wch == 0x30FD || wch == 0x30FE) wch -= 0x60;
5929 if (flags & LCMAP_HALFWIDTH)
5931 /* map full-width character to half-width one,
5932 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
5933 if (map_to_halfwidth(wch, dst_ptr, len) == 2)
5935 len--;
5936 dst_ptr++;
5937 if (!len) break;
5940 else *dst_ptr = wch;
5942 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen) goto done;
5944 srclen = dst_ptr - dst;
5945 src = dst;
5948 if (flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
5950 const USHORT *table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
5951 table = table + 2 + (flags & LCMAP_LOWERCASE ? table[1] : 0);
5952 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--, len--)
5953 *dst_ptr++ = casemap( table, *src );
5955 else
5957 len = min( srclen, dstlen );
5958 memcpy( dst, src, len * sizeof(WCHAR) );
5959 dst_ptr = dst + len;
5960 srclen -= len;
5963 done:
5964 if (srclen)
5966 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5967 return 0;
5970 return dst_ptr - dst;
5974 /***********************************************************************
5975 * LCMapStringA (kernelbase.@)
5977 INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
5978 char *dst, int dstlen )
5980 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
5981 LPWSTR srcW, dstW;
5982 INT ret = 0, srclenW, dstlenW;
5983 UINT locale_cp = CP_ACP;
5985 if (!src || !srclen || dstlen < 0)
5987 SetLastError( ERROR_INVALID_PARAMETER );
5988 return 0;
5991 locale_cp = get_lcid_codepage( lcid, flags );
5993 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
5994 if (srclenW) srcW = bufW;
5995 else
5997 srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
5998 srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
5999 if (!srcW)
6001 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6002 return 0;
6004 MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
6007 if (flags & LCMAP_SORTKEY)
6009 if (src == dst)
6011 SetLastError( ERROR_INVALID_FLAGS );
6012 goto done;
6014 ret = LCMapStringEx( NULL, flags, srcW, srclenW, (WCHAR *)dst, dstlen, NULL, NULL, 0 );
6015 goto done;
6018 if (flags & SORT_STRINGSORT)
6020 SetLastError( ERROR_INVALID_FLAGS );
6021 goto done;
6024 dstlenW = LCMapStringEx( NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0 );
6025 if (!dstlenW) goto done;
6027 dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
6028 if (!dstW)
6030 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
6031 goto done;
6033 LCMapStringEx( NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0 );
6034 ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
6035 HeapFree( GetProcessHeap(), 0, dstW );
6037 done:
6038 if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
6039 return ret;
6043 /***********************************************************************
6044 * LCMapStringW (kernelbase.@)
6046 INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
6047 WCHAR *dst, int dstlen )
6049 return LCMapStringEx( NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
6053 /***********************************************************************
6054 * LocaleNameToLCID (kernelbase.@)
6056 LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
6058 LCID lcid;
6059 const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid );
6061 if (!locale)
6063 SetLastError( ERROR_INVALID_PARAMETER );
6064 return 0;
6066 if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral)
6067 lcid = locale->idefaultlanguage;
6068 return lcid;
6072 /******************************************************************************
6073 * MultiByteToWideChar (kernelbase.@)
6075 INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
6076 WCHAR *dst, INT dstlen )
6078 int ret;
6080 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6082 SetLastError( ERROR_INVALID_PARAMETER );
6083 return 0;
6085 if (srclen < 0) srclen = strlen(src) + 1;
6087 switch (codepage)
6089 case CP_SYMBOL:
6090 ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
6091 break;
6092 case CP_UTF7:
6093 ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
6094 break;
6095 case CP_UTF8:
6096 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6097 break;
6098 case CP_UNIXCP:
6099 if (unix_cp == CP_UTF8)
6101 ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
6102 break;
6104 codepage = unix_cp;
6105 /* fall through */
6106 default:
6107 ret = mbstowcs_codepage( codepage, flags, src, srclen, dst, dstlen );
6108 break;
6110 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
6111 return ret;
6115 /******************************************************************************
6116 * NormalizeString (kernelbase.@)
6118 INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
6119 WCHAR *dst, INT dst_len)
6121 NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len );
6123 switch (status)
6125 case STATUS_OBJECT_NAME_NOT_FOUND:
6126 status = STATUS_INVALID_PARAMETER;
6127 break;
6128 case STATUS_BUFFER_TOO_SMALL:
6129 case STATUS_NO_UNICODE_TRANSLATION:
6130 dst_len = -dst_len;
6131 break;
6133 SetLastError( RtlNtStatusToDosError( status ));
6134 return dst_len;
6138 /******************************************************************************
6139 * ResolveLocaleName (kernelbase.@)
6141 INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
6143 FIXME( "stub: %s, %p, %d\n", wine_dbgstr_w(name), buffer, len );
6145 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
6146 return 0;
6150 /******************************************************************************
6151 * SetLocaleInfoW (kernelbase.@)
6153 BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
6155 const struct registry_value *value;
6156 DWORD index;
6157 LSTATUS status;
6159 lctype = LOWORD(lctype);
6160 value = get_locale_registry_value( lctype );
6162 if (!data || !value)
6164 SetLastError( ERROR_INVALID_PARAMETER );
6165 return FALSE;
6168 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
6170 SetLastError( ERROR_INVALID_FLAGS );
6171 return FALSE;
6174 TRACE( "setting %lx (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
6176 /* FIXME: should check that data to set is sane */
6178 status = RegSetValueExW( intl_key, value->name, 0, REG_SZ, (BYTE *)data, (lstrlenW(data)+1)*sizeof(WCHAR) );
6179 index = value - registry_values;
6181 RtlEnterCriticalSection( &locale_section );
6182 HeapFree( GetProcessHeap(), 0, registry_cache[index] );
6183 registry_cache[index] = NULL;
6184 RtlLeaveCriticalSection( &locale_section );
6186 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
6188 /* Set I-value from S value */
6189 WCHAR *pD, *pM, *pY, buf[2];
6191 pD = wcschr( data, 'd' );
6192 pM = wcschr( data, 'M' );
6193 pY = wcschr( data, 'y' );
6195 if (pD <= pM) buf[0] = '1'; /* D-M-Y */
6196 else if (pY <= pM) buf[0] = '2'; /* Y-M-D */
6197 else buf[0] = '0'; /* M-D-Y */
6198 buf[1] = 0;
6200 lctype = (lctype == LOCALE_SSHORTDATE) ? LOCALE_IDATE : LOCALE_ILDATE;
6201 value = get_locale_registry_value( lctype );
6202 index = value - registry_values;
6204 RegSetValueExW( intl_key, value->name, 0, REG_SZ, (BYTE *)buf, sizeof(buf) );
6206 RtlEnterCriticalSection( &locale_section );
6207 HeapFree( GetProcessHeap(), 0, registry_cache[index] );
6208 registry_cache[index] = NULL;
6209 RtlLeaveCriticalSection( &locale_section );
6211 return set_ntstatus( status );
6215 /***********************************************************************
6216 * SetCalendarInfoW (kernelbase.@)
6218 INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
6220 FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
6221 return 0;
6225 /***********************************************************************
6226 * SetProcessPreferredUILanguages (kernelbase.@)
6228 BOOL WINAPI DECLSPEC_HOTPATCH SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6230 return set_ntstatus( RtlSetProcessPreferredUILanguages( flags, buffer, count ));
6234 /***********************************************************************
6235 * SetThreadPreferredUILanguages (kernelbase.@)
6237 BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
6239 return set_ntstatus( RtlSetThreadPreferredUILanguages( flags, buffer, count ));
6243 /***********************************************************************
6244 * SetTimeZoneInformation (kernelbase.@)
6246 BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info )
6248 return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info ));
6252 /******************************************************************************
6253 * SetUserGeoID (kernelbase.@)
6255 BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id )
6257 const struct geoinfo *geoinfo = get_geoinfo_ptr( id );
6258 WCHAR bufferW[10];
6259 HKEY hkey;
6261 if (!geoinfo)
6263 SetLastError( ERROR_INVALID_PARAMETER );
6264 return FALSE;
6266 if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
6268 const WCHAR *name = geoinfo->kind == LOCATION_NATION ? L"Nation" : L"Region";
6269 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geoinfo->id );
6270 RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6272 if (geoinfo->kind == LOCATION_NATION || geoinfo->kind == LOCATION_BOTH)
6273 lstrcpyW( bufferW, geoinfo->iso2W );
6274 else
6275 swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", geoinfo->uncode );
6276 RegSetValueExW( hkey, L"Name", 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
6277 RegCloseKey( hkey );
6279 return TRUE;
6283 /***********************************************************************
6284 * SystemTimeToTzSpecificLocalTime (kernelbase.@)
6286 BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info,
6287 const SYSTEMTIME *system,
6288 SYSTEMTIME *local )
6290 TIME_ZONE_INFORMATION tzinfo;
6291 LARGE_INTEGER ft;
6293 if (!info)
6295 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6296 info = &tzinfo;
6299 if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE;
6300 switch (get_timezone_id( info, ft, FALSE ))
6302 case TIME_ZONE_ID_UNKNOWN:
6303 ft.QuadPart -= info->Bias * (LONGLONG)600000000;
6304 break;
6305 case TIME_ZONE_ID_STANDARD:
6306 ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6307 break;
6308 case TIME_ZONE_ID_DAYLIGHT:
6309 ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6310 break;
6311 default:
6312 return FALSE;
6314 return FileTimeToSystemTime( (FILETIME *)&ft, local );
6318 /***********************************************************************
6319 * TzSpecificLocalTimeToSystemTime (kernelbase.@)
6321 BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info,
6322 const SYSTEMTIME *local,
6323 SYSTEMTIME *system )
6325 TIME_ZONE_INFORMATION tzinfo;
6326 LARGE_INTEGER ft;
6328 if (!info)
6330 RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo );
6331 info = &tzinfo;
6334 if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE;
6335 switch (get_timezone_id( info, ft, TRUE ))
6337 case TIME_ZONE_ID_UNKNOWN:
6338 ft.QuadPart += info->Bias * (LONGLONG)600000000;
6339 break;
6340 case TIME_ZONE_ID_STANDARD:
6341 ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000;
6342 break;
6343 case TIME_ZONE_ID_DAYLIGHT:
6344 ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000;
6345 break;
6346 default:
6347 return FALSE;
6349 return FileTimeToSystemTime( (FILETIME *)&ft, system );
6353 /***********************************************************************
6354 * VerLanguageNameA (kernelbase.@)
6356 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
6358 return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6362 /***********************************************************************
6363 * VerLanguageNameW (kernelbase.@)
6365 DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
6367 return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
6371 /***********************************************************************
6372 * WideCharToMultiByte (kernelbase.@)
6374 INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
6375 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
6377 int ret;
6379 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
6381 SetLastError( ERROR_INVALID_PARAMETER );
6382 return 0;
6385 if (srclen < 0) srclen = lstrlenW(src) + 1;
6387 switch (codepage)
6389 case CP_SYMBOL:
6390 ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
6391 break;
6392 case CP_UTF7:
6393 ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
6394 break;
6395 case CP_UTF8:
6396 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
6397 break;
6398 case CP_UNIXCP:
6399 if (unix_cp == CP_UTF8)
6401 if (used) *used = FALSE;
6402 ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, NULL, NULL );
6403 break;
6405 codepage = unix_cp;
6406 /* fall through */
6407 default:
6408 ret = wcstombs_codepage( codepage, flags, src, srclen, dst, dstlen, defchar, used );
6409 break;
6411 TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
6412 return ret;
6416 /***********************************************************************
6417 * GetUserDefaultGeoName (kernelbase.@)
6419 INT WINAPI GetUserDefaultGeoName(LPWSTR geo_name, int count)
6421 const struct geoinfo *geoinfo;
6422 WCHAR buffer[32];
6423 LSTATUS status;
6424 DWORD size;
6425 HKEY key;
6427 TRACE( "geo_name %p, count %d.\n", geo_name, count );
6429 if (count && !geo_name)
6431 SetLastError( ERROR_INVALID_PARAMETER );
6432 return 0;
6434 if (!(status = RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &key )))
6436 size = sizeof(buffer);
6437 status = RegQueryValueExW( key, L"Name", NULL, NULL, (BYTE *)buffer, &size );
6438 RegCloseKey( key );
6440 if (status)
6442 if ((geoinfo = get_geoinfo_ptr( GetUserGeoID( GEOCLASS_NATION ))) && geoinfo->id != 39070)
6443 lstrcpyW( buffer, geoinfo->iso2W );
6444 else
6445 lstrcpyW( buffer, L"001" );
6447 size = lstrlenW( buffer ) + 1;
6448 if (count < size)
6450 if (!count)
6451 return size;
6452 SetLastError( ERROR_INSUFFICIENT_BUFFER );
6453 return 0;
6455 lstrcpyW( geo_name, buffer );
6456 return size;
6460 /***********************************************************************
6461 * SetUserDefaultGeoName (kernelbase.@)
6463 BOOL WINAPI SetUserGeoName(PWSTR geo_name)
6465 unsigned int i;
6466 WCHAR *endptr;
6467 int uncode;
6469 TRACE( "geo_name %s.\n", debugstr_w( geo_name ));
6471 if (!geo_name)
6473 SetLastError( ERROR_INVALID_PARAMETER );
6474 return FALSE;
6477 if (lstrlenW( geo_name ) == 3)
6479 uncode = wcstol( geo_name, &endptr, 10 );
6480 if (!uncode || endptr != geo_name + 3)
6482 SetLastError( ERROR_INVALID_PARAMETER );
6483 return FALSE;
6485 for (i = 0; i < ARRAY_SIZE(geoinfodata); ++i)
6486 if (geoinfodata[i].uncode == uncode)
6487 break;
6489 else
6491 if (!lstrcmpiW( geo_name, L"XX" ))
6492 return SetUserGeoID( 39070 );
6493 for (i = 0; i < ARRAY_SIZE(geoinfodata); ++i)
6494 if (!lstrcmpiW( geo_name, geoinfodata[i].iso2W ))
6495 break;
6497 if (i == ARRAY_SIZE(geoinfodata))
6499 SetLastError( ERROR_INVALID_PARAMETER );
6500 return FALSE;
6502 return SetUserGeoID( geoinfodata[i].id );