82e5b35b66b55981cb1ea6dbd716fb3e133d9897
[reactos.git] / reactos / dll / win32 / kernel32 / misc / lang.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: dll/win32/kernel32/file/lang.c
6 * PURPOSE: National Laguage Support related funcs
7 * PROGRAMMER: Thomas Weidenmueller
8 * Gunnar Andre Dalsnes
9 * Aleksey Bragin
10 * Eric Kohl
11 * Alex Ionescu
12 * Richard Campbell
13 * James Tabor
14 * UPDATE HISTORY:
15 * Created 21/09/2003
16 */
17
18 #include <k32.h>
19
20 #define NDEBUG
21 #include <debug.h>
22
23 #include "lcformat_private.h"
24
25 /* FIXME: these are included in winnls.h, however including this file causes alot of
26 conflicting type errors. */
27
28 #define LOCALE_RETURN_NUMBER 0x20000000
29 #define LOCALE_USE_CP_ACP 0x40000000
30 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|LOCALE_RETURN_NUMBER)
31 #define CALINFO_MAX_YEAR 2029
32
33 //static LCID SystemLocale = MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
34
35 //static RTL_CRITICAL_SECTION LocalesListLock;
36
37 typedef struct
38 {
39 union
40 {
41 UILANGUAGE_ENUMPROCA procA;
42 UILANGUAGE_ENUMPROCW procW;
43 } u;
44 DWORD flags;
45 LONG_PTR param;
46 } ENUM_UILANG_CALLBACK;
47
48 static const WCHAR szLocaleKeyName[] =
49
50 {
51 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
52 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
53 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
54 };
55
56 /******************************************************************************
57 * @implemented
58 * RIPPED FROM WINE's dlls\kernel\locale.c rev 1.42
59 *
60 * ConvertDefaultLocale (KERNEL32.@)
61 *
62 * Convert a default locale identifier into a real identifier.
63 *
64 * PARAMS
65 * lcid [I] LCID identifier of the locale to convert
66 *
67 * RETURNS
68 * lcid unchanged, if not a default locale or its sublanguage is
69 * not SUBLANG_NEUTRAL.
70 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
71 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
72 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
73 */
74 LCID WINAPI
75 ConvertDefaultLocale(LCID lcid)
76 {
77 LANGID langid;
78
79 switch (lcid)
80 {
81 case LOCALE_SYSTEM_DEFAULT:
82 lcid = GetSystemDefaultLCID();
83 break;
84
85 case LOCALE_USER_DEFAULT:
86 case LOCALE_NEUTRAL:
87 lcid = GetUserDefaultLCID();
88 break;
89
90 default:
91 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
92 langid = LANGIDFROMLCID(lcid);
93 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
94 {
95 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
96 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
97 }
98 }
99
100 return lcid;
101 }
102
103
104 /**************************************************************************
105 * EnumDateFormatsExA (KERNEL32.@)
106 *
107 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
108 * LOCALE_NOUSEROVERRIDE here as well?
109 */
110 BOOL
111 STDCALL
112 EnumDateFormatsExA(
113 DATEFMT_ENUMPROCEXA lpDateFmtEnumProcEx,
114 LCID Locale,
115 DWORD dwFlags)
116 {
117 CALID cal_id;
118 char szBuf[256];
119
120 if (!lpDateFmtEnumProcEx)
121 {
122 SetLastError(ERROR_INVALID_PARAMETER);
123 return FALSE;
124 }
125
126 if (!GetLocaleInfoW(Locale,
127 LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER,
128 (LPWSTR)&cal_id,
129 sizeof(cal_id)/sizeof(WCHAR)))
130 {
131 return FALSE;
132 }
133
134 switch (dwFlags & ~LOCALE_USE_CP_ACP)
135 {
136 case 0:
137 case DATE_SHORTDATE:
138 if (GetLocaleInfoA(Locale,
139 LOCALE_SSHORTDATE | (dwFlags & LOCALE_USE_CP_ACP),
140 szBuf, 256))
141 {
142 lpDateFmtEnumProcEx(szBuf, cal_id);
143 }
144 break;
145
146 case DATE_LONGDATE:
147 if (GetLocaleInfoA(Locale,
148 LOCALE_SLONGDATE | (dwFlags & LOCALE_USE_CP_ACP),
149 szBuf, 256))
150 {
151 lpDateFmtEnumProcEx(szBuf, cal_id);
152 }
153 break;
154
155 case DATE_YEARMONTH:
156 if (GetLocaleInfoA(Locale,
157 LOCALE_SYEARMONTH | (dwFlags & LOCALE_USE_CP_ACP),
158 szBuf, 256))
159 {
160 lpDateFmtEnumProcEx(szBuf, cal_id);
161 }
162 break;
163
164 default:
165 // FIXME: Unknown date format
166 SetLastError(ERROR_INVALID_PARAMETER);
167 return FALSE;
168 }
169 return TRUE;
170 }
171
172
173 /**************************************************************************
174 * EnumDateFormatsExW (KERNEL32.@)
175 */
176 BOOL
177 STDCALL
178 EnumDateFormatsExW(
179 DATEFMT_ENUMPROCEXW lpDateFmtEnumProcEx,
180 LCID Locale,
181 DWORD dwFlags)
182 {
183 CALID cal_id;
184 WCHAR wbuf[256]; // FIXME
185
186 if (!lpDateFmtEnumProcEx)
187 {
188 SetLastError(ERROR_INVALID_PARAMETER);
189 return FALSE;
190 }
191
192 if (!GetLocaleInfoW(Locale,
193 LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
194 (LPWSTR)&cal_id,
195 sizeof(cal_id)/sizeof(WCHAR)))
196 {
197 return FALSE;
198 }
199
200 switch (dwFlags & ~LOCALE_USE_CP_ACP)
201 {
202 case 0:
203 case DATE_SHORTDATE:
204 if (GetLocaleInfoW(Locale,
205 LOCALE_SSHORTDATE | (dwFlags & LOCALE_USE_CP_ACP),
206 wbuf,
207 256))
208 {
209 lpDateFmtEnumProcEx(wbuf, cal_id);
210 }
211 break;
212
213 case DATE_LONGDATE:
214 if (GetLocaleInfoW(Locale,
215 LOCALE_SLONGDATE | (dwFlags & LOCALE_USE_CP_ACP),
216 wbuf,
217 256))
218 {
219 lpDateFmtEnumProcEx(wbuf, cal_id);
220 }
221 break;
222
223 case DATE_YEARMONTH:
224 if (GetLocaleInfoW(Locale,
225 LOCALE_SYEARMONTH | (dwFlags & LOCALE_USE_CP_ACP),
226 wbuf,
227 256))
228 {
229 lpDateFmtEnumProcEx(wbuf, cal_id);
230 }
231 break;
232
233 default:
234 // FIXME: Unknown date format
235 SetLastError(ERROR_INVALID_PARAMETER);
236 return FALSE;
237 }
238 return TRUE;
239 }
240
241
242 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
243 LPWSTR szValueName, ULONG valueNameSize,
244 LPWSTR szValueData, ULONG valueDataSize)
245 {
246 BYTE buffer[80];
247 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
248 DWORD dwLen;
249
250 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
251 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
252 info->NameLength > valueNameSize ||
253 info->DataLength > valueDataSize)
254 {
255 return FALSE;
256 }
257
258 DPRINT("info->Name %s info->DataLength %d\n", info->Name, info->DataLength);
259
260 memcpy( szValueName, info->Name, info->NameLength);
261 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
262 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
263 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
264
265 DPRINT("returning %s %s\n", szValueName, szValueData);
266 return TRUE;
267 }
268
269
270 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
271 {
272 UNICODE_STRING keyName;
273 OBJECT_ATTRIBUTES attr;
274 HANDLE hkey;
275
276 RtlInitUnicodeString( &keyName, szKeyName );
277 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
278
279 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS)
280 hkey = 0;
281
282 return hkey;
283 }
284
285 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
286 ULONG keyNameSize)
287 {
288 BYTE buffer[80];
289 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
290 DWORD dwLen;
291
292 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
293 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
294 info->NameLength > keyNameSize)
295 {
296 return FALSE;
297 }
298
299 DPRINT("info->Name %s info->NameLength %d\n", info->Name, info->NameLength);
300
301 memcpy( szKeyName, info->Name, info->NameLength);
302 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
303
304 DPRINT("returning %s\n", szKeyName);
305 return TRUE;
306 }
307
308 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
309 {
310 BYTE buffer[128];
311 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
312 DWORD dwSize = sizeof(buffer);
313 UNICODE_STRING valueName;
314
315 RtlInitUnicodeString( &valueName, szValueName );
316
317 DPRINT("%p, %s\n", hKey, szValueName);
318 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
319 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
320 info->DataLength == sizeof(DWORD))
321 {
322 memcpy(lpVal, info->Data, sizeof(DWORD));
323 return TRUE;
324 }
325
326 return FALSE;
327 }
328
329
330 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
331 typedef struct
332 {
333 LANGGROUPLOCALE_ENUMPROCA procA;
334 LANGGROUPLOCALE_ENUMPROCW procW;
335 DWORD dwFlags;
336 LGRPID lgrpid;
337 LONG_PTR lParam;
338 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
339
340 /* Internal implementation of EnumLanguageGrouplocalesA/W */
341 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
342 {
343 static const WCHAR szAlternateSortsKeyName[] = {
344 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
345 };
346 WCHAR szNumber[10], szValue[4];
347 HANDLE hKey;
348 BOOL bContinue = TRUE, bAlternate = FALSE;
349 LGRPID lgrpid;
350 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
351
352 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
353 {
354 SetLastError(ERROR_INVALID_PARAMETER);
355 return FALSE;
356 }
357
358 if (lpProcs->dwFlags)
359 {
360 SetLastError(ERROR_INVALID_FLAGS);
361 return FALSE;
362 }
363
364 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
365
366 if (!hKey)
367 DPRINT("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
368
369 while (bContinue)
370 {
371 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
372 szValue, sizeof(szValue) ))
373 {
374 lgrpid = wcstoul( szValue, NULL, 16 );
375
376 DPRINT("lcid %s, grpid %d (%smatched)\n", szNumber,
377 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
378
379 if (lgrpid == lpProcs->lgrpid)
380 {
381 LCID lcid;
382
383 lcid = wcstoul( szNumber, NULL, 16 );
384
385 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
386 * '00000437 ;Georgian'
387 * At present we only pass the LCID string.
388 */
389
390 if (lpProcs->procW)
391 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
392 else
393 {
394 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
395
396 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
397
398 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
399 }
400 }
401
402 ulIndex++;
403 }
404 else
405 {
406 /* Finished enumerating this key */
407 if (!bAlternate)
408 {
409 /* Enumerate alternate sorts also */
410 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
411 bAlternate = TRUE;
412 ulIndex = 0;
413 }
414 else
415 bContinue = FALSE; /* Finished both keys */
416 }
417
418 if (!bContinue)
419 break;
420 }
421
422 if (hKey)
423 NtClose( hKey );
424
425 return TRUE;
426 }
427
428
429 /*
430 * @implemented
431 */
432 BOOL
433 STDCALL
434 EnumLanguageGroupLocalesA(
435 LANGGROUPLOCALE_ENUMPROCA lpLangGroupLocaleEnumProc,
436 LGRPID LanguageGroup,
437 DWORD dwFlags,
438 LONG_PTR lParam)
439 {
440 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
441
442 DPRINT("(%p,0x%08X,0x%08X,0x%08lX)\n", lpLangGroupLocaleEnumProc, LanguageGroup, dwFlags, lParam);
443
444 callbacks.procA = lpLangGroupLocaleEnumProc;
445 callbacks.procW = NULL;
446 callbacks.dwFlags = dwFlags;
447 callbacks.lgrpid = LanguageGroup;
448 callbacks.lParam = lParam;
449
450 return NLS_EnumLanguageGroupLocales( lpLangGroupLocaleEnumProc ? &callbacks : NULL );
451 }
452
453
454 /*
455 * @implemented
456 */
457 BOOL
458 STDCALL
459 EnumLanguageGroupLocalesW(
460 LANGGROUPLOCALE_ENUMPROCW lpLangGroupLocaleEnumProc,
461 LGRPID LanguageGroup,
462 DWORD dwFlags,
463 LONG_PTR lParam)
464 {
465 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
466
467 DPRINT("(%p,0x%08X,0x%08X,0x%08lX)\n", lpLangGroupLocaleEnumProc, LanguageGroup, dwFlags, lParam);
468
469 callbacks.procA = NULL;
470 callbacks.procW = lpLangGroupLocaleEnumProc;
471 callbacks.dwFlags = dwFlags;
472 callbacks.lgrpid = LanguageGroup;
473 callbacks.lParam = lParam;
474
475 return NLS_EnumLanguageGroupLocales( lpLangGroupLocaleEnumProc ? &callbacks : NULL );
476 }
477
478
479 /*
480 * @implemented
481 */
482 BOOL
483 STDCALL
484 EnumSystemCodePagesW (
485 CODEPAGE_ENUMPROCW lpCodePageEnumProc,
486 DWORD dwFlags
487 )
488 {
489 WCHAR szNumber[5 + 1], szValue[MAX_PATH];
490 HKEY hKey;
491 BOOL bContinue = TRUE;
492 ULONG ulIndex = 1;
493
494 DPRINT("(%p,0x%08X)\n", lpCodePageEnumProc, dwFlags);
495
496 if ((!dwFlags & CP_INSTALLED)&&(!dwFlags & CP_SUPPORTED))
497 {
498 SetLastError(ERROR_INVALID_FLAGS);
499 return FALSE;
500 }
501
502 if (!lpCodePageEnumProc)
503 {
504 SetLastError(ERROR_INVALID_PARAMETER);
505 return FALSE;
506 }
507
508 hKey = NLS_RegOpenKey(0, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\NLS\\CodePage");
509 if (!hKey)
510 {
511 DPRINT1("NLS_RegOpenKey() failed\n");
512 return FALSE;
513 }
514
515 while (bContinue)
516 {
517 if (NLS_RegEnumValue(hKey, ulIndex, szNumber, sizeof(szNumber),
518 szValue, sizeof(szValue)))
519 {
520 if ((dwFlags & CP_SUPPORTED)||
521 ((dwFlags & CP_INSTALLED)&&(wcslen(szValue) > 2)))
522 if (!lpCodePageEnumProc(szNumber))
523 break;
524
525 ulIndex++;
526
527 } else bContinue = FALSE;
528
529 if (!bContinue)
530 break;
531 }
532
533 if (hKey)
534 NtClose(hKey);
535
536 return TRUE;
537 }
538
539
540 /*
541 * @implemented
542 */
543 BOOL
544 STDCALL
545 EnumSystemCodePagesA (
546 CODEPAGE_ENUMPROCA lpCodePageEnumProc,
547 DWORD dwFlags
548 )
549 {
550 WCHAR szNumber[5 + 1], szValue[MAX_PATH];
551 HKEY hKey;
552 BOOL bContinue = TRUE;
553 ULONG ulIndex = 1;
554
555 DPRINT("(%p,0x%08X)\n", lpCodePageEnumProc, dwFlags);
556
557 if ((!dwFlags & CP_INSTALLED)&&(!dwFlags & CP_SUPPORTED))
558 {
559 SetLastError(ERROR_INVALID_FLAGS);
560 return FALSE;
561 }
562
563 if (!lpCodePageEnumProc)
564 {
565 SetLastError(ERROR_INVALID_PARAMETER);
566 return FALSE;
567 }
568
569 hKey = NLS_RegOpenKey(0, L"\\Machine\\SYSTEM\\CurrentControlSet\\Control\\NLS\\CodePage");
570 if (!hKey)
571 {
572 DPRINT1("NLS_RegOpenKey() failed\n");
573 return FALSE;
574 }
575
576 while (bContinue)
577 {
578 if (NLS_RegEnumValue(hKey, ulIndex, szNumber, sizeof(szNumber),
579 szValue, sizeof(szValue)))
580 {
581 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
582
583 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
584
585 if ((dwFlags & CP_SUPPORTED)||
586 ((dwFlags & CP_INSTALLED)&&(wcslen(szValue) > 1)))
587 if (!lpCodePageEnumProc(szNumberA))
588 break;
589
590 ulIndex++;
591
592 } else bContinue = FALSE;
593
594 if (!bContinue)
595 break;
596 }
597
598 if (hKey)
599 NtClose(hKey);
600
601 return TRUE;
602 }
603
604
605 /*
606 * @implemented
607 */
608 BOOL
609 STDCALL
610 EnumSystemGeoID(
611 GEOCLASS GeoClass,
612 GEOID ParentGeoId, // reserved
613 GEO_ENUMPROC lpGeoEnumProc)
614 {
615 WCHAR szNumber[5 + 1];
616 ULONG ulIndex = 0;
617 HANDLE hKey;
618
619 DPRINT("(0x%08X,0x%08X,%p)\n", GeoClass, ParentGeoId, lpGeoEnumProc);
620
621 if(!lpGeoEnumProc || GeoClass != GEOCLASS_NATION)
622 {
623 SetLastError(ERROR_INVALID_PARAMETER);
624 return FALSE;
625 }
626
627 hKey = NLS_RegOpenKey(0, L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Country List");
628 if (!hKey)
629 {
630 DPRINT1("NLS_RegOpenKey() failed\n");
631 return FALSE;
632 }
633
634 while (NLS_RegEnumSubKey(hKey, ulIndex, szNumber, sizeof(szNumber)))
635 {
636 BOOL bContinue = TRUE;
637 DWORD dwGeoId;
638 HANDLE hSubKey = NLS_RegOpenKey(hKey, szNumber);
639
640 if (hSubKey)
641 {
642 if (NLS_RegGetDword(hSubKey, L"CountryCode", &dwGeoId))
643 {
644 if (!lpGeoEnumProc(dwGeoId))
645 bContinue = FALSE;
646 }
647
648 NtClose(hSubKey);
649 }
650
651 if (!bContinue)
652 break;
653
654 ulIndex++;
655 }
656
657 if (hKey)
658 NtClose(hKey);
659
660 return TRUE;
661 }
662
663
664 /*
665 * @unimplemented
666 */
667 BOOL
668 STDCALL
669 EnumSystemLanguageGroupsA(
670 LANGUAGEGROUP_ENUMPROCA pLangGroupEnumProc,
671 DWORD dwFlags,
672 LONG_PTR lParam)
673 {
674 DPRINT("(%p,0x%08X,0x%08lX)\n", pLangGroupEnumProc, dwFlags, lParam);
675
676 if ((!dwFlags & LGRPID_INSTALLED)&&(!dwFlags & LGRPID_SUPPORTED))
677 {
678 SetLastError(ERROR_INVALID_FLAGS);
679 return FALSE;
680 }
681
682 if (!pLangGroupEnumProc)
683 {
684 SetLastError(ERROR_INVALID_PARAMETER);
685 return FALSE;
686 }
687
688 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
689 return 0;
690 }
691
692
693 /*
694 * @unimplemented
695 */
696 BOOL
697 STDCALL
698 EnumSystemLanguageGroupsW(
699 LANGUAGEGROUP_ENUMPROCW pLangGroupEnumProc,
700 DWORD dwFlags,
701 LONG_PTR lParam)
702 {
703 DPRINT("(%p,0x%08X,0x%08lX)\n", pLangGroupEnumProc, dwFlags, lParam);
704
705 if ((!dwFlags & LGRPID_INSTALLED)&&(!dwFlags & LGRPID_SUPPORTED))
706 {
707 SetLastError(ERROR_INVALID_FLAGS);
708 return FALSE;
709 }
710
711 if (!pLangGroupEnumProc)
712 {
713 SetLastError(ERROR_INVALID_PARAMETER);
714 return FALSE;
715 }
716
717 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
718 return 0;
719 }
720
721
722 /*
723 * @unimplemented
724 */
725 BOOL
726 STDCALL
727 EnumSystemLocalesA (
728 LOCALE_ENUMPROCA lpLocaleEnumProc,
729 DWORD dwFlags
730 )
731 {
732 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
733 return FALSE;
734 }
735
736
737 /*
738 * @implemented
739 */
740 BOOL
741 STDCALL
742 EnumSystemLocalesW (
743 LOCALE_ENUMPROCW lpLocaleEnumProc,
744 DWORD dwFlags
745 )
746 {
747 NTSTATUS result;
748 HANDLE langKey;
749 UNICODE_STRING langKeyName = RTL_CONSTANT_STRING(
750 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\Locale");
751 OBJECT_ATTRIBUTES objectAttributes;
752 ULONG index, length;
753 unsigned char fullInfo[sizeof(KEY_VALUE_FULL_INFORMATION)+255*2]; //FIXME: MAX_PATH*2
754 PKEY_VALUE_FULL_INFORMATION pFullInfo;
755
756 //TODO: Combine with EnumSystemLocalesA - maybe by having one common part, driven by some
757 // unicode/non-unicode flag.
758
759 //FIXME: dwFlags is really not used, sorry
760
761 // Check if enum proc is a real one
762 if (lpLocaleEnumProc == NULL)
763 {
764 SetLastError(ERROR_INVALID_PARAMETER);
765 return FALSE;
766 }
767
768 // Open language registry key
769 //FIXME: Should we use critical section here?
770
771 InitializeObjectAttributes(&objectAttributes,
772 &langKeyName,
773 OBJ_CASE_INSENSITIVE,
774 NULL,
775 NULL);
776
777 result = NtOpenKey(&langKey,
778 KEY_READ,
779 &objectAttributes);
780
781 if (!NT_SUCCESS(result))
782 return result;
783
784 DPRINT("Registry key succesfully opened\n");
785
786 length = sizeof(KEY_VALUE_FULL_INFORMATION) + 255*2;//MAX_PATH*sizeof(WCHAR);
787 pFullInfo = (PKEY_VALUE_FULL_INFORMATION)&fullInfo;
788 RtlZeroMemory(pFullInfo, length);
789
790 index = 0;
791
792 result = NtEnumerateValueKey(langKey,
793 index,
794 KeyValueFullInformation,
795 pFullInfo,
796 length,
797 &length);
798
799 DPRINT("First enumerate call result=%x\n", result);
800 while (result != STATUS_NO_MORE_ENTRIES)
801 {
802 int i;
803 WCHAR lpLocale[9];
804
805 // TODO: Here we should check, in case dwFlags & LCID_INSTALLED is specified,
806 // if this locale is really installed
807 // but for now we skip it
808
809 for (i=0; i<8; i++)
810 lpLocale[i] = pFullInfo->Name[i];
811
812 lpLocale[8]=0;
813
814 DPRINT("Locale=%S\n", lpLocale);
815
816 // Call Enum func
817 if (!lpLocaleEnumProc((LPWSTR)lpLocale))
818 break;
819
820 // Zero previous values
821 RtlZeroMemory(pFullInfo, length);
822
823 index++;
824 result = NtEnumerateValueKey(langKey, index,KeyValueFullInformation, pFullInfo, length, &length);
825 }
826
827 NtClose(langKey);
828
829 return STATUS_SUCCESS;
830 }
831
832
833 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
834 LPCSTR name, WORD LangID, LONG_PTR lParam )
835 {
836 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
837 char buf[20];
838
839 sprintf(buf, "%08x", (UINT)LangID);
840 return enum_uilang->u.procA( buf, enum_uilang->param );
841 }
842
843
844 /*
845 * @implemented
846 */
847 BOOL
848 STDCALL
849 EnumUILanguagesA(
850 UILANGUAGE_ENUMPROCA lpUILanguageEnumProc,
851 DWORD dwFlags,
852 LONG_PTR lParam)
853 {
854 ENUM_UILANG_CALLBACK enum_uilang;
855
856 DPRINT("%p, %x, %lx\n", lpUILanguageEnumProc, dwFlags, lParam);
857
858 if(!lpUILanguageEnumProc) {
859 SetLastError(ERROR_INVALID_PARAMETER);
860 return FALSE;
861 }
862 if(dwFlags) {
863 SetLastError(ERROR_INVALID_FLAGS);
864 return FALSE;
865 }
866
867 enum_uilang.u.procA = lpUILanguageEnumProc;
868 enum_uilang.flags = dwFlags;
869 enum_uilang.param = lParam;
870
871 EnumResourceLanguagesA( hCurrentModule, (LPCSTR)RT_STRING,
872 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
873 (LONG_PTR)&enum_uilang);
874 return TRUE;
875 }
876
877 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
878 LPCWSTR name, WORD LangID, LONG_PTR lParam )
879 {
880 static const WCHAR formatW[] = {'%','0','8','x',0};
881 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
882 WCHAR buf[20];
883
884 swprintf( buf, formatW, (UINT)LangID );
885 return enum_uilang->u.procW( buf, enum_uilang->param );
886 }
887
888 /*
889 * @implemented
890 */
891 BOOL
892 STDCALL
893 EnumUILanguagesW(
894 UILANGUAGE_ENUMPROCW lpUILanguageEnumProc,
895 DWORD dwFlags,
896 LONG_PTR lParam)
897 {
898 ENUM_UILANG_CALLBACK enum_uilang;
899
900 DPRINT("%p, %x, %lx\n", lpUILanguageEnumProc, dwFlags, lParam);
901
902
903 if(!lpUILanguageEnumProc) {
904 SetLastError(ERROR_INVALID_PARAMETER);
905 return FALSE;
906 }
907 if(dwFlags) {
908 SetLastError(ERROR_INVALID_FLAGS);
909 return FALSE;
910 }
911
912 enum_uilang.u.procW = lpUILanguageEnumProc;
913 enum_uilang.flags = dwFlags;
914 enum_uilang.param = lParam;
915
916 EnumResourceLanguagesW( hCurrentModule, (LPCWSTR)RT_STRING,
917 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
918 (LONG_PTR)&enum_uilang);
919 return TRUE;
920 }
921
922 /*
923 * @implemented
924 */
925 int
926 STDCALL
927 GetCalendarInfoA(
928 LCID lcid,
929 CALID Calendar,
930 CALTYPE CalType,
931 LPSTR lpCalData,
932 int cchData,
933 LPDWORD lpValue
934 )
935 {
936 int ret;
937 LPWSTR lpCalDataW = NULL;
938
939 if (NLS_IsUnicodeOnlyLcid(lcid))
940 {
941 SetLastError(ERROR_INVALID_PARAMETER);
942 return 0;
943 }
944
945 if (cchData &&
946 !(lpCalDataW = HeapAlloc(GetProcessHeap(), 0, cchData*sizeof(WCHAR))))
947 return 0;
948
949 ret = GetCalendarInfoW(lcid, Calendar, CalType, lpCalDataW, cchData, lpValue);
950 if(ret && lpCalDataW && lpCalData)
951 WideCharToMultiByte(CP_ACP, 0, lpCalDataW, cchData, lpCalData, cchData, NULL, NULL);
952 HeapFree(GetProcessHeap(), 0, lpCalDataW);
953
954 return ret;
955 }
956
957
958 /*
959 * @unimplemented
960 */
961 int
962 STDCALL
963 GetCalendarInfoW(
964 LCID Locale,
965 CALID Calendar,
966 CALTYPE CalType,
967 LPWSTR lpCalData,
968 int cchData,
969 LPDWORD lpValue)
970 {
971 if (CalType & CAL_NOUSEROVERRIDE)
972 DPRINT("FIXME: flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
973 if (CalType & CAL_USE_CP_ACP)
974 DPRINT("FIXME: flag CAL_USE_CP_ACP used, not fully implemented\n");
975
976 if (CalType & CAL_RETURN_NUMBER) {
977 if (lpCalData != NULL)
978 DPRINT("WARNING: lpCalData not NULL (%p) when it should!\n", lpCalData);
979 if (cchData != 0)
980 DPRINT("WARNING: cchData not 0 (%d) when it should!\n", cchData);
981 } else {
982 if (lpValue != NULL)
983 DPRINT("WARNING: lpValue not NULL (%p) when it should!\n", lpValue);
984 }
985
986 /* FIXME: No verification is made yet wrt Locale
987 * for the CALTYPES not requiring GetLocaleInfoA */
988 switch (CalType & ~(CAL_NOUSEROVERRIDE|CAL_RETURN_NUMBER|CAL_USE_CP_ACP)) {
989 case CAL_ICALINTVALUE:
990 DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
991 return E_FAIL;
992 case CAL_SCALNAME:
993 DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
994 return E_FAIL;
995 case CAL_IYEAROFFSETRANGE:
996 DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
997 return E_FAIL;
998 case CAL_SERASTRING:
999 DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
1000 return E_FAIL;
1001 case CAL_SSHORTDATE:
1002 return GetLocaleInfoW(Locale, LOCALE_SSHORTDATE, lpCalData, cchData);
1003 case CAL_SLONGDATE:
1004 return GetLocaleInfoW(Locale, LOCALE_SLONGDATE, lpCalData, cchData);
1005 case CAL_SDAYNAME1:
1006 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME1, lpCalData, cchData);
1007 case CAL_SDAYNAME2:
1008 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME2, lpCalData, cchData);
1009 case CAL_SDAYNAME3:
1010 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME3, lpCalData, cchData);
1011 case CAL_SDAYNAME4:
1012 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME4, lpCalData, cchData);
1013 case CAL_SDAYNAME5:
1014 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME5, lpCalData, cchData);
1015 case CAL_SDAYNAME6:
1016 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME6, lpCalData, cchData);
1017 case CAL_SDAYNAME7:
1018 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME7, lpCalData, cchData);
1019 case CAL_SABBREVDAYNAME1:
1020 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME1, lpCalData, cchData);
1021 case CAL_SABBREVDAYNAME2:
1022 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME2, lpCalData, cchData);
1023 case CAL_SABBREVDAYNAME3:
1024 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME3, lpCalData, cchData);
1025 case CAL_SABBREVDAYNAME4:
1026 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME4, lpCalData, cchData);
1027 case CAL_SABBREVDAYNAME5:
1028 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME5, lpCalData, cchData);
1029 case CAL_SABBREVDAYNAME6:
1030 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME6, lpCalData, cchData);
1031 case CAL_SABBREVDAYNAME7:
1032 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME7, lpCalData, cchData);
1033 case CAL_SMONTHNAME1:
1034 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME1, lpCalData, cchData);
1035 case CAL_SMONTHNAME2:
1036 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME2, lpCalData, cchData);
1037 case CAL_SMONTHNAME3:
1038 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME3, lpCalData, cchData);
1039 case CAL_SMONTHNAME4:
1040 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME4, lpCalData, cchData);
1041 case CAL_SMONTHNAME5:
1042 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME5, lpCalData, cchData);
1043 case CAL_SMONTHNAME6:
1044 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME6, lpCalData, cchData);
1045 case CAL_SMONTHNAME7:
1046 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME7, lpCalData, cchData);
1047 case CAL_SMONTHNAME8:
1048 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME8, lpCalData, cchData);
1049 case CAL_SMONTHNAME9:
1050 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME9, lpCalData, cchData);
1051 case CAL_SMONTHNAME10:
1052 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME10, lpCalData, cchData);
1053 case CAL_SMONTHNAME11:
1054 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME11, lpCalData, cchData);
1055 case CAL_SMONTHNAME12:
1056 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME12, lpCalData, cchData);
1057 case CAL_SMONTHNAME13:
1058 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME13, lpCalData, cchData);
1059 case CAL_SABBREVMONTHNAME1:
1060 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME1, lpCalData, cchData);
1061 case CAL_SABBREVMONTHNAME2:
1062 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME2, lpCalData, cchData);
1063 case CAL_SABBREVMONTHNAME3:
1064 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME3, lpCalData, cchData);
1065 case CAL_SABBREVMONTHNAME4:
1066 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME4, lpCalData, cchData);
1067 case CAL_SABBREVMONTHNAME5:
1068 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME5, lpCalData, cchData);
1069 case CAL_SABBREVMONTHNAME6:
1070 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME6, lpCalData, cchData);
1071 case CAL_SABBREVMONTHNAME7:
1072 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME7, lpCalData, cchData);
1073 case CAL_SABBREVMONTHNAME8:
1074 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME8, lpCalData, cchData);
1075 case CAL_SABBREVMONTHNAME9:
1076 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME9, lpCalData, cchData);
1077 case CAL_SABBREVMONTHNAME10:
1078 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME10, lpCalData, cchData);
1079 case CAL_SABBREVMONTHNAME11:
1080 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME11, lpCalData, cchData);
1081 case CAL_SABBREVMONTHNAME12:
1082 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME12, lpCalData, cchData);
1083 case CAL_SABBREVMONTHNAME13:
1084 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME13, lpCalData, cchData);
1085 case CAL_SYEARMONTH:
1086 return GetLocaleInfoW(Locale, LOCALE_SYEARMONTH, lpCalData, cchData);
1087 case CAL_ITWODIGITYEARMAX:
1088 if (lpValue) *lpValue = CALINFO_MAX_YEAR;
1089 break;
1090 default: DPRINT("Unknown caltype %d\n",CalType & 0xffff);
1091 return E_FAIL;
1092 }
1093 return 0;
1094 }
1095
1096
1097 /*
1098 * @unimplemented
1099 */
1100 BOOL
1101 STDCALL
1102 GetCPInfo (
1103 UINT CodePage,
1104 LPCPINFO CodePageInfo
1105 )
1106 {
1107 unsigned i;
1108
1109 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1110
1111 CodePageInfo->MaxCharSize = 1;
1112 CodePageInfo->DefaultChar[0] = '?';
1113 for (i = 1; i < MAX_DEFAULTCHAR; i++)
1114 {
1115 CodePageInfo->DefaultChar[i] = 0;
1116 }
1117 for (i = 0; i < MAX_LEADBYTES; i++)
1118 {
1119 CodePageInfo->LeadByte[i] = 0;
1120 }
1121
1122 return TRUE;
1123 }
1124
1125
1126 /*
1127 * @unimplemented
1128 */
1129 BOOL
1130 STDCALL
1131 GetCPInfoExW(
1132 UINT CodePage,
1133 DWORD dwFlags,
1134 LPCPINFOEXW lpCPInfoEx)
1135 {
1136 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1137 return 0;
1138 }
1139
1140
1141 /*
1142 * @unimplemented
1143 */
1144 BOOL
1145 STDCALL
1146 GetCPInfoExA(
1147 UINT CodePage,
1148 DWORD dwFlags,
1149 LPCPINFOEXA lpCPInfoEx)
1150 {
1151 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1152 return 0;
1153 }
1154
1155 static int
1156 NLS_GetGeoFriendlyName(GEOID Location, LPWSTR szFriendlyName, int cchData)
1157 {
1158 HANDLE hKey;
1159 WCHAR szPath[MAX_PATH];
1160 UNICODE_STRING ValueName;
1161 KEY_VALUE_PARTIAL_INFORMATION *info;
1162 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1163 DWORD dwSize;
1164 NTSTATUS Status;
1165 int Ret;
1166
1167 swprintf(szPath, L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Country List\\%d", Location);
1168
1169 hKey = NLS_RegOpenKey(0, szPath);
1170 if (!hKey)
1171 {
1172 DPRINT1("NLS_RegOpenKey() failed\n");
1173 return 0;
1174 }
1175
1176 dwSize = info_size + cchData * sizeof(WCHAR);
1177
1178 if (!(info = HeapAlloc(GetProcessHeap(), 0, dwSize)))
1179 {
1180 NtClose(hKey);
1181 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1182 return 0;
1183 }
1184
1185 RtlInitUnicodeString(&ValueName, L"Name");
1186
1187 Status = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation,
1188 (LPBYTE)info, dwSize, &dwSize);
1189
1190 if (!Status)
1191 {
1192 Ret = (dwSize - info_size) / sizeof(WCHAR);
1193
1194 if (!Ret || ((WCHAR *)info->Data)[Ret-1])
1195 {
1196 if (Ret < cchData || !szFriendlyName) Ret++;
1197 else
1198 {
1199 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1200 Ret = 0;
1201 }
1202 }
1203
1204 if (Ret && szFriendlyName)
1205 {
1206 memcpy(szFriendlyName, info->Data, (Ret-1) * sizeof(WCHAR));
1207 szFriendlyName[Ret-1] = 0;
1208 }
1209 }
1210 else if (Status == STATUS_BUFFER_OVERFLOW && !szFriendlyName)
1211 {
1212 Ret = (dwSize - info_size) / sizeof(WCHAR) + 1;
1213 }
1214 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1215 {
1216 Ret = -1;
1217 }
1218 else
1219 {
1220 SetLastError(RtlNtStatusToDosError(Status));
1221 Ret = 0;
1222 }
1223
1224 NtClose(hKey);
1225 HeapFree(GetProcessHeap(), 0, info);
1226
1227 return Ret;
1228 }
1229
1230 /*
1231 * @unimplemented
1232 */
1233 int
1234 STDCALL
1235 GetGeoInfoW(
1236 GEOID Location,
1237 GEOTYPE GeoType,
1238 LPWSTR lpGeoData,
1239 int cchData,
1240 LANGID LangId)
1241 {
1242 DPRINT1("%d %d %p %d %d\n", Location, GeoType, lpGeoData, cchData, LangId);
1243
1244 if ((GeoType == GEO_TIMEZONES)||(GeoType == GEO_OFFICIALLANGUAGES))
1245 {
1246 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1247 }
1248
1249 switch (GeoType)
1250 {
1251 case GEO_FRIENDLYNAME:
1252 {
1253 return NLS_GetGeoFriendlyName(Location, lpGeoData, cchData);
1254 }
1255 case GEO_NATION:
1256 case GEO_LATITUDE:
1257 case GEO_LONGITUDE:
1258 case GEO_ISO2:
1259 case GEO_ISO3:
1260 case GEO_RFC1766:
1261 case GEO_LCID:
1262 case GEO_OFFICIALNAME:
1263 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1264 break;
1265 }
1266
1267 return 0;
1268 }
1269
1270
1271 /*
1272 * @unimplemented
1273 */
1274 int
1275 STDCALL
1276 GetGeoInfoA(
1277 GEOID Location,
1278 GEOTYPE GeoType,
1279 LPSTR lpGeoData,
1280 int cchData,
1281 LANGID LangId)
1282 {
1283 DPRINT1("%d %d %p %d %d\n", Location, GeoType, lpGeoData, cchData, LangId);
1284
1285 if ((GeoType == GEO_TIMEZONES)||(GeoType == GEO_OFFICIALLANGUAGES))
1286 {
1287 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1288 }
1289
1290 switch (GeoType)
1291 {
1292 case GEO_FRIENDLYNAME:
1293 {
1294 WCHAR szBuffer[MAX_PATH];
1295 int Ret;
1296
1297 Ret = NLS_GetGeoFriendlyName(Location, szBuffer, cchData);
1298 char szBufferA[sizeof(szBuffer)/sizeof(WCHAR)];
1299
1300 WideCharToMultiByte(CP_ACP, 0, szBuffer, -1, szBufferA, sizeof(szBufferA), 0, 0);
1301 strcpy(lpGeoData, szBufferA);
1302
1303 return Ret;
1304 }
1305 case GEO_NATION:
1306 case GEO_LATITUDE:
1307 case GEO_LONGITUDE:
1308 case GEO_ISO2:
1309 case GEO_ISO3:
1310 case GEO_RFC1766:
1311 case GEO_LCID:
1312 case GEO_OFFICIALNAME:
1313 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1314 break;
1315 }
1316
1317 return 0;
1318 }
1319
1320 const WCHAR *RosGetLocaleValueName( DWORD lctype )
1321 {
1322 switch (lctype & ~LOCALE_LOCALEINFOFLAGSMASK)
1323 {
1324 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1325 * the values are stored in the registry, confirmed under Windows.
1326 */
1327 case LOCALE_ICALENDARTYPE: return L"iCalendarType";
1328 case LOCALE_ICURRDIGITS: return L"iCurrDigits";
1329 case LOCALE_ICURRENCY: return L"iCurrency";
1330 case LOCALE_IDIGITS: return L"iDigits";
1331 case LOCALE_IFIRSTDAYOFWEEK: return L"iFirstDayOfWeek";
1332 case LOCALE_IFIRSTWEEKOFYEAR: return L"iFirstWeekOfYear";
1333 case LOCALE_ILZERO: return L"iLZero";
1334 case LOCALE_IMEASURE: return L"iMeasure";
1335 case LOCALE_INEGCURR: return L"iNegCurr";
1336 case LOCALE_INEGNUMBER: return L"iNegNumber";
1337 case LOCALE_IPAPERSIZE: return L"iPaperSize";
1338 case LOCALE_ITIME: return L"iTime";
1339 case LOCALE_S1159: return L"s1159";
1340 case LOCALE_S2359: return L"s2359";
1341 case LOCALE_SCURRENCY: return L"sCurrency";
1342 case LOCALE_SDATE: return L"sDate";
1343 case LOCALE_SDECIMAL: return L"sDecimal";
1344 case LOCALE_SGROUPING: return L"sGrouping";
1345 case LOCALE_SLIST: return L"sList";
1346 case LOCALE_SLONGDATE: return L"sLongDate";
1347 case LOCALE_SMONDECIMALSEP: return L"sMonDecimalSep";
1348 case LOCALE_SMONGROUPING: return L"sMonGrouping";
1349 case LOCALE_SMONTHOUSANDSEP: return L"sMonThousandSep";
1350 case LOCALE_SNEGATIVESIGN: return L"sNegativeSign";
1351 case LOCALE_SPOSITIVESIGN: return L"sPositiveSign";
1352 case LOCALE_SSHORTDATE: return L"sShortDate";
1353 case LOCALE_STHOUSAND: return L"sThousand";
1354 case LOCALE_STIME: return L"sTime";
1355 case LOCALE_STIMEFORMAT: return L"sTimeFormat";
1356 case LOCALE_SYEARMONTH: return L"sYearMonth";
1357
1358 /* The following are not listed under MSDN as supported,
1359 * but seem to be used and also stored in the registry.
1360 */
1361 case LOCALE_ICOUNTRY: return L"iCountry";
1362 case LOCALE_IDATE: return L"iDate";
1363 case LOCALE_ILDATE: return L"iLDate";
1364 case LOCALE_ITLZERO: return L"iTLZero";
1365 case LOCALE_SCOUNTRY: return L"sCountry";
1366 case LOCALE_SLANGUAGE: return L"sLanguage";
1367 }
1368 return NULL;
1369 }
1370
1371 HKEY RosCreateRegistryKey(void)
1372 {
1373 OBJECT_ATTRIBUTES objAttr;
1374 UNICODE_STRING nameW;
1375 HANDLE hKey;
1376
1377 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hKey ) != STATUS_SUCCESS) return 0;
1378
1379 objAttr.Length = sizeof(objAttr);
1380 objAttr.RootDirectory = hKey;
1381 objAttr.ObjectName = &nameW;
1382 objAttr.Attributes = 0;
1383 objAttr.SecurityDescriptor = NULL;
1384 objAttr.SecurityQualityOfService = NULL;
1385 RtlInitUnicodeString( &nameW, L"Control Panel\\International");
1386
1387 if (NtCreateKey( &hKey, KEY_ALL_ACCESS, &objAttr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) hKey = 0;
1388 NtClose( objAttr.RootDirectory );
1389 return hKey;
1390 }
1391
1392 INT RosGetRegistryLocaleInfo( LPCWSTR lpValue, LPWSTR lpBuffer, INT nLen )
1393 {
1394 DWORD dwSize;
1395 HKEY hKey;
1396 INT nRet;
1397 NTSTATUS ntStatus;
1398 UNICODE_STRING usNameW;
1399 KEY_VALUE_PARTIAL_INFORMATION *kvpiInfo;
1400 const int nInfoSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1401
1402 if (!(hKey = RosCreateRegistryKey())) return -1;
1403
1404 RtlInitUnicodeString( &usNameW, lpValue );
1405 dwSize = nInfoSize + nLen * sizeof(WCHAR);
1406
1407 if (!(kvpiInfo = HeapAlloc( GetProcessHeap(), 0, dwSize )))
1408 {
1409 NtClose( hKey );
1410 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1411 return 0;
1412 }
1413
1414 ntStatus = NtQueryValueKey( hKey, &usNameW, KeyValuePartialInformation, kvpiInfo, dwSize, &dwSize );
1415 if (ntStatus == STATUS_BUFFER_OVERFLOW && !lpBuffer) ntStatus = 0;
1416
1417 if (!ntStatus)
1418 {
1419 nRet = (dwSize - nInfoSize) / sizeof(WCHAR);
1420
1421 if (!nRet || ((WCHAR *)kvpiInfo->Data)[nRet - 1])
1422 {
1423 if (nRet < nLen || !lpBuffer) nRet++;
1424 else
1425 {
1426 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1427 nRet = 0;
1428 }
1429 }
1430 if (nRet && lpBuffer)
1431 {
1432 memcpy( lpBuffer, kvpiInfo->Data, (nRet - 1) * sizeof(WCHAR) );
1433 lpBuffer[nRet - 1] = 0;
1434 }
1435 }
1436 else
1437 {
1438 if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND) nRet = -1;
1439 else
1440 {
1441 SetLastError( RtlNtStatusToDosError(ntStatus) );
1442 nRet = 0;
1443 }
1444 }
1445 NtClose( hKey );
1446 HeapFree( GetProcessHeap(), 0, kvpiInfo );
1447 return nRet;
1448 }
1449
1450 /*
1451 * @implemented
1452 */
1453 int
1454 STDCALL
1455 GetLocaleInfoW (
1456 LCID Locale,
1457 LCTYPE LCType,
1458 LPWSTR lpLCData,
1459 int cchData
1460 )
1461 {
1462 LANGID liLangID;
1463 HRSRC hRsrc;
1464 HGLOBAL hMem;
1465 HMODULE hModule;
1466 INT nRet;
1467 UINT uiFlags;
1468 const WCHAR *ch;
1469 int i;
1470
1471 if (cchData < 0 || (cchData && !lpLCData))
1472 {
1473 SetLastError( ERROR_INVALID_PARAMETER );
1474 return 0;
1475 }
1476 if (!cchData) lpLCData = NULL;
1477
1478 if (Locale == LOCALE_NEUTRAL || Locale == LOCALE_SYSTEM_DEFAULT) Locale = GetSystemDefaultLCID();
1479 else if (Locale == LOCALE_USER_DEFAULT) Locale = GetUserDefaultLCID();
1480
1481 uiFlags = LCType & LOCALE_LOCALEINFOFLAGSMASK;
1482 LCType &= ~LOCALE_LOCALEINFOFLAGSMASK;
1483
1484 if (!(uiFlags & LOCALE_NOUSEROVERRIDE) && Locale == GetUserDefaultLCID())
1485 {
1486 const WCHAR *value = RosGetLocaleValueName(LCType);
1487
1488 if (value && ((nRet = RosGetRegistryLocaleInfo( value, lpLCData, cchData )) != -1)) return nRet;
1489 }
1490
1491 liLangID = LANGIDFROMLCID( Locale );
1492
1493 if (SUBLANGID(liLangID) == SUBLANG_NEUTRAL)
1494 liLangID = MAKELANGID(PRIMARYLANGID(liLangID), SUBLANG_DEFAULT);
1495
1496 hModule = GetModuleHandleW( L"kernel32.dll" );
1497 if (!(hRsrc = FindResourceExW( hModule, (LPWSTR)RT_STRING, (LPCWSTR)((LCType >> 4) + 1), liLangID )))
1498 {
1499 SetLastError( ERROR_INVALID_FLAGS );
1500 return 0;
1501 }
1502 if (!(hMem = LoadResource( hModule, hRsrc )))
1503 return 0;
1504
1505 ch = LockResource( hMem );
1506 for (i = 0; i < (int)(LCType & 0x0f); i++) ch += *ch + 1;
1507
1508 if (uiFlags & LOCALE_RETURN_NUMBER) nRet = sizeof(UINT) / sizeof(WCHAR);
1509 else nRet = (LCType == LOCALE_FONTSIGNATURE) ? *ch : *ch + 1;
1510
1511 if (!lpLCData) return nRet;
1512
1513 if (nRet > cchData)
1514 {
1515 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1516 return 0;
1517 }
1518
1519 if (uiFlags & LOCALE_RETURN_NUMBER)
1520 {
1521 UINT uiNum;
1522 WCHAR *chEnd, *chTmp = HeapAlloc( GetProcessHeap(), 0, (*ch + 1) * sizeof(WCHAR) );
1523
1524 if (!chTmp)
1525 return 0;
1526
1527 memcpy( chTmp, ch + 1, *ch * sizeof(WCHAR) );
1528 chTmp[*ch] = L'\0';
1529 uiNum = wcstol( chTmp, &chEnd, 10 );
1530
1531 if (!*chEnd)
1532 memcpy( lpLCData, &uiNum, sizeof(uiNum) );
1533 else
1534 {
1535 SetLastError( ERROR_INVALID_FLAGS );
1536 nRet = 0;
1537 }
1538 HeapFree( GetProcessHeap(), 0, chTmp );
1539 }
1540 else
1541 {
1542 memcpy( lpLCData, ch + 1, *ch * sizeof(WCHAR) );
1543 if (LCType != LOCALE_FONTSIGNATURE) lpLCData[nRet-1] = 0;
1544 }
1545 return nRet;
1546 }
1547
1548
1549
1550 /***********************************************************************
1551 * get_lcid_codepage
1552 *
1553 * Retrieve the ANSI codepage for a given locale.
1554 */
1555 __inline static UINT get_lcid_codepage( LCID lcid )
1556 {
1557 UINT ret;
1558 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
1559 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
1560 return ret;
1561 }
1562
1563
1564 /*
1565 * @implemented
1566 */
1567 /* Synced to Wine-20102004 */
1568 int
1569 STDCALL
1570 CompareStringA (
1571 LCID Locale,
1572 DWORD dwCmpFlags,
1573 LPCSTR lpString1,
1574 int cchCount1,
1575 LPCSTR lpString2,
1576 int cchCount2
1577 )
1578 {
1579 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
1580 WCHAR *buf2W = buf1W + 130;
1581 LPWSTR str1W, str2W;
1582 INT len1W, len2W, ret;
1583 UINT locale_cp;
1584
1585 if (!lpString1 || !lpString2)
1586 {
1587 SetLastError(ERROR_INVALID_PARAMETER);
1588 return 0;
1589 }
1590 if (cchCount1 < 0) cchCount1 = strlen(lpString1);
1591 if (cchCount2 < 0) cchCount2 = strlen(lpString2);
1592
1593 locale_cp = get_lcid_codepage(Locale);
1594
1595 len1W = MultiByteToWideChar(locale_cp, 0, lpString1, cchCount1, buf1W, 130);
1596 if (len1W)
1597 str1W = buf1W;
1598 else
1599 {
1600 len1W = MultiByteToWideChar(locale_cp, 0, lpString1, cchCount1, NULL, 0);
1601 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
1602 if (!str1W)
1603 {
1604 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1605 return 0;
1606 }
1607 MultiByteToWideChar(locale_cp, 0, lpString1, cchCount1, str1W, len1W);
1608 }
1609 len2W = MultiByteToWideChar(locale_cp, 0, lpString2, cchCount2, buf2W, 130);
1610 if (len2W)
1611 str2W = buf2W;
1612 else
1613 {
1614 len2W = MultiByteToWideChar(locale_cp, 0, lpString2, cchCount2, NULL, 0);
1615 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
1616 if (!str2W)
1617 {
1618 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
1619 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1620 return 0;
1621 }
1622 MultiByteToWideChar(locale_cp, 0, lpString2, cchCount2, str2W, len2W);
1623 }
1624
1625 ret = CompareStringW(Locale, dwCmpFlags, str1W, len1W, str2W, len2W);
1626
1627 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
1628 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
1629 return ret;
1630 }
1631
1632
1633 static int compare_unicode_string(
1634 PUNICODE_STRING String1,
1635 PUNICODE_STRING String2,
1636 DWORD Flags
1637 )
1638 {
1639 ULONG len1, len2;
1640 PWCHAR s1, s2;
1641 WCHAR c1, c2;
1642
1643 if (String1 && String2)
1644 {
1645 len1 = String1->Length / sizeof(WCHAR);
1646 len2 = String2->Length / sizeof(WCHAR);
1647 s1 = String1->Buffer;
1648 s2 = String2->Buffer;
1649
1650 while (len1 > 0 && len2 > 0)
1651 {
1652 if (Flags & NORM_IGNORESYMBOLS)
1653 {
1654 int skip = 0;
1655 /* FIXME: not tested */
1656 if (iswctype(*s1, _SPACE | _PUNCT))
1657 {
1658 s1++;
1659 len1--;
1660 skip = 1;
1661 }
1662 if (iswctype(*s2, _SPACE | _PUNCT))
1663 {
1664 s2++;
1665 len2--;
1666 skip = 1;
1667 }
1668 if (skip) continue;
1669 }
1670
1671 /* hyphen and apostrophe are treated differently depending on
1672 * whether SORT_STRINGSORT specified or not
1673 */
1674 if (!(Flags & SORT_STRINGSORT))
1675 {
1676 if (*s1 == '-' || *s1 == '\'')
1677 {
1678 if (*s2 != '-' && *s2 != '\'')
1679 {
1680 s1++;
1681 len1--;
1682 continue;
1683 }
1684 }
1685 else if (*s2 == '-' || *s2 == '\'')
1686 {
1687 s2++;
1688 len2--;
1689 continue;
1690 }
1691 }
1692 if (Flags & NORM_IGNORECASE)
1693 {
1694 c1 = len1-- ? RtlUpcaseUnicodeChar(*s1++) : 0;
1695 c2 = len2-- ? RtlUpcaseUnicodeChar(*s2++) : 0;
1696 if (!c1 || !c2 || c1 != c2)
1697 return c1 - c2;
1698 }
1699 else
1700 {
1701 c1 = len1-- ? *s1++ : 0;
1702 c2 = len2-- ? *s2++ : 0;
1703 if (!c1 || !c2 || c1 != c2)
1704 return c1 - c2;
1705 }
1706 }
1707 return (int) len1 - (int) len2;
1708 }
1709 return 0;
1710 }
1711
1712
1713 /*
1714 * @unimplemented
1715 */
1716 int
1717 STDCALL
1718 CompareStringW (
1719 LCID Locale,
1720 DWORD dwCmpFlags,
1721 LPCWSTR lpString1,
1722 int cchCount1,
1723 LPCWSTR lpString2,
1724 int cchCount2
1725 )
1726 {
1727 INT Result;
1728 UNICODE_STRING String1, String2;
1729
1730 if (!lpString1 || !lpString2)
1731 {
1732 SetLastError(ERROR_INVALID_PARAMETER);
1733 return 0;
1734 }
1735
1736 if (dwCmpFlags & ~(NORM_IGNORECASE | NORM_IGNORENONSPACE |
1737 NORM_IGNORESYMBOLS | SORT_STRINGSORT | NORM_IGNOREKANATYPE |
1738 NORM_IGNOREWIDTH | 0x10000000))
1739 {
1740 SetLastError(ERROR_INVALID_FLAGS);
1741 return 0;
1742 }
1743
1744 if (cchCount1 < 0) cchCount1 = lstrlenW(lpString1);
1745 if (cchCount2 < 0) cchCount2 = lstrlenW(lpString2);
1746
1747 String1.Length = String1.MaximumLength = cchCount1 * sizeof(WCHAR);
1748 String1.Buffer = (LPWSTR)lpString1;
1749 String2.Length = String2.MaximumLength = cchCount2 * sizeof(WCHAR);
1750 String2.Buffer = (LPWSTR)lpString2;
1751
1752
1753 if (dwCmpFlags & ~NORM_IGNORECASE)
1754 {
1755 DPRINT("CompareString: STUB flags - 0x%x\n", dwCmpFlags);
1756 Result = compare_unicode_string(&String1, &String2, dwCmpFlags);
1757 }
1758 else
1759 Result = RtlCompareUnicodeString(
1760 &String1, &String2, (BOOLEAN)(dwCmpFlags & NORM_IGNORECASE));
1761
1762
1763 if (Result) /* need to translate result */
1764 return (Result < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
1765
1766 return CSTR_EQUAL;
1767 }
1768
1769
1770
1771
1772 /*
1773 * @implemented
1774 *
1775 * Get information about an aspect of a locale.
1776 *
1777 * PARAMS
1778 * lcid [I] LCID of the locale
1779 * lctype [I] LCTYPE_ flags from "winnls.h"
1780 * buffer [O] Destination for the information
1781 * len [I] Length of buffer in characters
1782 *
1783 * RETURNS
1784 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1785 * with the information.
1786 * Failure: 0. Use GetLastError() to determine the cause.
1787 *
1788 * NOTES
1789 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1790 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1791 * which is a bit string.
1792 */
1793 INT STDCALL GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1794 {
1795 WCHAR *bufferW;
1796 INT lenW, ret;
1797
1798 if (len < 0 || (len && !buffer))
1799 {
1800 SetLastError( ERROR_INVALID_PARAMETER );
1801 return 0;
1802 }
1803 if (!len) buffer = NULL;
1804
1805 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1806
1807 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1808 {
1809 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1810 return 0;
1811 }
1812 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1813 {
1814 if ((lctype & LOCALE_RETURN_NUMBER) ||
1815 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1816 {
1817 /* it's not an ASCII string, just bytes */
1818 ret *= sizeof(WCHAR);
1819 if (buffer)
1820 {
1821 if (ret <= len) memcpy( buffer, bufferW, ret );
1822 else
1823 {
1824 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1825 ret = 0;
1826 }
1827 }
1828 }
1829 else
1830 {
1831 UINT codepage = CP_ACP;
1832 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1833 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1834 }
1835 }
1836 HeapFree( GetProcessHeap(), 0, bufferW );
1837 return ret;
1838 }
1839
1840
1841 /*
1842 * @implemented
1843 */
1844 LANGID STDCALL
1845 GetSystemDefaultLangID(VOID)
1846 {
1847 return LANGIDFROMLCID(GetSystemDefaultLCID());
1848 }
1849
1850
1851 /*
1852 * @implemented
1853 */
1854 LCID STDCALL
1855 GetSystemDefaultLCID(VOID)
1856 {
1857 LCID lcid;
1858
1859 NtQueryDefaultLocale(FALSE, &lcid);
1860
1861 return lcid;
1862 }
1863
1864
1865 /*
1866 * @implemented
1867 */
1868 LANGID STDCALL
1869 GetSystemDefaultUILanguage(VOID)
1870 {
1871 LANGID LanguageId;
1872 NTSTATUS Status;
1873
1874 Status = NtQueryInstallUILanguage(&LanguageId);
1875 if (!NT_SUCCESS(Status))
1876 {
1877 SetLastErrorByStatus(Status);
1878 return 0;
1879 }
1880
1881 return LanguageId;
1882 }
1883
1884
1885 /*
1886 * @implemented
1887 */
1888 LCID STDCALL
1889 GetThreadLocale(VOID)
1890 {
1891 return NtCurrentTeb()->CurrentLocale;
1892 }
1893
1894
1895 /*
1896 * @implemented
1897 */
1898 LANGID STDCALL
1899 GetUserDefaultLangID(VOID)
1900 {
1901 return LANGIDFROMLCID(GetUserDefaultLCID());
1902 }
1903
1904
1905 /*
1906 * @implemented
1907 */
1908 LCID STDCALL
1909 GetUserDefaultLCID(VOID)
1910 {
1911 LCID lcid;
1912 NTSTATUS Status;
1913
1914 Status = NtQueryDefaultLocale(TRUE, &lcid);
1915 if (!NT_SUCCESS(Status))
1916 {
1917 SetLastErrorByStatus(Status);
1918 return 0;
1919 }
1920
1921 return lcid;
1922 }
1923
1924
1925 /*
1926 * @implemented
1927 */
1928 LANGID STDCALL
1929 GetUserDefaultUILanguage(VOID)
1930 {
1931 LANGID LangId;
1932 NTSTATUS Status;
1933
1934 Status = NtQueryDefaultUILanguage(&LangId);
1935 if (!NT_SUCCESS(Status))
1936 {
1937 SetLastErrorByStatus(Status);
1938 return 0;
1939 }
1940
1941 return LangId;
1942 }
1943
1944
1945 /***********************************************************************
1946 * create_registry_key
1947 *
1948 * Create the Control Panel\\International registry key.
1949 */
1950 static inline HANDLE create_registry_key(void)
1951 {
1952 static const WCHAR intlW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
1953 'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
1954 OBJECT_ATTRIBUTES attr;
1955 UNICODE_STRING nameW;
1956 HANDLE hkey;
1957
1958 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
1959
1960 attr.Length = sizeof(attr);
1961 attr.RootDirectory = hkey;
1962 attr.ObjectName = &nameW;
1963 attr.Attributes = 0;
1964 attr.SecurityDescriptor = NULL;
1965 attr.SecurityQualityOfService = NULL;
1966 RtlInitUnicodeString( &nameW, intlW );
1967
1968 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) hkey = 0;
1969 NtClose( attr.RootDirectory );
1970 return hkey;
1971 }
1972
1973
1974 /*
1975 * @unimplemented
1976 */
1977 GEOID
1978 STDCALL
1979 GetUserGeoID(
1980 GEOCLASS GeoClass)
1981 {
1982 GEOID ret = GEOID_NOT_AVAILABLE;
1983 static const WCHAR geoW[] = {'G','e','o',0};
1984 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
1985 WCHAR bufferW[40], *end;
1986 DWORD count;
1987 HANDLE hkey, hSubkey = 0;
1988 UNICODE_STRING keyW;
1989 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
1990 RtlInitUnicodeString( &keyW, nationW );
1991 count = sizeof(bufferW);
1992
1993 if(!(hkey = create_registry_key())) return ret;
1994
1995 switch( GeoClass ){
1996 case GEOCLASS_NATION:
1997 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
1998 {
1999 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
2000 (LPBYTE)bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
2001 ret = wcstol((LPCWSTR)info->Data, &end, 10);
2002 }
2003 break;
2004 case GEOCLASS_REGION:
2005 DPRINT("GEOCLASS_REGION not handled yet\n");
2006 break;
2007 }
2008
2009 NtClose(hkey);
2010 if (hSubkey) NtClose(hSubkey);
2011 return ret;
2012 }
2013
2014
2015 /******************************************************************************
2016 * IsValidLanguageGroup
2017 *
2018 * Determine if a language group is supported and/or installed.
2019 *
2020 * PARAMS
2021 * LanguageGroup [I] Language Group Id (LGRPID_ values from "winnls.h")
2022 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
2023 *
2024 * RETURNS
2025 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
2026 * FALSE otherwise.
2027 *
2028 * @implemented
2029 */
2030 BOOL
2031 STDCALL
2032 IsValidLanguageGroup(
2033 LGRPID LanguageGroup,
2034 DWORD dwFlags)
2035 {
2036 static const WCHAR szFormat[] = { '%','x','\0' };
2037 UNICODE_STRING szNlsKeyName =
2038 RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls");
2039 UNICODE_STRING szLangGroupsKeyName =
2040 RTL_CONSTANT_STRING(L"Language Groups");
2041 const int MAX_VALUE_NAME = 16;
2042 const int MAX_VALUE_SYMB = 128;
2043
2044 BOOL bNtQuery;
2045 PKEY_VALUE_PARTIAL_INFORMATION kvpiInfo;
2046
2047 WCHAR szValueName[MAX_VALUE_NAME];
2048 UNICODE_STRING ucsValueName;
2049 DWORD dwRetSize;
2050 PWSTR pwszValueData;
2051
2052 DWORD dwSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + MAX_VALUE_SYMB * sizeof(WCHAR);
2053
2054 OBJECT_ATTRIBUTES oaAttr;
2055 HANDLE hkey, hRootKey;
2056 BOOL bSupported = FALSE, bInstalled = FALSE;
2057
2058 DPRINT("IsValidLanguageGroup() called\n");
2059
2060 kvpiInfo = RtlAllocateHeap(RtlGetProcessHeap(),
2061 HEAP_ZERO_MEMORY,
2062 dwSize);
2063
2064 switch (dwFlags)
2065 {
2066 case LGRPID_INSTALLED:
2067 case LGRPID_SUPPORTED:
2068
2069 InitializeObjectAttributes(&oaAttr, &szNlsKeyName, 0, 0, NULL);
2070 if(NtOpenKey(&hRootKey, KEY_ALL_ACCESS, &oaAttr) != STATUS_SUCCESS) return FALSE;
2071
2072 InitializeObjectAttributes(&oaAttr, &szLangGroupsKeyName, 0, hRootKey, NULL);
2073 if(NtOpenKey(&hkey, KEY_ALL_ACCESS, &oaAttr) != STATUS_SUCCESS) return FALSE;
2074
2075 if(hRootKey) NtClose(hRootKey);
2076
2077 swprintf(szValueName, szFormat, (ULONG)LanguageGroup);
2078 RtlInitUnicodeString(&ucsValueName, szValueName);
2079
2080 bNtQuery = NtQueryValueKey(hkey,
2081 &ucsValueName,
2082 KeyValuePartialInformation,
2083 kvpiInfo,
2084 dwSize,
2085 &dwRetSize);
2086 if(hkey) NtClose(hkey);
2087
2088 if(bNtQuery == STATUS_SUCCESS &&
2089 kvpiInfo->DataLength == sizeof(DWORD))
2090 {
2091 pwszValueData = (PWSTR)&kvpiInfo->Data[0];
2092 bSupported = TRUE;
2093 if(pwszValueData[0] == L'1') bInstalled = TRUE;
2094 }
2095 else
2096 {
2097 DPRINT("NtQueryValueKey() failed (Status %lx)\n", bNtQuery);
2098 RtlFreeHeap(RtlGetProcessHeap(), 0, kvpiInfo);
2099 return FALSE;
2100 }
2101
2102 break;
2103 }
2104
2105 RtlFreeHeap(RtlGetProcessHeap(), 0, kvpiInfo);
2106
2107 if((dwFlags == LGRPID_SUPPORTED && bSupported) ||
2108 (dwFlags == LGRPID_INSTALLED && bInstalled))
2109 {
2110 DPRINT("Language group is supported and installed\n");
2111 return TRUE;
2112 }
2113
2114 return FALSE;
2115 }
2116
2117
2118 /******************************************************************************
2119 * IsValidLocale
2120 *
2121 * Determine if a locale is valid.
2122 *
2123 * PARAMS
2124 * Locale [I] LCID of the locale to check
2125 * dwFlags [I] LCID_SUPPORTED = Valid
2126 * LCID_INSTALLED = Valid and installed on the system
2127 *
2128 * RETURN
2129 * TRUE, if Locale is valid,
2130 * FALSE, otherwise.
2131 *
2132 * @implemented
2133 */
2134 BOOL STDCALL
2135 IsValidLocale(LCID Locale,
2136 DWORD dwFlags)
2137 {
2138 OBJECT_ATTRIBUTES ObjectAttributes;
2139 PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
2140 WCHAR ValueNameBuffer[9];
2141 UNICODE_STRING KeyName;
2142 UNICODE_STRING ValueName;
2143 ULONG KeyInfoSize;
2144 ULONG ReturnedSize;
2145 HANDLE KeyHandle;
2146 PWSTR ValueData;
2147 NTSTATUS Status;
2148
2149 DPRINT("IsValidLocale() called\n");
2150
2151 if ((dwFlags & ~(LCID_SUPPORTED | LCID_INSTALLED)) ||
2152 (dwFlags == (LCID_SUPPORTED | LCID_INSTALLED)))
2153 {
2154 DPRINT("Invalid flags: %lx\n", dwFlags);
2155 return FALSE;
2156 }
2157
2158 if (Locale & 0xFFFF0000)
2159 {
2160 RtlInitUnicodeString(&KeyName,
2161 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Nls\\Locale\\Alternate Sorts");
2162 }
2163 else
2164 {
2165 RtlInitUnicodeString(&KeyName,
2166 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Nls\\Locale");
2167 }
2168
2169 InitializeObjectAttributes(&ObjectAttributes,
2170 &KeyName,
2171 OBJ_CASE_INSENSITIVE,
2172 NULL,
2173 NULL);
2174
2175 Status = NtOpenKey(&KeyHandle,
2176 KEY_QUERY_VALUE,
2177 &ObjectAttributes);
2178 if (!NT_SUCCESS(Status))
2179 {
2180 DPRINT("NtOpenKey() failed (Status %lx)\n", Status);
2181 return FALSE;
2182 }
2183
2184 swprintf(ValueNameBuffer, L"%08lx", (ULONG)Locale);
2185 RtlInitUnicodeString(&ValueName, ValueNameBuffer);
2186
2187 KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4 * sizeof(WCHAR);
2188 KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(),
2189 HEAP_ZERO_MEMORY,
2190 KeyInfoSize);
2191 if (KeyInfo == NULL)
2192 {
2193 DPRINT("RtlAllocateHeap() failed (Status %lx)\n", Status);
2194 NtClose(KeyHandle);
2195 return FALSE;
2196 }
2197
2198 Status = NtQueryValueKey(KeyHandle,
2199 &ValueName,
2200 KeyValuePartialInformation,
2201 KeyInfo,
2202 KeyInfoSize,
2203 &ReturnedSize);
2204 NtClose(KeyHandle);
2205
2206 if (!NT_SUCCESS(Status))
2207 {
2208 DPRINT("NtQueryValueKey() failed (Status %lx)\n", Status);
2209 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
2210 return FALSE;
2211 }
2212
2213 if (dwFlags & LCID_SUPPORTED)
2214 {
2215 DPRINT("Locale is supported\n");
2216 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
2217 return TRUE;
2218 }
2219
2220 ValueData = (PWSTR)&KeyInfo->Data[0];
2221 if ((KeyInfo->Type == REG_SZ) &&
2222 (KeyInfo->DataLength == 2 * sizeof(WCHAR)) &&
2223 (ValueData[0] == L'1'))
2224 {
2225 DPRINT("Locale is supported and installed\n");
2226 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
2227 return TRUE;
2228 }
2229
2230 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
2231
2232 DPRINT("IsValidLocale() called\n");
2233
2234 return FALSE;
2235 }
2236
2237
2238 /*
2239 * @unimplemented
2240 */
2241 int
2242 STDCALL
2243 LCMapStringA (
2244 LCID Locale,
2245 DWORD dwMapFlags,
2246 LPCSTR lpSrcStr,
2247 int cchSrc,
2248 LPSTR lpDestStr,
2249 int cchDest
2250 )
2251 {
2252 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2253 return 0;
2254 }
2255
2256
2257 /*
2258 * @unimplemented
2259 */
2260 int
2261 STDCALL
2262 LCMapStringW (
2263 LCID Locale,
2264 DWORD dwMapFlags,
2265 LPCWSTR lpSrcStr,
2266 int cchSrc,
2267 LPWSTR lpDestStr,
2268 int cchDest
2269 )
2270 {
2271 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2272 return 0;
2273 }
2274
2275
2276 /*
2277 * @unimplemented
2278 */
2279 BOOL
2280 STDCALL
2281 SetCalendarInfoA(
2282 LCID Locale,
2283 CALID Calendar,
2284 CALTYPE CalType,
2285 LPCSTR lpCalData)
2286 {
2287 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2288 return 0;
2289 }
2290
2291 /*
2292 * @unimplemented
2293 */
2294 BOOL
2295 STDCALL
2296 SetCalendarInfoW(
2297 LCID Locale,
2298 CALID Calendar,
2299 CALTYPE CalType,
2300 LPCWSTR lpCalData)
2301 {
2302 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2303 return 0;
2304 }
2305
2306
2307 /**********************************************************************
2308 * @implemented
2309 * RIPPED FROM WINE's dlls\kernel\locale.c ver 0.9.29
2310 *
2311 * SetLocaleInfoA (KERNEL32.@)
2312 *
2313 * Set the current locale info.
2314 *
2315 * PARAMS
2316 * Locale [I] LCID of the locale
2317 * LCType [I] LCTYPE_ flags from "winnls.h"
2318 * lpLCData [I] Information to set
2319 *
2320 * RETURNS
2321 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
2322 * whenever it is called without LOCALE_NOUSEROVERRIDE.
2323 * Failure: FALSE. Use GetLastError() to determine the cause.
2324 */
2325 BOOL
2326 STDCALL
2327 SetLocaleInfoA (
2328 LCID Locale,
2329 LCTYPE LCType,
2330 LPCSTR lpLCData
2331 )
2332 {
2333 UINT codepage = CP_ACP;
2334 WCHAR *strW;
2335 DWORD len;
2336 BOOL ret;
2337
2338 if (!(LCType & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( Locale );
2339
2340 if (!lpLCData)
2341 {
2342 SetLastError( ERROR_INVALID_PARAMETER );
2343 return FALSE;
2344 }
2345 len = MultiByteToWideChar( codepage, 0, lpLCData, -1, NULL, 0 );
2346 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
2347 {
2348 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
2349 return FALSE;
2350 }
2351 MultiByteToWideChar( codepage, 0, lpLCData, -1, strW, len );
2352 ret = SetLocaleInfoW( Locale, LCType, strW );
2353 HeapFree( GetProcessHeap(), 0, strW );
2354 return ret;
2355 }
2356
2357
2358 /**********************************************************************
2359 * @implemented
2360 * RIPPED FROM WINE's dlls\kernel\locale.c ver 0.9.29
2361 *
2362 * SetLocaleInfoW (KERNEL32.@)
2363 *
2364 * See SetLocaleInfoA.
2365 *
2366 */
2367 BOOL
2368 STDCALL
2369 SetLocaleInfoW (
2370 LCID Locale,
2371 LCTYPE LCType,
2372 LPCWSTR lpLCData
2373 )
2374 {
2375 const WCHAR *value;
2376 UNICODE_STRING valueW;
2377 NTSTATUS status;
2378 HANDLE hkey;
2379
2380 LCType &= 0xffff;
2381 value = RosGetLocaleValueName( LCType );
2382
2383 if (!lpLCData || !value)
2384 {
2385 SetLastError( ERROR_INVALID_PARAMETER );
2386 return FALSE;
2387 }
2388
2389 if (LCType == LOCALE_IDATE || LCType == LOCALE_ILDATE)
2390 {
2391 SetLastError( ERROR_INVALID_FLAGS );
2392 return FALSE;
2393 }
2394
2395 if (!(hkey = RosCreateRegistryKey())) return FALSE;
2396 RtlInitUnicodeString( &valueW, value );
2397 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, (PVOID)lpLCData, (lstrlenW(lpLCData)+1)*sizeof(WCHAR) );
2398
2399 if (LCType == LOCALE_SSHORTDATE || LCType == LOCALE_SLONGDATE)
2400 {
2401 /* Set I-value from S value */
2402 WCHAR *lpD, *lpM, *lpY;
2403 WCHAR szBuff[2];
2404
2405 lpD = wcschr(lpLCData, 'd');
2406 lpM = wcschr(lpLCData, 'M');
2407 lpY = wcschr(lpLCData, 'y');
2408
2409 if (lpD <= lpM)
2410 {
2411 szBuff[0] = '1'; /* D-M-Y */
2412 }
2413 else
2414 {
2415 if (lpY <= lpM)
2416 szBuff[0] = '2'; /* Y-M-D */
2417 else
2418 szBuff[0] = '0'; /* M-D-Y */
2419 }
2420
2421 szBuff[1] = '\0';
2422
2423 if (LCType == LOCALE_SSHORTDATE)
2424 LCType = LOCALE_IDATE;
2425 else
2426 LCType = LOCALE_ILDATE;
2427
2428 value = RosGetLocaleValueName( LCType );
2429
2430 RtlInitUnicodeString( &valueW, value );
2431 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
2432 }
2433
2434 NtClose( hkey );
2435
2436 if (status) SetLastError( RtlNtStatusToDosError(status) );
2437 return !status;
2438 }
2439
2440
2441 /**********************************************************************
2442 * @implemented
2443 * RIPPED FROM WINE's dlls\kernel\locale.c rev 1.42
2444 *
2445 * SetThreadLocale (KERNEL32.@)
2446 *
2447 * Set the current threads locale.
2448 *
2449 * PARAMS
2450 * lcid [I] LCID of the locale to set
2451 *
2452 * RETURNS
2453 * Success: TRUE. The threads locale is set to lcid.
2454 * Failure: FALSE. Use GetLastError() to determine the cause.
2455 *
2456 */
2457 BOOL WINAPI SetThreadLocale( LCID lcid )
2458 {
2459 DPRINT("SetThreadLocale(0x%04lX)\n", lcid);
2460
2461 lcid = ConvertDefaultLocale(lcid);
2462
2463 if (lcid != GetThreadLocale())
2464 {
2465 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2466 {
2467 SetLastError(ERROR_INVALID_PARAMETER);
2468 return FALSE;
2469 }
2470
2471 NtCurrentTeb()->CurrentLocale = lcid;
2472 /* FIXME: NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
2473 * Wine save the acp for easy/fast access, but ROS has no such Teb member.
2474 * Maybe add this member to ros as well?
2475 */
2476
2477 /*
2478 Lag test app for å se om locale etc, endres i en app. etter at prosessen er
2479 startet, eller om bare nye prosesser blir berørt.
2480 */
2481 }
2482 return TRUE;
2483 }
2484
2485
2486 /*
2487 * @implemented
2488 */
2489 BOOL STDCALL
2490 SetUserDefaultLCID(LCID lcid)
2491 {
2492 NTSTATUS Status;
2493
2494 Status = NtSetDefaultLocale(TRUE, lcid);
2495 if (!NT_SUCCESS(Status))
2496 {
2497 SetLastErrorByStatus(Status);
2498 return 0;
2499 }
2500 return 1;
2501 }
2502
2503
2504 /*
2505 * @implemented
2506 */
2507 BOOL STDCALL
2508 SetUserDefaultUILanguage(LANGID LangId)
2509 {
2510 NTSTATUS Status;
2511
2512 Status = NtSetDefaultUILanguage(LangId);
2513 if (!NT_SUCCESS(Status))
2514 {
2515 SetLastErrorByStatus(Status);
2516 return 0;
2517 }
2518 return 1;
2519 }
2520
2521
2522 /*
2523 * @implemented
2524 */
2525 BOOL
2526 STDCALL
2527 SetUserGeoID(
2528 GEOID GeoId)
2529 {
2530 static const WCHAR geoW[] = {'G','e','o',0};
2531 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
2532 static const WCHAR formatW[] = {'%','i',0};
2533 UNICODE_STRING nameW,keyW;
2534 WCHAR bufferW[10];
2535 OBJECT_ATTRIBUTES attr;
2536 HANDLE hkey;
2537
2538 if(!(hkey = create_registry_key())) return FALSE;
2539
2540 attr.Length = sizeof(attr);
2541 attr.RootDirectory = hkey;
2542 attr.ObjectName = &nameW;
2543 attr.Attributes = 0;
2544 attr.SecurityDescriptor = NULL;
2545 attr.SecurityQualityOfService = NULL;
2546 RtlInitUnicodeString( &nameW, geoW );
2547 RtlInitUnicodeString( &keyW, nationW );
2548
2549 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
2550
2551 {
2552 NtClose(attr.RootDirectory);
2553 return FALSE;
2554 }
2555
2556 swprintf(bufferW, formatW, GeoId);
2557 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (wcslen(bufferW) + 1) * sizeof(WCHAR));
2558 NtClose(attr.RootDirectory);
2559 NtClose(hkey);
2560 return TRUE;
2561 }
2562
2563
2564 /*
2565 * @implemented
2566 */
2567 DWORD
2568 STDCALL
2569 VerLanguageNameA (
2570 DWORD wLang,
2571 LPSTR szLang,
2572 DWORD nSize
2573 )
2574 {
2575 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2576 }
2577
2578
2579 /*
2580 * @implemented
2581 */
2582 DWORD
2583 STDCALL
2584 VerLanguageNameW (
2585 DWORD wLang,
2586 LPWSTR szLang,
2587 DWORD nSize
2588 )
2589 {
2590 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2591 }
2592
2593
2594