mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-02 19:26:32 +00:00
sync with trunk (r49230) svn path=/branches/cmake-bringup/; revision=49246
2996 lines
77 KiB
C
2996 lines
77 KiB
C
/*
|
|
*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: dll/win32/kernel32/file/lang.c
|
|
* PURPOSE: National Laguage Support related funcs
|
|
* PROGRAMMER: Thomas Weidenmueller
|
|
* Gunnar Andre Dalsnes
|
|
* Aleksey Bragin
|
|
* Eric Kohl
|
|
* Alex Ionescu
|
|
* Richard Campbell
|
|
* James Tabor
|
|
* Dmitry Chapyshev
|
|
* UPDATE HISTORY:
|
|
* Created 21/09/2003
|
|
*/
|
|
|
|
#include <k32.h>
|
|
#include <winnls.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include "lcformat_private.h"
|
|
|
|
#define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
|
|
LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
|
|
#define CALINFO_MAX_YEAR 2029
|
|
|
|
//static LCID SystemLocale = MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
|
|
|
|
//static RTL_CRITICAL_SECTION LocalesListLock;
|
|
|
|
extern int wine_fold_string(int flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen);
|
|
extern int wine_get_sortkey(int flags, const WCHAR *src, int srclen, char *dst, int dstlen);
|
|
extern int wine_compare_string(int flags, const WCHAR *str1, int len1, const WCHAR *str2, int len2);
|
|
|
|
typedef struct
|
|
{
|
|
union
|
|
{
|
|
UILANGUAGE_ENUMPROCA procA;
|
|
UILANGUAGE_ENUMPROCW procW;
|
|
} u;
|
|
DWORD flags;
|
|
LONG_PTR param;
|
|
} ENUM_UILANG_CALLBACK;
|
|
|
|
static const WCHAR szLocaleKeyName[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\NLS\\Locale";
|
|
static const WCHAR szLangGroupsKeyName[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\NLS\\Language Groups";
|
|
|
|
/***********************************************************************
|
|
* is_genitive_name_supported
|
|
*
|
|
* Determine could LCTYPE basically support genitive name form or not.
|
|
*/
|
|
static BOOL is_genitive_name_supported( LCTYPE lctype )
|
|
{
|
|
switch(lctype & 0xffff)
|
|
{
|
|
case LOCALE_SMONTHNAME1:
|
|
case LOCALE_SMONTHNAME2:
|
|
case LOCALE_SMONTHNAME3:
|
|
case LOCALE_SMONTHNAME4:
|
|
case LOCALE_SMONTHNAME5:
|
|
case LOCALE_SMONTHNAME6:
|
|
case LOCALE_SMONTHNAME7:
|
|
case LOCALE_SMONTHNAME8:
|
|
case LOCALE_SMONTHNAME9:
|
|
case LOCALE_SMONTHNAME10:
|
|
case LOCALE_SMONTHNAME11:
|
|
case LOCALE_SMONTHNAME12:
|
|
case LOCALE_SMONTHNAME13:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* create_registry_key
|
|
*
|
|
* Create the Control Panel\\International registry key.
|
|
*/
|
|
static inline HANDLE create_registry_key(void)
|
|
{
|
|
static const WCHAR intlW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
|
|
'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING nameW;
|
|
HANDLE hkey;
|
|
|
|
if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
|
|
|
|
attr.Length = sizeof(attr);
|
|
attr.RootDirectory = hkey;
|
|
attr.ObjectName = &nameW;
|
|
attr.Attributes = 0;
|
|
attr.SecurityDescriptor = NULL;
|
|
attr.SecurityQualityOfService = NULL;
|
|
RtlInitUnicodeString( &nameW, intlW );
|
|
|
|
if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) hkey = 0;
|
|
NtClose( attr.RootDirectory );
|
|
return hkey;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* @implemented
|
|
* RIPPED FROM WINE's dlls\kernel\locale.c rev 1.42
|
|
*
|
|
* ConvertDefaultLocale (KERNEL32.@)
|
|
*
|
|
* Convert a default locale identifier into a real identifier.
|
|
*
|
|
* PARAMS
|
|
* lcid [I] LCID identifier of the locale to convert
|
|
*
|
|
* RETURNS
|
|
* lcid unchanged, if not a default locale or its sublanguage is
|
|
* not SUBLANG_NEUTRAL.
|
|
* GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
|
|
* GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
|
|
* Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
|
|
*/
|
|
LCID WINAPI
|
|
ConvertDefaultLocale(LCID lcid)
|
|
{
|
|
LANGID langid;
|
|
|
|
switch (lcid)
|
|
{
|
|
case LOCALE_SYSTEM_DEFAULT:
|
|
lcid = GetSystemDefaultLCID();
|
|
break;
|
|
|
|
case LOCALE_USER_DEFAULT:
|
|
case LOCALE_NEUTRAL:
|
|
lcid = GetUserDefaultLCID();
|
|
break;
|
|
|
|
default:
|
|
/* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
|
|
langid = LANGIDFROMLCID(lcid);
|
|
if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
|
|
{
|
|
langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
|
|
lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
|
|
}
|
|
}
|
|
|
|
return lcid;
|
|
}
|
|
|
|
|
|
static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
|
|
LPWSTR szValueName, ULONG valueNameSize,
|
|
LPWSTR szValueData, ULONG valueDataSize)
|
|
{
|
|
BYTE buffer[80];
|
|
KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
|
|
DWORD dwLen;
|
|
|
|
if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
|
|
buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
|
|
info->NameLength > valueNameSize ||
|
|
info->DataLength > valueDataSize)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DPRINT("info->Name %s info->DataLength %d\n", info->Name, info->DataLength);
|
|
|
|
memcpy( szValueName, info->Name, info->NameLength);
|
|
szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
|
|
memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
|
|
szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
|
|
|
|
DPRINT("returning %s %s\n", szValueName, szValueData);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
|
|
{
|
|
UNICODE_STRING keyName;
|
|
OBJECT_ATTRIBUTES attr;
|
|
HANDLE hkey;
|
|
|
|
RtlInitUnicodeString( &keyName, szKeyName );
|
|
InitializeObjectAttributes(&attr, &keyName, OBJ_CASE_INSENSITIVE, hRootKey, NULL);
|
|
|
|
if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS)
|
|
{
|
|
SetLastError( ERROR_BADDB );
|
|
hkey = 0;
|
|
}
|
|
|
|
return hkey;
|
|
}
|
|
|
|
static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
|
|
ULONG keyNameSize)
|
|
{
|
|
BYTE buffer[80];
|
|
KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
|
|
DWORD dwLen;
|
|
|
|
if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
|
|
sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
|
|
info->NameLength > keyNameSize)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DPRINT("info->Name %s info->NameLength %d\n", info->Name, info->NameLength);
|
|
|
|
memcpy( szKeyName, info->Name, info->NameLength);
|
|
szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
|
|
|
|
DPRINT("returning %s\n", szKeyName);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
|
|
{
|
|
BYTE buffer[128];
|
|
const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
|
|
DWORD dwSize = sizeof(buffer);
|
|
UNICODE_STRING valueName;
|
|
|
|
RtlInitUnicodeString( &valueName, szValueName );
|
|
|
|
DPRINT("%p, %s\n", hKey, szValueName);
|
|
if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
|
|
buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
|
|
info->DataLength == sizeof(DWORD))
|
|
{
|
|
memcpy(lpVal, info->Data, sizeof(DWORD));
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
|
|
{
|
|
LANGID langId;
|
|
LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
|
|
HRSRC hResource;
|
|
BOOL bRet = FALSE;
|
|
|
|
/* FIXME: Is it correct to use the system default langid? */
|
|
langId = GetSystemDefaultLangID();
|
|
|
|
if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
|
|
langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
|
|
|
|
hResource = FindResourceExW( hCurrentModule, (LPWSTR)RT_STRING, szResourceName, langId );
|
|
|
|
if (hResource)
|
|
{
|
|
HGLOBAL hResDir = LoadResource( hCurrentModule, hResource );
|
|
|
|
if (hResDir)
|
|
{
|
|
ULONG iResourceIndex = lgrpid & 0xf;
|
|
LPCWSTR lpResEntry = LockResource( hResDir );
|
|
ULONG i;
|
|
|
|
for (i = 0; i < iResourceIndex; i++)
|
|
lpResEntry += *lpResEntry + 1;
|
|
|
|
if (*lpResEntry < nameSize)
|
|
{
|
|
memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
|
|
szName[*lpResEntry] = '\0';
|
|
bRet = TRUE;
|
|
}
|
|
|
|
}
|
|
FreeResource( hResource );
|
|
}
|
|
else DPRINT1("FindResourceExW() failed\n");
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/* Callback function ptrs for EnumLanguageGrouplocalesA/W */
|
|
typedef struct
|
|
{
|
|
LANGGROUPLOCALE_ENUMPROCA procA;
|
|
LANGGROUPLOCALE_ENUMPROCW procW;
|
|
DWORD dwFlags;
|
|
LGRPID lgrpid;
|
|
LONG_PTR lParam;
|
|
} ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
|
|
|
|
/* Internal implementation of EnumLanguageGrouplocalesA/W */
|
|
static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
|
|
{
|
|
static const WCHAR szAlternateSortsKeyName[] = {
|
|
'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
|
|
};
|
|
WCHAR szNumber[10], szValue[4];
|
|
HANDLE hKey;
|
|
BOOL bContinue = TRUE, bAlternate = FALSE;
|
|
LGRPID lgrpid;
|
|
ULONG ulIndex = 1; /* Ignore default entry of 1st key */
|
|
|
|
if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (lpProcs->dwFlags)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
|
|
|
|
if (!hKey)
|
|
{
|
|
DPRINT1("NLS_RegOpenKey() failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
while (bContinue)
|
|
{
|
|
if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
|
|
szValue, sizeof(szValue) ))
|
|
{
|
|
lgrpid = wcstoul( szValue, NULL, 16 );
|
|
|
|
DPRINT("lcid %s, grpid %d (%smatched)\n", szNumber,
|
|
lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
|
|
|
|
if (lgrpid == lpProcs->lgrpid)
|
|
{
|
|
LCID lcid;
|
|
|
|
lcid = wcstoul( szNumber, NULL, 16 );
|
|
|
|
/* FIXME: native returns extra text for a few (17/150) locales, e.g:
|
|
* '00000437 ;Georgian'
|
|
* At present we only pass the LCID string.
|
|
*/
|
|
|
|
if (lpProcs->procW)
|
|
bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
|
|
else
|
|
{
|
|
char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
|
|
|
|
bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
|
|
}
|
|
}
|
|
|
|
ulIndex++;
|
|
}
|
|
else
|
|
{
|
|
/* Finished enumerating this key */
|
|
if (!bAlternate)
|
|
{
|
|
/* Enumerate alternate sorts also */
|
|
hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
|
|
bAlternate = TRUE;
|
|
ulIndex = 0;
|
|
}
|
|
else
|
|
bContinue = FALSE; /* Finished both keys */
|
|
}
|
|
|
|
if (!bContinue)
|
|
break;
|
|
}
|
|
|
|
if (hKey)
|
|
NtClose( hKey );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumLanguageGroupLocalesA(
|
|
LANGGROUPLOCALE_ENUMPROCA lpLangGroupLocaleEnumProc,
|
|
LGRPID LanguageGroup,
|
|
DWORD dwFlags,
|
|
LONG_PTR lParam)
|
|
{
|
|
ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
|
|
|
|
DPRINT("(%p,0x%08X,0x%08X,0x%08lX)\n", lpLangGroupLocaleEnumProc, LanguageGroup, dwFlags, lParam);
|
|
|
|
callbacks.procA = lpLangGroupLocaleEnumProc;
|
|
callbacks.procW = NULL;
|
|
callbacks.dwFlags = dwFlags;
|
|
callbacks.lgrpid = LanguageGroup;
|
|
callbacks.lParam = lParam;
|
|
|
|
return NLS_EnumLanguageGroupLocales( lpLangGroupLocaleEnumProc ? &callbacks : NULL );
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumLanguageGroupLocalesW(
|
|
LANGGROUPLOCALE_ENUMPROCW lpLangGroupLocaleEnumProc,
|
|
LGRPID LanguageGroup,
|
|
DWORD dwFlags,
|
|
LONG_PTR lParam)
|
|
{
|
|
ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
|
|
|
|
DPRINT("(%p,0x%08X,0x%08X,0x%08lX)\n", lpLangGroupLocaleEnumProc, LanguageGroup, dwFlags, lParam);
|
|
|
|
callbacks.procA = NULL;
|
|
callbacks.procW = lpLangGroupLocaleEnumProc;
|
|
callbacks.dwFlags = dwFlags;
|
|
callbacks.lgrpid = LanguageGroup;
|
|
callbacks.lParam = lParam;
|
|
|
|
return NLS_EnumLanguageGroupLocales( lpLangGroupLocaleEnumProc ? &callbacks : NULL );
|
|
}
|
|
|
|
|
|
/* Callback function ptrs for EnumSystemCodePagesA/W */
|
|
typedef struct
|
|
{
|
|
CODEPAGE_ENUMPROCA procA;
|
|
CODEPAGE_ENUMPROCW procW;
|
|
DWORD dwFlags;
|
|
} ENUMSYSTEMCODEPAGES_CALLBACKS;
|
|
|
|
/* Internal implementation of EnumSystemCodePagesA/W */
|
|
static BOOL NLS_EnumSystemCodePages(ENUMSYSTEMCODEPAGES_CALLBACKS *lpProcs)
|
|
{
|
|
WCHAR szNumber[5 + 1], szValue[MAX_PATH];
|
|
HANDLE hKey;
|
|
BOOL bContinue = TRUE;
|
|
ULONG ulIndex = 0;
|
|
|
|
if (!lpProcs)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (lpProcs->dwFlags)
|
|
{
|
|
case CP_INSTALLED:
|
|
case CP_SUPPORTED:
|
|
break;
|
|
default:
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
hKey = NLS_RegOpenKey(0, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\NLS\\CodePage");
|
|
if (!hKey)
|
|
{
|
|
DPRINT1("NLS_RegOpenKey() failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
while (bContinue)
|
|
{
|
|
if (NLS_RegEnumValue(hKey, ulIndex, szNumber, sizeof(szNumber),
|
|
szValue, sizeof(szValue)))
|
|
{
|
|
if ((lpProcs->dwFlags == CP_SUPPORTED)||
|
|
((lpProcs->dwFlags == CP_INSTALLED)&&(wcslen(szValue) > 2)))
|
|
{
|
|
if (lpProcs->procW)
|
|
{
|
|
bContinue = lpProcs->procW(szNumber);
|
|
}
|
|
else
|
|
{
|
|
char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
|
|
bContinue = lpProcs->procA(szNumberA);
|
|
}
|
|
}
|
|
|
|
ulIndex++;
|
|
|
|
} else bContinue = FALSE;
|
|
|
|
if (!bContinue)
|
|
break;
|
|
}
|
|
|
|
if (hKey)
|
|
NtClose(hKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumSystemCodePagesW (
|
|
CODEPAGE_ENUMPROCW lpCodePageEnumProc,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
ENUMSYSTEMCODEPAGES_CALLBACKS procs;
|
|
|
|
DPRINT("(%p,0x%08X,0x%08lX)\n", lpCodePageEnumProc, dwFlags);
|
|
|
|
procs.procA = NULL;
|
|
procs.procW = lpCodePageEnumProc;
|
|
procs.dwFlags = dwFlags;
|
|
|
|
return NLS_EnumSystemCodePages(lpCodePageEnumProc ? &procs : NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumSystemCodePagesA (
|
|
CODEPAGE_ENUMPROCA lpCodePageEnumProc,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
ENUMSYSTEMCODEPAGES_CALLBACKS procs;
|
|
|
|
DPRINT("(%p,0x%08X,0x%08lX)\n", lpCodePageEnumProc, dwFlags);
|
|
|
|
procs.procA = lpCodePageEnumProc;
|
|
procs.procW = NULL;
|
|
procs.dwFlags = dwFlags;
|
|
|
|
return NLS_EnumSystemCodePages(lpCodePageEnumProc ? &procs : NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumSystemGeoID(
|
|
GEOCLASS GeoClass,
|
|
GEOID ParentGeoId, // reserved
|
|
GEO_ENUMPROC lpGeoEnumProc)
|
|
{
|
|
WCHAR szNumber[5 + 1];
|
|
ULONG ulIndex = 0;
|
|
HANDLE hKey;
|
|
|
|
DPRINT("(0x%08X,0x%08X,%p)\n", GeoClass, ParentGeoId, lpGeoEnumProc);
|
|
|
|
if(!lpGeoEnumProc || GeoClass != GEOCLASS_NATION)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
hKey = NLS_RegOpenKey(0, L"\\REGISTRY\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Country List");
|
|
if (!hKey)
|
|
{
|
|
DPRINT1("NLS_RegOpenKey() failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
while (NLS_RegEnumSubKey(hKey, ulIndex, szNumber, sizeof(szNumber)))
|
|
{
|
|
BOOL bContinue = TRUE;
|
|
DWORD dwGeoId;
|
|
HANDLE hSubKey = NLS_RegOpenKey(hKey, szNumber);
|
|
|
|
if (hSubKey)
|
|
{
|
|
if (NLS_RegGetDword(hSubKey, L"CountryCode", &dwGeoId))
|
|
{
|
|
if (!lpGeoEnumProc(dwGeoId))
|
|
bContinue = FALSE;
|
|
}
|
|
|
|
NtClose(hSubKey);
|
|
}
|
|
|
|
if (!bContinue)
|
|
break;
|
|
|
|
ulIndex++;
|
|
}
|
|
|
|
if (hKey)
|
|
NtClose(hKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Callback function ptrs for EnumSystemLanguageGroupsA/W */
|
|
typedef struct
|
|
{
|
|
LANGUAGEGROUP_ENUMPROCA procA;
|
|
LANGUAGEGROUP_ENUMPROCW procW;
|
|
DWORD dwFlags;
|
|
LONG_PTR lParam;
|
|
} ENUMLANGUAGEGROUP_CALLBACKS;
|
|
|
|
|
|
/* Internal implementation of EnumSystemLanguageGroupsA/W */
|
|
static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
|
|
{
|
|
WCHAR szNumber[10], szValue[4];
|
|
HANDLE hKey;
|
|
BOOL bContinue = TRUE;
|
|
ULONG ulIndex = 0;
|
|
|
|
if (!lpProcs)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (lpProcs->dwFlags)
|
|
{
|
|
case 0:
|
|
/* Default to LGRPID_INSTALLED */
|
|
lpProcs->dwFlags = LGRPID_INSTALLED;
|
|
/* Fall through... */
|
|
case LGRPID_INSTALLED:
|
|
case LGRPID_SUPPORTED:
|
|
break;
|
|
default:
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
|
|
|
|
if (!hKey)
|
|
{
|
|
DPRINT1("NLS_RegOpenKey() failed, KeyName='%S'\n", szLangGroupsKeyName);
|
|
return FALSE;
|
|
}
|
|
|
|
while (bContinue)
|
|
{
|
|
if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
|
|
szValue, sizeof(szValue) ))
|
|
{
|
|
BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
|
|
LGRPID lgrpid = wcstoul( szNumber, NULL, 16 );
|
|
|
|
DPRINT("grpid %s (%sinstalled)\n", szNumber,
|
|
bInstalled ? "" : "not ");
|
|
|
|
if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
|
|
{
|
|
WCHAR szGrpName[48];
|
|
|
|
if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
|
|
szGrpName[0] = '\0';
|
|
|
|
if (lpProcs->procW)
|
|
bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
|
|
lpProcs->lParam );
|
|
else
|
|
{
|
|
char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
|
|
char szGrpNameA[48];
|
|
|
|
/* FIXME: MSDN doesn't say which code page the W->A translation uses,
|
|
* or whether the language names are ever localised. Assume CP_ACP.
|
|
*/
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
|
|
WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
|
|
|
|
bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
|
|
lpProcs->lParam );
|
|
}
|
|
}
|
|
|
|
ulIndex++;
|
|
}
|
|
else
|
|
bContinue = FALSE;
|
|
|
|
if (!bContinue)
|
|
break;
|
|
}
|
|
|
|
if (hKey)
|
|
NtClose( hKey );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumSystemLanguageGroupsA(
|
|
LANGUAGEGROUP_ENUMPROCA pLangGroupEnumProc,
|
|
DWORD dwFlags,
|
|
LONG_PTR lParam)
|
|
{
|
|
ENUMLANGUAGEGROUP_CALLBACKS procs;
|
|
|
|
DPRINT("(%p,0x%08X,0x%08lX)\n", pLangGroupEnumProc, dwFlags, lParam);
|
|
|
|
procs.procA = pLangGroupEnumProc;
|
|
procs.procW = NULL;
|
|
procs.dwFlags = dwFlags;
|
|
procs.lParam = lParam;
|
|
|
|
return NLS_EnumSystemLanguageGroups( pLangGroupEnumProc ? &procs : NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumSystemLanguageGroupsW(
|
|
LANGUAGEGROUP_ENUMPROCW pLangGroupEnumProc,
|
|
DWORD dwFlags,
|
|
LONG_PTR lParam)
|
|
{
|
|
ENUMLANGUAGEGROUP_CALLBACKS procs;
|
|
|
|
DPRINT("(%p,0x%08X,0x%08lX)\n", pLangGroupEnumProc, dwFlags, lParam);
|
|
|
|
procs.procA = NULL;
|
|
procs.procW = pLangGroupEnumProc;
|
|
procs.dwFlags = dwFlags;
|
|
procs.lParam = lParam;
|
|
|
|
return NLS_EnumSystemLanguageGroups( pLangGroupEnumProc ? &procs : NULL);
|
|
}
|
|
|
|
|
|
/* Callback function ptrs for EnumSystemLocalesA/W */
|
|
typedef struct
|
|
{
|
|
LOCALE_ENUMPROCA procA;
|
|
LOCALE_ENUMPROCW procW;
|
|
DWORD dwFlags;
|
|
} ENUMSYSTEMLOCALES_CALLBACKS;
|
|
|
|
|
|
/* Internal implementation of EnumSystemLocalesA/W */
|
|
static BOOL NLS_EnumSystemLocales(ENUMSYSTEMLOCALES_CALLBACKS *lpProcs)
|
|
{
|
|
WCHAR szNumber[10], szValue[4];
|
|
HANDLE hKey;
|
|
BOOL bContinue = TRUE;
|
|
ULONG ulIndex = 0;
|
|
|
|
if (!lpProcs)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Passing 0 flags behaves like LCID_SUPPORTED */
|
|
if (lpProcs->dwFlags == 0)
|
|
{
|
|
lpProcs->dwFlags = LCID_SUPPORTED;
|
|
}
|
|
|
|
switch (lpProcs->dwFlags)
|
|
{
|
|
case LCID_ALTERNATE_SORTS:
|
|
case LCID_INSTALLED:
|
|
case LCID_SUPPORTED:
|
|
break;
|
|
default:
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
hKey = NLS_RegOpenKey(0, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\Locale");
|
|
|
|
if (!hKey)
|
|
{
|
|
DPRINT1("NLS_RegOpenKey() failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
while (bContinue)
|
|
{
|
|
if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
|
|
szValue, sizeof(szValue)))
|
|
{
|
|
if ((lpProcs->dwFlags == LCID_SUPPORTED)||
|
|
((lpProcs->dwFlags == LCID_INSTALLED)&&(wcslen(szValue) > 0)))
|
|
{
|
|
if (lpProcs->procW)
|
|
{
|
|
bContinue = lpProcs->procW(szNumber);
|
|
}
|
|
else
|
|
{
|
|
char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
|
|
bContinue = lpProcs->procA(szNumberA);
|
|
}
|
|
}
|
|
|
|
ulIndex++;
|
|
}
|
|
else
|
|
bContinue = FALSE;
|
|
|
|
if (!bContinue)
|
|
break;
|
|
}
|
|
|
|
if (hKey)
|
|
NtClose(hKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumSystemLocalesA (
|
|
LOCALE_ENUMPROCA lpLocaleEnumProc,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
ENUMSYSTEMLOCALES_CALLBACKS procs;
|
|
|
|
DPRINT("(%p,0x%08X)\n", lpLocaleEnumProc, dwFlags);
|
|
|
|
procs.procA = lpLocaleEnumProc;
|
|
procs.procW = NULL;
|
|
procs.dwFlags = dwFlags;
|
|
|
|
return NLS_EnumSystemLocales(lpLocaleEnumProc ? &procs : NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumSystemLocalesW (
|
|
LOCALE_ENUMPROCW lpLocaleEnumProc,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
ENUMSYSTEMLOCALES_CALLBACKS procs;
|
|
|
|
DPRINT("(%p,0x%08X)\n", lpLocaleEnumProc, dwFlags);
|
|
|
|
procs.procA = NULL;
|
|
procs.procW = lpLocaleEnumProc;
|
|
procs.dwFlags = dwFlags;
|
|
|
|
return NLS_EnumSystemLocales(lpLocaleEnumProc ? &procs : NULL);
|
|
}
|
|
|
|
|
|
static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
|
|
LPCSTR name, WORD LangID, LONG_PTR lParam )
|
|
{
|
|
ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
|
|
char buf[20];
|
|
|
|
sprintf(buf, "%08x", (UINT)LangID);
|
|
return enum_uilang->u.procA( buf, enum_uilang->param );
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumUILanguagesA(
|
|
UILANGUAGE_ENUMPROCA lpUILanguageEnumProc,
|
|
DWORD dwFlags,
|
|
LONG_PTR lParam)
|
|
{
|
|
ENUM_UILANG_CALLBACK enum_uilang;
|
|
|
|
DPRINT("%p, %x, %lx\n", lpUILanguageEnumProc, dwFlags, lParam);
|
|
|
|
if(!lpUILanguageEnumProc) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if(dwFlags) {
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
enum_uilang.u.procA = lpUILanguageEnumProc;
|
|
enum_uilang.flags = dwFlags;
|
|
enum_uilang.param = lParam;
|
|
|
|
EnumResourceLanguagesA( hCurrentModule, (LPCSTR)RT_STRING,
|
|
(LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
|
|
(LONG_PTR)&enum_uilang);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
|
|
LPCWSTR name, WORD LangID, LONG_PTR lParam )
|
|
{
|
|
static const WCHAR formatW[] = {'%','0','8','x',0};
|
|
ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
|
|
WCHAR buf[20];
|
|
|
|
swprintf( buf, formatW, (UINT)LangID );
|
|
return enum_uilang->u.procW( buf, enum_uilang->param );
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
EnumUILanguagesW(
|
|
UILANGUAGE_ENUMPROCW lpUILanguageEnumProc,
|
|
DWORD dwFlags,
|
|
LONG_PTR lParam)
|
|
{
|
|
ENUM_UILANG_CALLBACK enum_uilang;
|
|
|
|
DPRINT("%p, %x, %lx\n", lpUILanguageEnumProc, dwFlags, lParam);
|
|
|
|
|
|
if(!lpUILanguageEnumProc) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if(dwFlags) {
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
enum_uilang.u.procW = lpUILanguageEnumProc;
|
|
enum_uilang.flags = dwFlags;
|
|
enum_uilang.param = lParam;
|
|
|
|
EnumResourceLanguagesW( hCurrentModule, (LPCWSTR)RT_STRING,
|
|
(LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
|
|
(LONG_PTR)&enum_uilang);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
GetCalendarInfoA(
|
|
LCID lcid,
|
|
CALID Calendar,
|
|
CALTYPE CalType,
|
|
LPSTR lpCalData,
|
|
int cchData,
|
|
LPDWORD lpValue
|
|
)
|
|
{
|
|
int ret;
|
|
LPWSTR lpCalDataW = NULL;
|
|
|
|
if (NLS_IsUnicodeOnlyLcid(lcid))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
if (cchData &&
|
|
!(lpCalDataW = HeapAlloc(GetProcessHeap(), 0, cchData*sizeof(WCHAR))))
|
|
return 0;
|
|
|
|
ret = GetCalendarInfoW(lcid, Calendar, CalType, lpCalDataW, cchData, lpValue);
|
|
if(ret && lpCalDataW && lpCalData)
|
|
WideCharToMultiByte(CP_ACP, 0, lpCalDataW, cchData, lpCalData, cchData, NULL, NULL);
|
|
HeapFree(GetProcessHeap(), 0, lpCalDataW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
GetCalendarInfoW(
|
|
LCID Locale,
|
|
CALID Calendar,
|
|
CALTYPE CalType,
|
|
LPWSTR lpCalData,
|
|
int cchData,
|
|
LPDWORD lpValue)
|
|
{
|
|
if (CalType & CAL_NOUSEROVERRIDE)
|
|
DPRINT("FIXME: flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
|
|
if (CalType & CAL_USE_CP_ACP)
|
|
DPRINT("FIXME: flag CAL_USE_CP_ACP used, not fully implemented\n");
|
|
|
|
if (CalType & CAL_RETURN_NUMBER) {
|
|
if (lpCalData != NULL)
|
|
DPRINT("WARNING: lpCalData not NULL (%p) when it should!\n", lpCalData);
|
|
if (cchData != 0)
|
|
DPRINT("WARNING: cchData not 0 (%d) when it should!\n", cchData);
|
|
} else {
|
|
if (lpValue != NULL)
|
|
DPRINT("WARNING: lpValue not NULL (%p) when it should!\n", lpValue);
|
|
}
|
|
|
|
/* FIXME: No verification is made yet wrt Locale
|
|
* for the CALTYPES not requiring GetLocaleInfoA */
|
|
switch (CalType & ~(CAL_NOUSEROVERRIDE|CAL_RETURN_NUMBER|CAL_USE_CP_ACP)) {
|
|
case CAL_ICALINTVALUE:
|
|
DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
|
|
return E_FAIL;
|
|
case CAL_SCALNAME:
|
|
DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
|
|
return E_FAIL;
|
|
case CAL_IYEAROFFSETRANGE:
|
|
DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
|
|
return E_FAIL;
|
|
case CAL_SERASTRING:
|
|
DPRINT("FIXME: Unimplemented caltype %d\n", CalType & 0xffff);
|
|
return E_FAIL;
|
|
case CAL_SSHORTDATE:
|
|
return GetLocaleInfoW(Locale, LOCALE_SSHORTDATE, lpCalData, cchData);
|
|
case CAL_SLONGDATE:
|
|
return GetLocaleInfoW(Locale, LOCALE_SLONGDATE, lpCalData, cchData);
|
|
case CAL_SDAYNAME1:
|
|
return GetLocaleInfoW(Locale, LOCALE_SDAYNAME1, lpCalData, cchData);
|
|
case CAL_SDAYNAME2:
|
|
return GetLocaleInfoW(Locale, LOCALE_SDAYNAME2, lpCalData, cchData);
|
|
case CAL_SDAYNAME3:
|
|
return GetLocaleInfoW(Locale, LOCALE_SDAYNAME3, lpCalData, cchData);
|
|
case CAL_SDAYNAME4:
|
|
return GetLocaleInfoW(Locale, LOCALE_SDAYNAME4, lpCalData, cchData);
|
|
case CAL_SDAYNAME5:
|
|
return GetLocaleInfoW(Locale, LOCALE_SDAYNAME5, lpCalData, cchData);
|
|
case CAL_SDAYNAME6:
|
|
return GetLocaleInfoW(Locale, LOCALE_SDAYNAME6, lpCalData, cchData);
|
|
case CAL_SDAYNAME7:
|
|
return GetLocaleInfoW(Locale, LOCALE_SDAYNAME7, lpCalData, cchData);
|
|
case CAL_SABBREVDAYNAME1:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME1, lpCalData, cchData);
|
|
case CAL_SABBREVDAYNAME2:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME2, lpCalData, cchData);
|
|
case CAL_SABBREVDAYNAME3:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME3, lpCalData, cchData);
|
|
case CAL_SABBREVDAYNAME4:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME4, lpCalData, cchData);
|
|
case CAL_SABBREVDAYNAME5:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME5, lpCalData, cchData);
|
|
case CAL_SABBREVDAYNAME6:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME6, lpCalData, cchData);
|
|
case CAL_SABBREVDAYNAME7:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME7, lpCalData, cchData);
|
|
case CAL_SMONTHNAME1:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME1, lpCalData, cchData);
|
|
case CAL_SMONTHNAME2:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME2, lpCalData, cchData);
|
|
case CAL_SMONTHNAME3:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME3, lpCalData, cchData);
|
|
case CAL_SMONTHNAME4:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME4, lpCalData, cchData);
|
|
case CAL_SMONTHNAME5:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME5, lpCalData, cchData);
|
|
case CAL_SMONTHNAME6:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME6, lpCalData, cchData);
|
|
case CAL_SMONTHNAME7:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME7, lpCalData, cchData);
|
|
case CAL_SMONTHNAME8:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME8, lpCalData, cchData);
|
|
case CAL_SMONTHNAME9:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME9, lpCalData, cchData);
|
|
case CAL_SMONTHNAME10:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME10, lpCalData, cchData);
|
|
case CAL_SMONTHNAME11:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME11, lpCalData, cchData);
|
|
case CAL_SMONTHNAME12:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME12, lpCalData, cchData);
|
|
case CAL_SMONTHNAME13:
|
|
return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME13, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME1:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME1, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME2:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME2, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME3:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME3, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME4:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME4, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME5:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME5, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME6:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME6, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME7:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME7, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME8:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME8, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME9:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME9, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME10:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME10, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME11:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME11, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME12:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME12, lpCalData, cchData);
|
|
case CAL_SABBREVMONTHNAME13:
|
|
return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME13, lpCalData, cchData);
|
|
case CAL_SYEARMONTH:
|
|
return GetLocaleInfoW(Locale, LOCALE_SYEARMONTH, lpCalData, cchData);
|
|
case CAL_ITWODIGITYEARMAX:
|
|
if (lpValue) *lpValue = CALINFO_MAX_YEAR;
|
|
break;
|
|
default: DPRINT("Unknown caltype %d\n",CalType & 0xffff);
|
|
return E_FAIL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetCPInfo(UINT CodePage,
|
|
LPCPINFO CodePageInfo)
|
|
{
|
|
PCODEPAGE_ENTRY CodePageEntry;
|
|
|
|
if (!CodePageInfo)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
CodePageEntry = IntGetCodePageEntry(CodePage);
|
|
if (CodePageEntry == NULL)
|
|
{
|
|
switch(CodePage)
|
|
{
|
|
case CP_UTF7:
|
|
case CP_UTF8:
|
|
CodePageInfo->DefaultChar[0] = 0x3f;
|
|
CodePageInfo->DefaultChar[1] = 0;
|
|
CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
|
|
CodePageInfo->MaxCharSize = (CodePage == CP_UTF7) ? 5 : 4;
|
|
return TRUE;
|
|
}
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if (CodePageEntry->CodePageTable.DefaultChar & 0xff00)
|
|
{
|
|
CodePageInfo->DefaultChar[0] = (CodePageEntry->CodePageTable.DefaultChar & 0xff00) >> 8;
|
|
CodePageInfo->DefaultChar[1] = CodePageEntry->CodePageTable.DefaultChar & 0x00ff;
|
|
}
|
|
else
|
|
{
|
|
CodePageInfo->DefaultChar[0] = CodePageEntry->CodePageTable.DefaultChar & 0xff;
|
|
CodePageInfo->DefaultChar[1] = 0;
|
|
}
|
|
|
|
if ((CodePageInfo->MaxCharSize = CodePageEntry->CodePageTable.MaximumCharacterSize) == 2)
|
|
memcpy(CodePageInfo->LeadByte, CodePageEntry->CodePageTable.LeadByte, sizeof(CodePageInfo->LeadByte));
|
|
else
|
|
CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
GetLocalisedText(DWORD dwResId, WCHAR *lpszDest)
|
|
{
|
|
HRSRC hrsrc;
|
|
LCID lcid;
|
|
LANGID langId;
|
|
DWORD dwId;
|
|
|
|
if (dwResId == 37)
|
|
dwId = dwResId * 100;
|
|
else
|
|
dwId = dwResId;
|
|
|
|
lcid = GetUserDefaultLCID();
|
|
lcid = ConvertDefaultLocale(lcid);
|
|
|
|
langId = LANGIDFROMLCID(lcid);
|
|
|
|
if (PRIMARYLANGID(langId) == LANG_NEUTRAL)
|
|
langId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
|
|
|
|
hrsrc = FindResourceExW(hCurrentModule,
|
|
(LPWSTR)RT_STRING,
|
|
MAKEINTRESOURCEW((dwId >> 4) + 1),
|
|
langId);
|
|
if (hrsrc)
|
|
{
|
|
HGLOBAL hmem = LoadResource(hCurrentModule, hrsrc);
|
|
|
|
if (hmem)
|
|
{
|
|
const WCHAR *p;
|
|
unsigned int i;
|
|
|
|
p = LockResource(hmem);
|
|
for (i = 0; i < (dwId & 0x0f); i++) p += *p + 1;
|
|
|
|
memcpy(lpszDest, p + 1, *p * sizeof(WCHAR));
|
|
lpszDest[*p] = '\0';
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
DPRINT1("Could not get codepage name. dwResId = %ld\n", dwResId);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetCPInfoExW(UINT CodePage,
|
|
DWORD dwFlags,
|
|
LPCPINFOEXW lpCPInfoEx)
|
|
{
|
|
if (!GetCPInfo(CodePage, (LPCPINFO) lpCPInfoEx))
|
|
return FALSE;
|
|
|
|
switch(CodePage)
|
|
{
|
|
case CP_UTF7:
|
|
{
|
|
lpCPInfoEx->CodePage = CP_UTF7;
|
|
lpCPInfoEx->UnicodeDefaultChar = 0x3f;
|
|
return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName);
|
|
}
|
|
break;
|
|
|
|
case CP_UTF8:
|
|
{
|
|
lpCPInfoEx->CodePage = CP_UTF8;
|
|
lpCPInfoEx->UnicodeDefaultChar = 0x3f;
|
|
return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName);
|
|
}
|
|
|
|
default:
|
|
{
|
|
PCODEPAGE_ENTRY CodePageEntry;
|
|
|
|
CodePageEntry = IntGetCodePageEntry(CodePage);
|
|
if (CodePageEntry == NULL)
|
|
{
|
|
DPRINT1("Could not get CodePage Entry! CodePageEntry = 0\n");
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
lpCPInfoEx->CodePage = CodePageEntry->CodePageTable.CodePage;
|
|
lpCPInfoEx->UnicodeDefaultChar = CodePageEntry->CodePageTable.UniDefaultChar;
|
|
return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetCPInfoExA(UINT CodePage,
|
|
DWORD dwFlags,
|
|
LPCPINFOEXA lpCPInfoEx)
|
|
{
|
|
CPINFOEXW CPInfo;
|
|
|
|
if (!GetCPInfoExW(CodePage, dwFlags, &CPInfo))
|
|
return FALSE;
|
|
|
|
/* the layout is the same except for CodePageName */
|
|
memcpy(lpCPInfoEx, &CPInfo, sizeof(CPINFOEXA));
|
|
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
CPInfo.CodePageName,
|
|
-1,
|
|
lpCPInfoEx->CodePageName,
|
|
sizeof(lpCPInfoEx->CodePageName),
|
|
NULL,
|
|
NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
NLS_GetGeoFriendlyName(GEOID Location, LPWSTR szFriendlyName, int cchData)
|
|
{
|
|
HANDLE hKey;
|
|
WCHAR szPath[MAX_PATH];
|
|
UNICODE_STRING ValueName;
|
|
KEY_VALUE_PARTIAL_INFORMATION *info;
|
|
static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
|
|
DWORD dwSize;
|
|
NTSTATUS Status;
|
|
int Ret;
|
|
|
|
swprintf(szPath, L"\\REGISTRY\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Country List\\%d", Location);
|
|
|
|
hKey = NLS_RegOpenKey(0, szPath);
|
|
if (!hKey)
|
|
{
|
|
DPRINT1("NLS_RegOpenKey() failed\n");
|
|
return 0;
|
|
}
|
|
|
|
dwSize = info_size + cchData * sizeof(WCHAR);
|
|
|
|
if (!(info = HeapAlloc(GetProcessHeap(), 0, dwSize)))
|
|
{
|
|
NtClose(hKey);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
RtlInitUnicodeString(&ValueName, L"Name");
|
|
|
|
Status = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation,
|
|
(LPBYTE)info, dwSize, &dwSize);
|
|
|
|
if (!Status)
|
|
{
|
|
Ret = (dwSize - info_size) / sizeof(WCHAR);
|
|
|
|
if (!Ret || ((WCHAR *)info->Data)[Ret-1])
|
|
{
|
|
if (Ret < cchData || !szFriendlyName) Ret++;
|
|
else
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
Ret = 0;
|
|
}
|
|
}
|
|
|
|
if (Ret && szFriendlyName)
|
|
{
|
|
memcpy(szFriendlyName, info->Data, (Ret-1) * sizeof(WCHAR));
|
|
szFriendlyName[Ret-1] = 0;
|
|
}
|
|
}
|
|
else if (Status == STATUS_BUFFER_OVERFLOW && !szFriendlyName)
|
|
{
|
|
Ret = (dwSize - info_size) / sizeof(WCHAR) + 1;
|
|
}
|
|
else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
Ret = -1;
|
|
}
|
|
else
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
Ret = 0;
|
|
}
|
|
|
|
NtClose(hKey);
|
|
HeapFree(GetProcessHeap(), 0, info);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
GetGeoInfoW(
|
|
GEOID Location,
|
|
GEOTYPE GeoType,
|
|
LPWSTR lpGeoData,
|
|
int cchData,
|
|
LANGID LangId)
|
|
{
|
|
DPRINT("%d %d %p %d %d\n", Location, GeoType, lpGeoData, cchData, LangId);
|
|
|
|
if ((GeoType == GEO_TIMEZONES)||(GeoType == GEO_OFFICIALLANGUAGES))
|
|
{
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
switch (GeoType)
|
|
{
|
|
case GEO_FRIENDLYNAME:
|
|
{
|
|
return NLS_GetGeoFriendlyName(Location, lpGeoData, cchData);
|
|
}
|
|
case GEO_NATION:
|
|
case GEO_LATITUDE:
|
|
case GEO_LONGITUDE:
|
|
case GEO_ISO2:
|
|
case GEO_ISO3:
|
|
case GEO_RFC1766:
|
|
case GEO_LCID:
|
|
case GEO_OFFICIALNAME:
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
GetGeoInfoA(
|
|
GEOID Location,
|
|
GEOTYPE GeoType,
|
|
LPSTR lpGeoData,
|
|
int cchData,
|
|
LANGID LangId)
|
|
{
|
|
DPRINT("%d %d %p %d %d\n", Location, GeoType, lpGeoData, cchData, LangId);
|
|
|
|
if ((GeoType == GEO_TIMEZONES)||(GeoType == GEO_OFFICIALLANGUAGES))
|
|
{
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
switch (GeoType)
|
|
{
|
|
case GEO_FRIENDLYNAME:
|
|
{
|
|
WCHAR szBuffer[MAX_PATH];
|
|
char szBufferA[sizeof(szBuffer)/sizeof(WCHAR)];
|
|
int Ret;
|
|
|
|
Ret = NLS_GetGeoFriendlyName(Location, szBuffer, cchData);
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szBuffer, -1, szBufferA, sizeof(szBufferA), 0, 0);
|
|
strcpy(lpGeoData, szBufferA);
|
|
|
|
return Ret;
|
|
}
|
|
case GEO_NATION:
|
|
case GEO_LATITUDE:
|
|
case GEO_LONGITUDE:
|
|
case GEO_ISO2:
|
|
case GEO_ISO3:
|
|
case GEO_RFC1766:
|
|
case GEO_LCID:
|
|
case GEO_OFFICIALNAME:
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const WCHAR *RosGetLocaleValueName( DWORD lctype )
|
|
{
|
|
switch (lctype & ~LOCALE_LOCALEINFOFLAGSMASK)
|
|
{
|
|
/* These values are used by SetLocaleInfo and GetLocaleInfo, and
|
|
* the values are stored in the registry, confirmed under Windows.
|
|
*/
|
|
case LOCALE_ICALENDARTYPE: return L"iCalendarType";
|
|
case LOCALE_ICURRDIGITS: return L"iCurrDigits";
|
|
case LOCALE_ICURRENCY: return L"iCurrency";
|
|
case LOCALE_IDIGITS: return L"iDigits";
|
|
case LOCALE_IFIRSTDAYOFWEEK: return L"iFirstDayOfWeek";
|
|
case LOCALE_IFIRSTWEEKOFYEAR: return L"iFirstWeekOfYear";
|
|
case LOCALE_ILZERO: return L"iLZero";
|
|
case LOCALE_IMEASURE: return L"iMeasure";
|
|
case LOCALE_INEGCURR: return L"iNegCurr";
|
|
case LOCALE_INEGNUMBER: return L"iNegNumber";
|
|
case LOCALE_IPAPERSIZE: return L"iPaperSize";
|
|
case LOCALE_ITIME: return L"iTime";
|
|
case LOCALE_S1159: return L"s1159";
|
|
case LOCALE_S2359: return L"s2359";
|
|
case LOCALE_SCURRENCY: return L"sCurrency";
|
|
case LOCALE_SDATE: return L"sDate";
|
|
case LOCALE_SDECIMAL: return L"sDecimal";
|
|
case LOCALE_SGROUPING: return L"sGrouping";
|
|
case LOCALE_SLIST: return L"sList";
|
|
case LOCALE_SLONGDATE: return L"sLongDate";
|
|
case LOCALE_SMONDECIMALSEP: return L"sMonDecimalSep";
|
|
case LOCALE_SMONGROUPING: return L"sMonGrouping";
|
|
case LOCALE_SMONTHOUSANDSEP: return L"sMonThousandSep";
|
|
case LOCALE_SNEGATIVESIGN: return L"sNegativeSign";
|
|
case LOCALE_SPOSITIVESIGN: return L"sPositiveSign";
|
|
case LOCALE_SSHORTDATE: return L"sShortDate";
|
|
case LOCALE_STHOUSAND: return L"sThousand";
|
|
case LOCALE_STIME: return L"sTime";
|
|
case LOCALE_STIMEFORMAT: return L"sTimeFormat";
|
|
case LOCALE_SYEARMONTH: return L"sYearMonth";
|
|
|
|
/* The following are not listed under MSDN as supported,
|
|
* but seem to be used and also stored in the registry.
|
|
*/
|
|
case LOCALE_ICOUNTRY: return L"iCountry";
|
|
case LOCALE_IDATE: return L"iDate";
|
|
case LOCALE_ILDATE: return L"iLDate";
|
|
case LOCALE_ITLZERO: return L"iTLZero";
|
|
case LOCALE_SCOUNTRY: return L"sCountry";
|
|
case LOCALE_SLANGUAGE: return L"sLanguage";
|
|
|
|
/* The following are used in XP and later */
|
|
case LOCALE_IDIGITSUBSTITUTION: return L"NumShape";
|
|
case LOCALE_SNATIVEDIGITS: return L"sNativeDigits";
|
|
case LOCALE_ITIMEMARKPOSN: return L"iTimePrefix";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
HKEY RosCreateRegistryKey(void)
|
|
{
|
|
OBJECT_ATTRIBUTES objAttr;
|
|
UNICODE_STRING nameW;
|
|
HANDLE hKey;
|
|
|
|
if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hKey ) != STATUS_SUCCESS) return 0;
|
|
|
|
objAttr.Length = sizeof(objAttr);
|
|
objAttr.RootDirectory = hKey;
|
|
objAttr.ObjectName = &nameW;
|
|
objAttr.Attributes = 0;
|
|
objAttr.SecurityDescriptor = NULL;
|
|
objAttr.SecurityQualityOfService = NULL;
|
|
RtlInitUnicodeString( &nameW, L"Control Panel\\International");
|
|
|
|
if (NtCreateKey( &hKey, KEY_ALL_ACCESS, &objAttr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) hKey = 0;
|
|
NtClose( objAttr.RootDirectory );
|
|
return hKey;
|
|
}
|
|
|
|
INT RosGetRegistryLocaleInfo( LPCWSTR lpValue, LPWSTR lpBuffer, INT nLen )
|
|
{
|
|
DWORD dwSize;
|
|
HKEY hKey;
|
|
INT nRet;
|
|
NTSTATUS ntStatus;
|
|
UNICODE_STRING usNameW;
|
|
KEY_VALUE_PARTIAL_INFORMATION *kvpiInfo;
|
|
const int nInfoSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
|
|
|
|
if (!(hKey = RosCreateRegistryKey())) return -1;
|
|
|
|
RtlInitUnicodeString( &usNameW, lpValue );
|
|
dwSize = nInfoSize + nLen * sizeof(WCHAR);
|
|
|
|
if (!(kvpiInfo = HeapAlloc( GetProcessHeap(), 0, dwSize )))
|
|
{
|
|
NtClose( hKey );
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return 0;
|
|
}
|
|
|
|
ntStatus = NtQueryValueKey( hKey, &usNameW, KeyValuePartialInformation, kvpiInfo, dwSize, &dwSize );
|
|
|
|
if (!ntStatus)
|
|
{
|
|
nRet = (dwSize - nInfoSize) / sizeof(WCHAR);
|
|
|
|
if (!nRet || ((WCHAR *)kvpiInfo->Data)[nRet - 1])
|
|
{
|
|
if (nRet < nLen || !lpBuffer) nRet++;
|
|
else
|
|
{
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
nRet = 0;
|
|
}
|
|
}
|
|
if (nRet && lpBuffer)
|
|
{
|
|
memcpy( lpBuffer, kvpiInfo->Data, (nRet - 1) * sizeof(WCHAR) );
|
|
lpBuffer[nRet - 1] = 0;
|
|
}
|
|
}
|
|
else if (ntStatus == STATUS_BUFFER_OVERFLOW && !lpBuffer)
|
|
{
|
|
nRet = (dwSize - nInfoSize) / sizeof(WCHAR) + 1;
|
|
}
|
|
else if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
nRet = -1;
|
|
}
|
|
else
|
|
{
|
|
SetLastError( RtlNtStatusToDosError(ntStatus) );
|
|
nRet = 0;
|
|
}
|
|
NtClose( hKey );
|
|
HeapFree( GetProcessHeap(), 0, kvpiInfo );
|
|
return nRet;
|
|
}
|
|
|
|
int
|
|
WINAPI
|
|
GetLocaleInfoEx (
|
|
LPCWSTR lpLocaleName,
|
|
LCTYPE LCType,
|
|
LPWSTR lpLCData,
|
|
int cchData
|
|
)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
GetLocaleInfoW (
|
|
LCID Locale,
|
|
LCTYPE LCType,
|
|
LPWSTR lpLCData,
|
|
int cchData
|
|
)
|
|
{
|
|
LANGID liLangID;
|
|
HRSRC hRsrc;
|
|
HGLOBAL hMem;
|
|
HMODULE hModule;
|
|
INT nRet;
|
|
UINT uiFlags;
|
|
const WCHAR *ch;
|
|
int i;
|
|
|
|
if (cchData < 0 || (cchData && !lpLCData))
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return 0;
|
|
}
|
|
if (LCType & LOCALE_RETURN_GENITIVE_NAMES &&
|
|
!is_genitive_name_supported( LCType ))
|
|
{
|
|
SetLastError( ERROR_INVALID_FLAGS );
|
|
return 0;
|
|
}
|
|
|
|
if (!cchData) lpLCData = NULL;
|
|
|
|
if (Locale == LOCALE_NEUTRAL || Locale == LOCALE_SYSTEM_DEFAULT) Locale = GetSystemDefaultLCID();
|
|
else if (Locale == LOCALE_USER_DEFAULT) Locale = GetUserDefaultLCID();
|
|
|
|
uiFlags = LCType & LOCALE_LOCALEINFOFLAGSMASK;
|
|
LCType &= ~LOCALE_LOCALEINFOFLAGSMASK;
|
|
|
|
if (!(uiFlags & LOCALE_NOUSEROVERRIDE) && Locale == GetUserDefaultLCID())
|
|
{
|
|
const WCHAR *value = RosGetLocaleValueName(LCType);
|
|
|
|
if (value && ((nRet = RosGetRegistryLocaleInfo( value, lpLCData, cchData )) != -1)) return nRet;
|
|
}
|
|
|
|
liLangID = LANGIDFROMLCID( Locale );
|
|
|
|
if (SUBLANGID(liLangID) == SUBLANG_NEUTRAL)
|
|
liLangID = MAKELANGID(PRIMARYLANGID(liLangID), SUBLANG_DEFAULT);
|
|
|
|
hModule = GetModuleHandleW( L"kernel32.dll" );
|
|
if (!(hRsrc = FindResourceExW( hModule, (LPWSTR)RT_STRING, (LPCWSTR)((LCType >> 4) + 1), liLangID )))
|
|
{
|
|
SetLastError( ERROR_INVALID_FLAGS );
|
|
return 0;
|
|
}
|
|
if (!(hMem = LoadResource( hModule, hRsrc )))
|
|
return 0;
|
|
|
|
ch = LockResource( hMem );
|
|
for (i = 0; i < (LCType & 0x0f); i++) ch += *ch + 1;
|
|
|
|
if (uiFlags & LOCALE_RETURN_NUMBER) nRet = sizeof(UINT) / sizeof(WCHAR);
|
|
else if (is_genitive_name_supported( LCType ) && *ch)
|
|
{
|
|
/* genitive form's stored after a null separator from a nominative */
|
|
for (i = 1; i <= *ch; i++) if (!ch[i]) break;
|
|
|
|
if (i <= *ch && (uiFlags & LOCALE_RETURN_GENITIVE_NAMES))
|
|
{
|
|
nRet = *ch - i + 1;
|
|
ch += i;
|
|
}
|
|
else nRet = i;
|
|
}
|
|
else
|
|
nRet = (LCType == LOCALE_FONTSIGNATURE) ? *ch : *ch + 1;
|
|
|
|
if (!lpLCData) return nRet;
|
|
|
|
if (nRet > cchData)
|
|
{
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
return 0;
|
|
}
|
|
|
|
if (uiFlags & LOCALE_RETURN_NUMBER)
|
|
{
|
|
UINT uiNum;
|
|
WCHAR *chEnd, *chTmp = HeapAlloc( GetProcessHeap(), 0, (*ch + 1) * sizeof(WCHAR) );
|
|
|
|
if (!chTmp)
|
|
return 0;
|
|
|
|
memcpy( chTmp, ch + 1, *ch * sizeof(WCHAR) );
|
|
chTmp[*ch] = L'\0';
|
|
uiNum = wcstol( chTmp, &chEnd, 10 );
|
|
|
|
if (!*chEnd)
|
|
memcpy( lpLCData, &uiNum, sizeof(uiNum) );
|
|
else
|
|
{
|
|
SetLastError( ERROR_INVALID_FLAGS );
|
|
nRet = 0;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, chTmp );
|
|
}
|
|
else
|
|
{
|
|
memcpy( lpLCData, ch + 1, nRet * sizeof(WCHAR) );
|
|
if (LCType != LOCALE_FONTSIGNATURE) lpLCData[nRet-1] = 0;
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
* get_lcid_codepage
|
|
*
|
|
* Retrieve the ANSI codepage for a given locale.
|
|
*/
|
|
__inline static UINT get_lcid_codepage( LCID lcid )
|
|
{
|
|
UINT ret;
|
|
if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
|
|
sizeof(ret)/sizeof(WCHAR) )) ret = 0;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* FoldStringA (KERNEL32.@)
|
|
*
|
|
* Map characters in a string.
|
|
*
|
|
* PARAMS
|
|
* dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
|
|
* src [I] String to map
|
|
* srclen [I] Length of src, or -1 if src is NUL terminated
|
|
* dst [O] Destination for mapped string
|
|
* dstlen [I] Length of dst, or 0 to find the required length for the mapped string
|
|
*
|
|
* RETURNS
|
|
* Success: The length of the string written to dst, including the terminating NUL. If
|
|
* dstlen is 0, the value returned is the same, but nothing is written to dst,
|
|
* and dst may be NULL.
|
|
* Failure: 0. Use GetLastError() to determine the cause.
|
|
*/
|
|
INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
|
|
LPSTR dst, INT dstlen)
|
|
{
|
|
INT ret = 0, srclenW = 0;
|
|
WCHAR *srcW = NULL, *dstW = NULL;
|
|
|
|
if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
|
|
src, srclen, NULL, 0);
|
|
srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
|
|
|
|
if (!srcW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto FoldStringA_exit;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
|
|
src, srclen, srcW, srclenW);
|
|
|
|
dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
|
|
|
|
ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
|
|
if (ret && dstlen)
|
|
{
|
|
dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
|
|
|
|
if (!dstW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto FoldStringA_exit;
|
|
}
|
|
|
|
ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
|
|
if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
|
|
{
|
|
ret = 0;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, dstW);
|
|
|
|
FoldStringA_exit:
|
|
HeapFree(GetProcessHeap(), 0, srcW);
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* FoldStringW (KERNEL32.@)
|
|
*
|
|
* See FoldStringA.
|
|
*/
|
|
INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
|
|
LPWSTR dst, INT dstlen)
|
|
{
|
|
int ret;
|
|
|
|
switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
|
|
{
|
|
case 0:
|
|
if (dwFlags)
|
|
break;
|
|
/* Fall through for dwFlags == 0 */
|
|
case MAP_PRECOMPOSED|MAP_COMPOSITE:
|
|
case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
|
|
case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return 0;
|
|
}
|
|
|
|
if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
|
|
if (!ret)
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented (Synced to Wine-22112008)
|
|
*/
|
|
int
|
|
WINAPI
|
|
CompareStringA (
|
|
LCID Locale,
|
|
DWORD dwCmpFlags,
|
|
LPCSTR lpString1,
|
|
int cchCount1,
|
|
LPCSTR lpString2,
|
|
int cchCount2
|
|
)
|
|
{
|
|
WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
|
|
WCHAR *buf2W = buf1W + 130;
|
|
LPWSTR str1W, str2W;
|
|
INT len1W, len2W, ret;
|
|
UINT locale_cp = CP_ACP;
|
|
|
|
if (!lpString1 || !lpString2)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
if (cchCount1 < 0) cchCount1 = strlen(lpString1);
|
|
if (cchCount2 < 0) cchCount2 = strlen(lpString2);
|
|
|
|
if (!(dwCmpFlags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage(Locale);
|
|
|
|
len1W = MultiByteToWideChar(locale_cp, 0, lpString1, cchCount1, buf1W, 130);
|
|
if (len1W)
|
|
str1W = buf1W;
|
|
else
|
|
{
|
|
len1W = MultiByteToWideChar(locale_cp, 0, lpString1, cchCount1, NULL, 0);
|
|
str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
|
|
if (!str1W)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
MultiByteToWideChar(locale_cp, 0, lpString1, cchCount1, str1W, len1W);
|
|
}
|
|
len2W = MultiByteToWideChar(locale_cp, 0, lpString2, cchCount2, buf2W, 130);
|
|
if (len2W)
|
|
str2W = buf2W;
|
|
else
|
|
{
|
|
len2W = MultiByteToWideChar(locale_cp, 0, lpString2, cchCount2, NULL, 0);
|
|
str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
|
|
if (!str2W)
|
|
{
|
|
if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
MultiByteToWideChar(locale_cp, 0, lpString2, cchCount2, str2W, len2W);
|
|
}
|
|
|
|
ret = CompareStringW(Locale, dwCmpFlags, str1W, len1W, str2W, len2W);
|
|
|
|
if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
|
|
if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented (Synced to Wine-22/11/2008)
|
|
*/
|
|
int
|
|
WINAPI
|
|
CompareStringW (
|
|
LCID Locale,
|
|
DWORD dwCmpFlags,
|
|
LPCWSTR lpString1,
|
|
int cchCount1,
|
|
LPCWSTR lpString2,
|
|
int cchCount2
|
|
)
|
|
{
|
|
INT Result;
|
|
|
|
if (!lpString1 || !lpString2)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
if (dwCmpFlags & ~(NORM_IGNORECASE | NORM_IGNORENONSPACE |
|
|
NORM_IGNORESYMBOLS | SORT_STRINGSORT | NORM_IGNOREKANATYPE |
|
|
NORM_IGNOREWIDTH | LOCALE_USE_CP_ACP | 0x10000000))
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return 0;
|
|
}
|
|
|
|
/* this style is related to diacritics in Arabic, Japanese, and Hebrew */
|
|
if (dwCmpFlags & 0x10000000)
|
|
DPRINT1("Ignoring unknown style 0x10000000\n");
|
|
|
|
if (cchCount1 < 0) cchCount1 = wcslen(lpString1);
|
|
if (cchCount2 < 0) cchCount2 = wcslen(lpString2);
|
|
|
|
Result = wine_compare_string(dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);
|
|
|
|
if (Result) /* need to translate result */
|
|
return (Result < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
|
|
|
|
return CSTR_EQUAL;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*
|
|
* Get information about an aspect of a locale.
|
|
*
|
|
* PARAMS
|
|
* lcid [I] LCID of the locale
|
|
* lctype [I] LCTYPE_ flags from "winnls.h"
|
|
* buffer [O] Destination for the information
|
|
* len [I] Length of buffer in characters
|
|
*
|
|
* RETURNS
|
|
* Success: The size of the data requested. If buffer is non-NULL, it is filled
|
|
* with the information.
|
|
* Failure: 0. Use GetLastError() to determine the cause.
|
|
*
|
|
* NOTES
|
|
* - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
|
|
* - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
|
|
* which is a bit string.
|
|
*/
|
|
INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
|
|
{
|
|
WCHAR *bufferW;
|
|
INT lenW, ret;
|
|
|
|
if (len < 0 || (len && !buffer))
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return 0;
|
|
}
|
|
if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
|
|
{
|
|
SetLastError( ERROR_INVALID_FLAGS );
|
|
return 0;
|
|
}
|
|
|
|
if (!len) buffer = NULL;
|
|
|
|
if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
|
|
|
|
if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return 0;
|
|
}
|
|
if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
|
|
{
|
|
if ((lctype & LOCALE_RETURN_NUMBER) ||
|
|
((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
|
|
{
|
|
/* it's not an ASCII string, just bytes */
|
|
ret *= sizeof(WCHAR);
|
|
if (buffer)
|
|
{
|
|
if (ret <= len) memcpy( buffer, bufferW, ret );
|
|
else
|
|
{
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
ret = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UINT codepage = CP_ACP;
|
|
if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
|
|
ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
|
|
}
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, bufferW );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LANGID WINAPI
|
|
GetSystemDefaultLangID(VOID)
|
|
{
|
|
return LANGIDFROMLCID(GetSystemDefaultLCID());
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LCID WINAPI
|
|
GetSystemDefaultLCID(VOID)
|
|
{
|
|
LCID lcid;
|
|
|
|
NtQueryDefaultLocale(FALSE, &lcid);
|
|
|
|
return lcid;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LANGID WINAPI
|
|
GetSystemDefaultUILanguage(VOID)
|
|
{
|
|
LANGID LanguageId;
|
|
NTSTATUS Status;
|
|
|
|
Status = NtQueryInstallUILanguage(&LanguageId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastErrorByStatus(Status);
|
|
return 0;
|
|
}
|
|
|
|
return LanguageId;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LCID WINAPI
|
|
GetThreadLocale(VOID)
|
|
{
|
|
return NtCurrentTeb()->CurrentLocale;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LANGID WINAPI
|
|
GetUserDefaultLangID(VOID)
|
|
{
|
|
return LANGIDFROMLCID(GetUserDefaultLCID());
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LCID WINAPI
|
|
GetUserDefaultLCID(VOID)
|
|
{
|
|
LCID lcid;
|
|
NTSTATUS Status;
|
|
|
|
Status = NtQueryDefaultLocale(TRUE, &lcid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastErrorByStatus(Status);
|
|
return 0;
|
|
}
|
|
|
|
return lcid;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LANGID WINAPI
|
|
GetUserDefaultUILanguage(VOID)
|
|
{
|
|
LANGID LangId;
|
|
NTSTATUS Status;
|
|
|
|
Status = NtQueryDefaultUILanguage(&LangId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastErrorByStatus(Status);
|
|
return 0;
|
|
}
|
|
|
|
return LangId;
|
|
}
|
|
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
GEOID
|
|
WINAPI
|
|
GetUserGeoID(
|
|
GEOCLASS GeoClass)
|
|
{
|
|
GEOID ret = GEOID_NOT_AVAILABLE;
|
|
static const WCHAR geoW[] = {'G','e','o',0};
|
|
static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
|
|
WCHAR bufferW[40], *end;
|
|
DWORD count;
|
|
HANDLE hkey, hSubkey = 0;
|
|
UNICODE_STRING keyW;
|
|
const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
|
|
RtlInitUnicodeString( &keyW, nationW );
|
|
count = sizeof(bufferW);
|
|
|
|
if(!(hkey = create_registry_key())) return ret;
|
|
|
|
switch( GeoClass ){
|
|
case GEOCLASS_NATION:
|
|
if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
|
|
{
|
|
if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
|
|
(LPBYTE)bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
|
|
ret = wcstol((LPCWSTR)info->Data, &end, 10);
|
|
}
|
|
break;
|
|
case GEOCLASS_REGION:
|
|
DPRINT("GEOCLASS_REGION not handled yet\n");
|
|
break;
|
|
}
|
|
|
|
NtClose(hkey);
|
|
if (hSubkey) NtClose(hSubkey);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* IsValidLanguageGroup
|
|
*
|
|
* Determine if a language group is supported and/or installed.
|
|
*
|
|
* PARAMS
|
|
* LanguageGroup [I] Language Group Id (LGRPID_ values from "winnls.h")
|
|
* dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
|
|
*
|
|
* RETURNS
|
|
* TRUE, if lgrpid is supported and/or installed, according to dwFlags.
|
|
* FALSE otherwise.
|
|
*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
IsValidLanguageGroup(
|
|
LGRPID LanguageGroup,
|
|
DWORD dwFlags)
|
|
{
|
|
static const WCHAR szFormat[] = { '%','x','\0' };
|
|
WCHAR szValueName[16], szValue[2];
|
|
BOOL bSupported = FALSE, bInstalled = FALSE;
|
|
HANDLE hKey;
|
|
|
|
|
|
switch (dwFlags)
|
|
{
|
|
case LGRPID_INSTALLED:
|
|
case LGRPID_SUPPORTED:
|
|
|
|
hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
|
|
if (!hKey)
|
|
break;
|
|
|
|
swprintf( szValueName, szFormat, LanguageGroup );
|
|
|
|
if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
|
|
{
|
|
bSupported = TRUE;
|
|
|
|
if (szValue[0] == '1')
|
|
bInstalled = TRUE;
|
|
}
|
|
|
|
NtClose( hKey );
|
|
|
|
break;
|
|
|
|
default:
|
|
DPRINT("Invalid flags: %lx\n", dwFlags);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
|
|
(dwFlags == LGRPID_INSTALLED && bInstalled))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* IsValidLocale
|
|
*
|
|
* Determine if a locale is valid.
|
|
*
|
|
* PARAMS
|
|
* Locale [I] LCID of the locale to check
|
|
* dwFlags [I] LCID_SUPPORTED = Valid
|
|
* LCID_INSTALLED = Valid and installed on the system
|
|
*
|
|
* RETURN
|
|
* TRUE, if Locale is valid,
|
|
* FALSE, otherwise.
|
|
*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
IsValidLocale(LCID Locale,
|
|
DWORD dwFlags)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
|
|
WCHAR ValueNameBuffer[9];
|
|
UNICODE_STRING KeyName;
|
|
UNICODE_STRING ValueName;
|
|
ULONG KeyInfoSize;
|
|
ULONG ReturnedSize;
|
|
HANDLE KeyHandle;
|
|
PWSTR ValueData;
|
|
NTSTATUS Status;
|
|
BOOL Installed = FALSE;
|
|
|
|
DPRINT("IsValidLocale() called\n");
|
|
|
|
if ((dwFlags & ~(LCID_SUPPORTED | LCID_INSTALLED)) ||
|
|
(dwFlags == (LCID_SUPPORTED | LCID_INSTALLED)))
|
|
{
|
|
DPRINT("Invalid flags: %lx\n", dwFlags);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Locale & 0xFFFF0000)
|
|
{
|
|
RtlInitUnicodeString(&KeyName,
|
|
L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Nls\\Locale\\Alternate Sorts");
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&KeyName,
|
|
L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Nls\\Locale");
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("NtOpenKey() failed (Status %lx)\n", Status);
|
|
SetLastError(ERROR_BADDB);
|
|
return FALSE;
|
|
}
|
|
|
|
swprintf(ValueNameBuffer, L"%08lx", (ULONG)Locale);
|
|
RtlInitUnicodeString(&ValueName, ValueNameBuffer);
|
|
|
|
KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4 * sizeof(WCHAR);
|
|
KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
KeyInfoSize);
|
|
if (KeyInfo == NULL)
|
|
{
|
|
DPRINT("RtlAllocateHeap() failed (Status %lx)\n", Status);
|
|
NtClose(KeyHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
Status = NtQueryValueKey(KeyHandle,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
KeyInfo,
|
|
KeyInfoSize,
|
|
&ReturnedSize);
|
|
NtClose(KeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("NtQueryValueKey() failed (Status %lx)\n", Status);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
|
|
return FALSE;
|
|
}
|
|
|
|
if (dwFlags & LCID_SUPPORTED)
|
|
{
|
|
DPRINT("Locale is supported\n");
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
|
|
return TRUE;
|
|
}
|
|
|
|
ValueData = (PWSTR)&KeyInfo->Data[0];
|
|
if ((KeyInfo->Type == REG_SZ) &&
|
|
(KeyInfo->DataLength == 2 * sizeof(WCHAR)))
|
|
{
|
|
/* Find out if there is support for the language group
|
|
* installed, to which this language belongs */
|
|
KeyHandle = NLS_RegOpenKey(0, szLangGroupsKeyName);
|
|
if (KeyHandle)
|
|
{
|
|
WCHAR Value[2];
|
|
if (NLS_RegGetDword(KeyHandle, ValueData, (LPDWORD) Value) &&
|
|
Value[0] == L'1')
|
|
{
|
|
Installed = TRUE;
|
|
DPRINT("Locale is supported and installed\n");
|
|
}
|
|
|
|
NtClose(KeyHandle);
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
|
|
|
|
DPRINT("IsValidLocale() called\n");
|
|
|
|
return Installed;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
LCMapStringA (
|
|
LCID Locale,
|
|
DWORD dwMapFlags,
|
|
LPCSTR lpSrcStr,
|
|
int cchSrc,
|
|
LPSTR lpDestStr,
|
|
int cchDest
|
|
)
|
|
{
|
|
WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
|
|
LPWSTR srcW, dstW;
|
|
INT ret = 0, srclenW, dstlenW;
|
|
UINT locale_cp = CP_ACP;
|
|
|
|
if (!lpSrcStr || !cchSrc || cchDest < 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
if (!(dwMapFlags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage(Locale);
|
|
|
|
srclenW = MultiByteToWideChar(locale_cp, 0, lpSrcStr, cchSrc, bufW, 260);
|
|
if (srclenW)
|
|
srcW = bufW;
|
|
else
|
|
{
|
|
srclenW = MultiByteToWideChar(locale_cp, 0, lpSrcStr, cchSrc, NULL, 0);
|
|
srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
|
|
if (!srcW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
MultiByteToWideChar(locale_cp, 0, lpSrcStr, cchSrc, srcW, srclenW);
|
|
}
|
|
|
|
if (dwMapFlags & LCMAP_SORTKEY)
|
|
{
|
|
if (lpSrcStr == lpDestStr)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
goto map_string_exit;
|
|
}
|
|
ret = wine_get_sortkey(dwMapFlags, srcW, srclenW, lpDestStr, cchDest);
|
|
if (ret == 0)
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
else
|
|
ret++;
|
|
goto map_string_exit;
|
|
}
|
|
|
|
if (dwMapFlags & SORT_STRINGSORT)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
goto map_string_exit;
|
|
}
|
|
|
|
dstlenW = LCMapStringW(Locale, dwMapFlags, srcW, srclenW, NULL, 0);
|
|
if (!dstlenW)
|
|
goto map_string_exit;
|
|
|
|
dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
|
|
if (!dstW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto map_string_exit;
|
|
}
|
|
|
|
LCMapStringW(Locale, dwMapFlags, srcW, srclenW, dstW, dstlenW);
|
|
ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, lpDestStr, cchDest, NULL, NULL);
|
|
HeapFree(GetProcessHeap(), 0, dstW);
|
|
|
|
map_string_exit:
|
|
if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
LCMapStringW (
|
|
LCID Locale,
|
|
DWORD dwMapFlags,
|
|
LPCWSTR lpSrcStr,
|
|
int cchSrc,
|
|
LPWSTR lpDestStr,
|
|
int cchDest
|
|
)
|
|
{
|
|
LPWSTR dst_ptr;
|
|
|
|
if (!lpSrcStr || !cchSrc || cchDest < 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
/* mutually exclusive flags */
|
|
if ((dwMapFlags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
|
|
(dwMapFlags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
|
|
(dwMapFlags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
|
|
(dwMapFlags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return 0;
|
|
}
|
|
|
|
if (!cchDest) lpDestStr = NULL;
|
|
|
|
Locale = ConvertDefaultLocale(Locale);
|
|
|
|
if (dwMapFlags & LCMAP_SORTKEY)
|
|
{
|
|
INT ret;
|
|
if (lpSrcStr == lpDestStr)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return 0;
|
|
}
|
|
|
|
if (cchSrc < 0) cchSrc = wcslen(lpSrcStr);
|
|
|
|
ret = wine_get_sortkey(dwMapFlags, lpSrcStr, cchSrc, (char *)lpDestStr, cchDest);
|
|
if (ret == 0)
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
/* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
|
|
if (dwMapFlags & SORT_STRINGSORT)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return 0;
|
|
}
|
|
|
|
if (cchSrc < 0) cchSrc = wcslen(lpSrcStr) + 1;
|
|
|
|
if (!lpDestStr) /* return required string length */
|
|
{
|
|
INT len;
|
|
|
|
for (len = 0; cchSrc; lpSrcStr++, cchSrc--)
|
|
{
|
|
WCHAR wch = *lpSrcStr;
|
|
/* tests show that win2k just ignores NORM_IGNORENONSPACE,
|
|
* and skips white space and punctuation characters for
|
|
* NORM_IGNORESYMBOLS.
|
|
*/
|
|
if ((dwMapFlags & NORM_IGNORESYMBOLS) && (iswctype(wch, _SPACE | _PUNCT)))
|
|
continue;
|
|
len++;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
if (dwMapFlags & LCMAP_UPPERCASE)
|
|
{
|
|
for (dst_ptr = lpDestStr; cchSrc && cchDest; lpSrcStr++, cchSrc--)
|
|
{
|
|
WCHAR wch = *lpSrcStr;
|
|
if ((dwMapFlags & NORM_IGNORESYMBOLS) && (iswctype(wch, _SPACE | _PUNCT)))
|
|
continue;
|
|
*dst_ptr++ = towupper(wch);
|
|
cchDest--;
|
|
}
|
|
}
|
|
else if (dwMapFlags & LCMAP_LOWERCASE)
|
|
{
|
|
for (dst_ptr = lpDestStr; cchSrc && cchDest; lpSrcStr++, cchSrc--)
|
|
{
|
|
WCHAR wch = *lpSrcStr;
|
|
if ((dwMapFlags & NORM_IGNORESYMBOLS) && (iswctype(wch, _SPACE | _PUNCT)))
|
|
continue;
|
|
*dst_ptr++ = towlower(wch);
|
|
cchDest--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (lpSrcStr == lpDestStr)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return 0;
|
|
}
|
|
for (dst_ptr = lpDestStr; cchSrc && cchDest; lpSrcStr++, cchSrc--)
|
|
{
|
|
WCHAR wch = *lpSrcStr;
|
|
if ((dwMapFlags & NORM_IGNORESYMBOLS) && (iswctype(wch, _SPACE | _PUNCT)))
|
|
continue;
|
|
*dst_ptr++ = wch;
|
|
cchDest--;
|
|
}
|
|
}
|
|
|
|
if (cchSrc)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return 0;
|
|
}
|
|
|
|
return dst_ptr - lpDestStr;
|
|
}
|
|
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetCalendarInfoA(
|
|
LCID Locale,
|
|
CALID Calendar,
|
|
CALTYPE CalType,
|
|
LPCSTR lpCalData)
|
|
{
|
|
if (!Locale || !lpCalData)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (CalType)
|
|
{
|
|
case CAL_NOUSEROVERRIDE:
|
|
case CAL_RETURN_NUMBER:
|
|
case CAL_USE_CP_ACP:
|
|
break;
|
|
default:
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetCalendarInfoW(
|
|
LCID Locale,
|
|
CALID Calendar,
|
|
CALTYPE CalType,
|
|
LPCWSTR lpCalData)
|
|
{
|
|
if (!Locale || !lpCalData)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (CalType)
|
|
{
|
|
case CAL_NOUSEROVERRIDE:
|
|
case CAL_RETURN_NUMBER:
|
|
case CAL_USE_CP_ACP:
|
|
break;
|
|
default:
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* @implemented
|
|
* RIPPED FROM WINE's dlls\kernel\locale.c ver 0.9.29
|
|
*
|
|
* SetLocaleInfoA (KERNEL32.@)
|
|
*
|
|
* Set the current locale info.
|
|
*
|
|
* PARAMS
|
|
* Locale [I] LCID of the locale
|
|
* LCType [I] LCTYPE_ flags from "winnls.h"
|
|
* lpLCData [I] Information to set
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE. The information given will be returned by GetLocaleInfoA()
|
|
* whenever it is called without LOCALE_NOUSEROVERRIDE.
|
|
* Failure: FALSE. Use GetLastError() to determine the cause.
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetLocaleInfoA (
|
|
LCID Locale,
|
|
LCTYPE LCType,
|
|
LPCSTR lpLCData
|
|
)
|
|
{
|
|
UINT codepage = CP_ACP;
|
|
WCHAR *strW;
|
|
DWORD len;
|
|
BOOL ret;
|
|
|
|
if (!(LCType & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( Locale );
|
|
|
|
if (!lpLCData)
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
len = MultiByteToWideChar( codepage, 0, lpLCData, -1, NULL, 0 );
|
|
if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return FALSE;
|
|
}
|
|
MultiByteToWideChar( codepage, 0, lpLCData, -1, strW, len );
|
|
ret = SetLocaleInfoW( Locale, LCType, strW );
|
|
HeapFree( GetProcessHeap(), 0, strW );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* @implemented
|
|
* RIPPED FROM WINE's dlls\kernel\locale.c ver 0.9.29
|
|
*
|
|
* SetLocaleInfoW (KERNEL32.@)
|
|
*
|
|
* See SetLocaleInfoA.
|
|
*
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetLocaleInfoW (
|
|
LCID Locale,
|
|
LCTYPE LCType,
|
|
LPCWSTR lpLCData
|
|
)
|
|
{
|
|
const WCHAR *value;
|
|
UNICODE_STRING valueW;
|
|
NTSTATUS status;
|
|
HANDLE hkey;
|
|
|
|
LCType &= 0xffff;
|
|
value = RosGetLocaleValueName( LCType );
|
|
|
|
if (!lpLCData || !value)
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if (LCType == LOCALE_IDATE || LCType == LOCALE_ILDATE)
|
|
{
|
|
SetLastError( ERROR_INVALID_FLAGS );
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(hkey = RosCreateRegistryKey())) return FALSE;
|
|
RtlInitUnicodeString( &valueW, value );
|
|
status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, (PVOID)lpLCData, (lstrlenW(lpLCData)+1)*sizeof(WCHAR) );
|
|
|
|
if (LCType == LOCALE_SSHORTDATE || LCType == LOCALE_SLONGDATE)
|
|
{
|
|
/* Set I-value from S value */
|
|
WCHAR *lpD, *lpM, *lpY;
|
|
WCHAR szBuff[2];
|
|
|
|
lpD = wcschr(lpLCData, 'd');
|
|
lpM = wcschr(lpLCData, 'M');
|
|
lpY = wcschr(lpLCData, 'y');
|
|
|
|
if (lpD <= lpM)
|
|
{
|
|
szBuff[0] = '1'; /* D-M-Y */
|
|
}
|
|
else
|
|
{
|
|
if (lpY <= lpM)
|
|
szBuff[0] = '2'; /* Y-M-D */
|
|
else
|
|
szBuff[0] = '0'; /* M-D-Y */
|
|
}
|
|
|
|
szBuff[1] = '\0';
|
|
|
|
if (LCType == LOCALE_SSHORTDATE)
|
|
LCType = LOCALE_IDATE;
|
|
else
|
|
LCType = LOCALE_ILDATE;
|
|
|
|
value = RosGetLocaleValueName( LCType );
|
|
|
|
RtlInitUnicodeString( &valueW, value );
|
|
status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
|
|
if (status) SetLastError( RtlNtStatusToDosError(status) );
|
|
return !status;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* @implemented
|
|
* RIPPED FROM WINE's dlls\kernel\locale.c rev 1.42
|
|
*
|
|
* SetThreadLocale (KERNEL32.@)
|
|
*
|
|
* Set the current threads locale.
|
|
*
|
|
* PARAMS
|
|
* lcid [I] LCID of the locale to set
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE. The threads locale is set to lcid.
|
|
* Failure: FALSE. Use GetLastError() to determine the cause.
|
|
*
|
|
*/
|
|
BOOL WINAPI SetThreadLocale( LCID lcid )
|
|
{
|
|
DPRINT("SetThreadLocale(0x%04lX)\n", lcid);
|
|
|
|
lcid = ConvertDefaultLocale(lcid);
|
|
|
|
if (lcid != GetThreadLocale())
|
|
{
|
|
if (!IsValidLocale(lcid, LCID_SUPPORTED))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
NtCurrentTeb()->CurrentLocale = lcid;
|
|
/* FIXME: NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
|
|
* Wine save the acp for easy/fast access, but ROS has no such Teb member.
|
|
* Maybe add this member to ros as well?
|
|
*/
|
|
|
|
/*
|
|
Lag test app for � se om locale etc, endres i en app. etter at prosessen er
|
|
startet, eller om bare nye prosesser blir ber�rt.
|
|
*/
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
SetUserDefaultLCID(LCID lcid)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtSetDefaultLocale(TRUE, lcid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastErrorByStatus(Status);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
SetUserDefaultUILanguage(LANGID LangId)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtSetDefaultUILanguage(LangId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastErrorByStatus(Status);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetUserGeoID(
|
|
GEOID GeoId)
|
|
{
|
|
static const WCHAR geoW[] = {'G','e','o',0};
|
|
static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
|
|
static const WCHAR formatW[] = {'%','i',0};
|
|
UNICODE_STRING nameW,keyW;
|
|
WCHAR bufferW[10];
|
|
OBJECT_ATTRIBUTES attr;
|
|
HANDLE hkey;
|
|
|
|
if(!(hkey = create_registry_key())) return FALSE;
|
|
|
|
attr.Length = sizeof(attr);
|
|
attr.RootDirectory = hkey;
|
|
attr.ObjectName = &nameW;
|
|
attr.Attributes = 0;
|
|
attr.SecurityDescriptor = NULL;
|
|
attr.SecurityQualityOfService = NULL;
|
|
RtlInitUnicodeString( &nameW, geoW );
|
|
RtlInitUnicodeString( &keyW, nationW );
|
|
|
|
if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
|
|
|
|
{
|
|
NtClose(attr.RootDirectory);
|
|
return FALSE;
|
|
}
|
|
|
|
swprintf(bufferW, formatW, GeoId);
|
|
NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (wcslen(bufferW) + 1) * sizeof(WCHAR));
|
|
NtClose(attr.RootDirectory);
|
|
NtClose(hkey);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
VerLanguageNameA (
|
|
DWORD wLang,
|
|
LPSTR szLang,
|
|
DWORD nSize
|
|
)
|
|
{
|
|
return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
VerLanguageNameW (
|
|
DWORD wLang,
|
|
LPWSTR szLang,
|
|
DWORD nSize
|
|
)
|
|
{
|
|
return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* LCIDToLocaleName (KERNEL32.@) Wine 13.02.2009
|
|
*/
|
|
INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
|
|
{
|
|
if (flags) DPRINT1( "unsupported flags %x\n", flags );
|
|
|
|
return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
|
|
}
|