You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6276 lines
194 KiB
6276 lines
194 KiB
/*++
|
|
|
|
Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
locale.c
|
|
|
|
Abstract:
|
|
|
|
This file contains functions that return information about a
|
|
language group, a UI language, a locale, or a calendar.
|
|
|
|
APIs found in this file:
|
|
IsValidLanguageGroup
|
|
IsValidLocale
|
|
IsValidUILanguage
|
|
ConvertDefaultLocale
|
|
GetThreadLocale
|
|
SetThreadLocale
|
|
SetThreadUILanguage
|
|
GetSystemDefaultUILanguage
|
|
GetUserDefaultUILanguage
|
|
GetSystemDefaultLangID
|
|
GetUserDefaultLangID
|
|
GetSystemDefaultLCID
|
|
GetUserDefaultLCID
|
|
VerLanguageNameW
|
|
VerLanguageNameA
|
|
GetLocaleInfoW
|
|
SetLocaleInfoW
|
|
GetCalendarInfoW
|
|
SetCalendarInfoW
|
|
|
|
Revision History:
|
|
|
|
05-31-91 JulieB Created.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
//
|
|
// Include Files.
|
|
//
|
|
|
|
#include "nls.h"
|
|
#include "nlssafe.h"
|
|
|
|
|
|
|
|
//
|
|
// Allow this file to build without warnings when the DUnicode switch
|
|
// is turned off.
|
|
//
|
|
#undef MAKEINTRESOURCE
|
|
#define MAKEINTRESOURCE MAKEINTRESOURCEW
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NLS_STRING_TO_INTEGER
|
|
//
|
|
// Converts a string to an integer value.
|
|
//
|
|
// DEFINED AS A MACRO.
|
|
//
|
|
// 10-19-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define NLS_STRING_TO_INTEGER( CalData, \
|
|
pCalData ) \
|
|
{ \
|
|
UNICODE_STRING ObUnicodeStrCalData; /* value string */ \
|
|
\
|
|
\
|
|
/* \
|
|
* No need to check return value since the calendar number \
|
|
* will be validated after this anyway. \
|
|
*/ \
|
|
RtlInitUnicodeString(&ObUnicodeStrCalData, pCalData); \
|
|
RtlUnicodeStringToInteger(&ObUnicodeStrCalData, 10, &CalData); \
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Global Variables.
|
|
//
|
|
|
|
LCID gProcessLocale;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Forward Declarations.
|
|
//
|
|
|
|
BOOL
|
|
SetUserInfo(
|
|
LCTYPE LCType,
|
|
LPWSTR pData,
|
|
ULONG DataLength);
|
|
|
|
BOOL SetCurrentUserRegValue(
|
|
LCTYPE LCType,
|
|
LPWSTR pData,
|
|
ULONG DataLength);
|
|
|
|
BOOL
|
|
SetMultipleUserInfo(
|
|
DWORD dwFlags,
|
|
int cchData,
|
|
LPCWSTR pPicture,
|
|
LPCWSTR pSeparator,
|
|
LPCWSTR pOrder,
|
|
LPCWSTR pTLZero,
|
|
LPCWSTR pTimeMarkPosn);
|
|
|
|
|
|
BOOL
|
|
SetTwoDigitYearInfo(
|
|
CALID Calendar,
|
|
LPCWSTR pYearInfo,
|
|
int cchData);
|
|
|
|
void
|
|
GetInstallLanguageFromRegistry();
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// PRIVATE API ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsResetProcessLocale
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void NlsResetProcessLocale(void)
|
|
{
|
|
|
|
//
|
|
// If the thread isn't impersonating, then re-read the process locale
|
|
// from the current user's registry.
|
|
//
|
|
if (NtCurrentTeb()->IsImpersonating == 0L)
|
|
{
|
|
NlsFlushProcessCache(LOCALE_SLOCALE);
|
|
NlsGetUserLocale(&gProcessLocale);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// API ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidLanguageGroup
|
|
//
|
|
// Determines whether or not a language group is installed in the system
|
|
// if the LGRPID_INSTALLED flag is set, or whether or not a language group
|
|
// is supported in the system if the LGRPID_SUPPORTED flag is set.
|
|
//
|
|
// 03-10-98 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI IsValidLanguageGroup(
|
|
LGRPID LanguageGroup,
|
|
DWORD dwFlags)
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueFull;
|
|
BYTE pStatic[MAX_KEY_VALUE_FULLINFO];
|
|
|
|
WCHAR pTmpBuf[MAX_PATH]; // temp buffer
|
|
UNICODE_STRING ObUnicodeStr; // registry data value string
|
|
LPWSTR pData; // ptr to registry data
|
|
|
|
|
|
//
|
|
// Invalid Flags Check:
|
|
// - flags other than valid ones
|
|
// - more than one of either supported or installed
|
|
//
|
|
if ((dwFlags & IVLG_INVALID_FLAG) ||
|
|
(MORE_THAN_ONE(dwFlags, IVLG_SINGLE_FLAG)))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Open the Language Groups registry key.
|
|
//
|
|
OPEN_LANG_GROUPS_KEY(FALSE);
|
|
|
|
//
|
|
// Convert language group value to Unicode string.
|
|
//
|
|
if (NlsConvertIntegerToString(LanguageGroup, 16, 1, pTmpBuf, MAX_PATH))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Query the registry for the value.
|
|
//
|
|
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
|
|
if ((QueryRegValue( hLangGroupsKey,
|
|
pTmpBuf,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL ) != NO_ERROR))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Language Group is SUPPORTED. If the INSTALLED flag is NOT set, then
|
|
// return success.
|
|
//
|
|
if (!(dwFlags & LGRPID_INSTALLED))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Need to find out if it's installed.
|
|
//
|
|
if (pKeyValueFull->DataLength > 2)
|
|
{
|
|
pData = GET_VALUE_DATA_PTR(pKeyValueFull);
|
|
if ((pData[0] == L'1') && (pData[1] == 0))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return result.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidLocale
|
|
//
|
|
// Determines whether or not a locale is installed in the system if the
|
|
// LCID_INSTALLED flag is set, or whether or not a locale is supported in
|
|
// the system if the LCID_SUPPORTED flag is set.
|
|
//
|
|
// 07-26-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI IsValidLocale(
|
|
LCID Locale,
|
|
DWORD dwFlags)
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueFull;
|
|
BYTE pStatic1[MAX_KEY_VALUE_FULLINFO];
|
|
BYTE pStatic2[MAX_KEY_VALUE_FULLINFO];
|
|
|
|
WCHAR pTmpBuf[MAX_PATH]; // temp buffer
|
|
UNICODE_STRING ObUnicodeStr; // registry data value string
|
|
DWORD Data; // registry data value
|
|
LPWSTR pData; // ptr to registry data
|
|
BOOL bResult = FALSE; // result value
|
|
|
|
|
|
//
|
|
// Invalid Flags Check:
|
|
// - flags other than valid ones
|
|
// - more than one of either supported or installed
|
|
//
|
|
if ((dwFlags & IVL_INVALID_FLAG) ||
|
|
(MORE_THAN_ONE(dwFlags, IVL_SINGLE_FLAG)))
|
|
{
|
|
//
|
|
// The ME release of NT 4 did a really bad thing and allowed 0x39
|
|
// to be passed in as a valid flag value for Arabic and Hebrew.
|
|
// As a result, we need to allow this flag combination for
|
|
// the Arabic and Hebrew locales.
|
|
//
|
|
if ((dwFlags == 0x39) &&
|
|
((Locale == MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT)) ||
|
|
(Locale == MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT))))
|
|
{
|
|
dwFlags = LCID_INSTALLED;
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Invalid Locale Check.
|
|
//
|
|
if (IS_INVALID_LOCALE(Locale))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// See if the LOCALE information is in the system for the
|
|
// given locale.
|
|
//
|
|
if (GetLocHashNode(Locale) == NULL)
|
|
{
|
|
//
|
|
// Return failure.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Locale is SUPPORTED. If the INSTALLED flag is NOT set, then
|
|
// return success.
|
|
//
|
|
if (!(dwFlags & LCID_INSTALLED))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Open the Locale, the Alternate Sorts, and the Language Groups
|
|
// registry keys.
|
|
//
|
|
OPEN_LOCALE_KEY(FALSE);
|
|
OPEN_ALT_SORTS_KEY(FALSE);
|
|
OPEN_LANG_GROUPS_KEY(FALSE);
|
|
|
|
//
|
|
// Convert locale value to Unicode string.
|
|
//
|
|
if (NlsConvertIntegerToString(Locale, 16, 8, pTmpBuf, MAX_PATH))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Query the registry for the value.
|
|
//
|
|
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic1;
|
|
if (((QueryRegValue( hLocaleKey,
|
|
pTmpBuf,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL ) == NO_ERROR) ||
|
|
(QueryRegValue( hAltSortsKey,
|
|
pTmpBuf,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL ) == NO_ERROR)) &&
|
|
(pKeyValueFull->DataLength > 2))
|
|
{
|
|
RtlInitUnicodeString(&ObUnicodeStr, GET_VALUE_DATA_PTR(pKeyValueFull));
|
|
if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 16, &Data) == NO_ERROR) &&
|
|
(Data != 0))
|
|
{
|
|
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic2;
|
|
if ((QueryRegValue( hLangGroupsKey,
|
|
ObUnicodeStr.Buffer,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL ) == NO_ERROR) &&
|
|
(pKeyValueFull->DataLength > 2))
|
|
{
|
|
pData = GET_VALUE_DATA_PTR(pKeyValueFull);
|
|
if ((pData[0] == L'1') && (pData[1] == 0))
|
|
{
|
|
bResult = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return result.
|
|
//
|
|
return (bResult);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidUILanguage
|
|
//
|
|
// Determines whether or not the specified UI language is installed in the system.
|
|
//
|
|
//
|
|
// 12-03-00 YSLin Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI IsValidUILanguage(LANGID UILangID)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING KeyPath, KeyValueName;
|
|
HANDLE Key;
|
|
WCHAR UILangIDStr[5];
|
|
WCHAR KeyValueBuffer[ 128 ];
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
|
ULONG ResultLength;
|
|
|
|
ULONG Value = 0, Digit, i;
|
|
WCHAR c;
|
|
|
|
BOOL Result = FALSE;
|
|
|
|
RtlInitUnicodeString(&KeyPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\MUILanguages");
|
|
|
|
if (!NlsConvertIntegerToHexStringW(UILangID, FALSE, UILangIDStr, sizeof(UILangIDStr)/sizeof(WCHAR)))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
RtlInitUnicodeString(&KeyValueName, UILangIDStr);
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(NtOpenKey (&Key, KEY_READ, &ObjectAttributes)))
|
|
{
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
|
|
Status = NtQueryValueKey(Key,
|
|
&KeyValueName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof( KeyValueBuffer ),
|
|
&ResultLength
|
|
);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (KeyValueInformation->Type == REG_SZ && *((PWSTR)(KeyValueInformation->Data)) == L'1')
|
|
{
|
|
Result = TRUE;
|
|
}
|
|
}
|
|
NtClose(Key);
|
|
}
|
|
return (Result);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ConvertDefaultLocale
|
|
//
|
|
// Converts any of the special case locale values to an actual locale id.
|
|
// If none of the special case locales was given, the given locale id
|
|
// is returned.
|
|
//
|
|
// 09-01-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LCID WINAPI ConvertDefaultLocale(
|
|
LCID Locale)
|
|
{
|
|
//
|
|
// Check for the special locale values.
|
|
//
|
|
CHECK_SPECIAL_LOCALES(Locale, FALSE);
|
|
|
|
//
|
|
// Return the locale id.
|
|
//
|
|
return (Locale);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetThreadLocale
|
|
//
|
|
// Returns the locale id for the current thread.
|
|
//
|
|
// 03-11-93 JulieB Moved from base\client.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LCID WINAPI GetThreadLocale()
|
|
{
|
|
//
|
|
// Return the locale id stored in the TEB.
|
|
//
|
|
return ((LCID)(NtCurrentTeb()->CurrentLocale));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetThreadLocale
|
|
//
|
|
// Resets the locale id for the current thread. Any locale-dependent
|
|
// functions will reflect the new locale. If the locale passed in is
|
|
// not a valid locale id, then FALSE is returned.
|
|
//
|
|
// 03-11-93 JulieB Moved from base\client; Added Locale Validation.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI SetThreadLocale(
|
|
LCID Locale)
|
|
{
|
|
PLOC_HASH pHashN; // ptr to hash node
|
|
|
|
|
|
//
|
|
// Validate locale id.
|
|
//
|
|
VALIDATE_LANGUAGE(Locale, pHashN, 0, FALSE);
|
|
if (pHashN == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the locale id in the TEB.
|
|
//
|
|
NtCurrentTeb()->CurrentLocale = (ULONG)Locale;
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetThreadUILanguage
|
|
//
|
|
// This routine sets the thread UI language based on the console codepage.
|
|
//
|
|
// 9-29-00 WeiWu Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LANGID WINAPI SetThreadUILanguage(
|
|
WORD wReserved)
|
|
{
|
|
//
|
|
// Cache system locale and CP info
|
|
//
|
|
static LCID s_lidSystem = 0;
|
|
static UINT s_uiSysCp = 0;
|
|
static UINT s_uiSysOEMCp = 0;
|
|
|
|
UINT uiUserUICp;
|
|
UINT uiUserUIOEMCp;
|
|
WCHAR szData[16];
|
|
|
|
LANGID lidUserUI = GetUserDefaultUILanguage();
|
|
LCID lcidThreadOld = GetThreadLocale();
|
|
|
|
//
|
|
// Set default thread locale to EN-US
|
|
//
|
|
// This allow us to fall back to English UI to avoid trashed characters
|
|
// when console doesn't meet the criteria of rendering native UI.
|
|
//
|
|
LCID lcidThread = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
|
|
UINT uiConsoleCp = GetConsoleOutputCP();
|
|
|
|
//
|
|
// Make sure nobody uses it yet
|
|
//
|
|
ASSERT(wReserved == 0);
|
|
|
|
//
|
|
// Get cached system locale and CP info.
|
|
//
|
|
if (!s_uiSysCp)
|
|
{
|
|
LCID lcidSystem = GetSystemDefaultLCID();
|
|
|
|
if (lcidSystem)
|
|
{
|
|
//
|
|
// Get ANSI CP
|
|
//
|
|
GetLocaleInfoW(lcidSystem, LOCALE_IDEFAULTANSICODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
|
|
NlsConvertStringToIntegerW(szData, 10, -1, &s_uiSysCp);
|
|
|
|
//
|
|
// Get OEM CP
|
|
//
|
|
GetLocaleInfoW(lcidSystem, LOCALE_IDEFAULTCODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
|
|
NlsConvertStringToIntegerW(szData, 10, -1, &s_uiSysOEMCp);
|
|
|
|
//
|
|
// Cache system primary langauge
|
|
//
|
|
s_lidSystem = PRIMARYLANGID(LANGIDFROMLCID(lcidSystem));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Don't cache user UI language and CP info, UI language can be changed without system reboot.
|
|
//
|
|
if (lidUserUI)
|
|
{
|
|
GetLocaleInfoW(MAKELCID(lidUserUI,SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
|
|
NlsConvertStringToIntegerW(szData, 10, -1, &uiUserUICp);
|
|
|
|
GetLocaleInfoW(MAKELCID(lidUserUI,SORT_DEFAULT), LOCALE_IDEFAULTCODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
|
|
NlsConvertStringToIntegerW(szData, 10, -1, &uiUserUIOEMCp);
|
|
}
|
|
|
|
//
|
|
// Complex scripts cannot be rendered in the console, so we
|
|
// force the English (US) resource.
|
|
//
|
|
if (uiConsoleCp)
|
|
{
|
|
if (s_lidSystem != LANG_ARABIC &&
|
|
s_lidSystem != LANG_HEBREW &&
|
|
s_lidSystem != LANG_VIETNAMESE &&
|
|
s_lidSystem != LANG_THAI)
|
|
{
|
|
//
|
|
// Use UI language for console only when console CP, system CP and UI language CP match.
|
|
//
|
|
if ((uiConsoleCp == s_uiSysCp || uiConsoleCp == s_uiSysOEMCp) &&
|
|
(uiConsoleCp == uiUserUICp || uiConsoleCp == uiUserUIOEMCp))
|
|
{
|
|
lcidThread = MAKELCID(lidUserUI, SORT_DEFAULT);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No console window, keep the original thread locale
|
|
//
|
|
lcidThread = lcidThreadOld;
|
|
}
|
|
|
|
//
|
|
// Set the thread locale if it's different from the currently set
|
|
// thread locale.
|
|
//
|
|
if ((lcidThread != lcidThreadOld) && (!SetThreadLocale(lcidThread)))
|
|
{
|
|
lcidThread = lcidThreadOld;
|
|
}
|
|
|
|
//
|
|
// Return the thread locale that was set.
|
|
//
|
|
return (LANGIDFROMLCID(lcidThread));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetSystemDefaultUILanguage
|
|
//
|
|
// Returns the language of the original install.
|
|
//
|
|
// 03-10-98 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LANGID WINAPI GetSystemDefaultUILanguage()
|
|
{
|
|
//
|
|
// Get the original install language and return it.
|
|
//
|
|
if (gSystemInstallLang == 0)
|
|
{
|
|
if (NtQueryInstallUILanguage(&gSystemInstallLang) != STATUS_SUCCESS)
|
|
{
|
|
gSystemInstallLang = 0;
|
|
return (NLS_DEFAULT_UILANG);
|
|
}
|
|
}
|
|
|
|
return (gSystemInstallLang);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetUserDefaultUILanguage
|
|
//
|
|
// Returns the current User's UI language selection. If the UI language
|
|
// is not available, then the chosen default UI language is used
|
|
// (NLS_DEFAULT_UILANG).
|
|
//
|
|
// 03-10-98 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LANGID WINAPI GetUserDefaultUILanguage()
|
|
{
|
|
LANGID DefaultUILang;
|
|
LANGID SystemUILang;
|
|
|
|
//
|
|
// Note that the default UI language is coming from HKCU. However,
|
|
// in the roaming profile situation, the default UI language for the
|
|
// user may be not installed in the roamming machine, therefore we will
|
|
// need to check if the DefaultUILang is a valid UI language installed
|
|
// in the machine (the check is based in HKLM).
|
|
//
|
|
if (NtQueryDefaultUILanguage(&DefaultUILang) != STATUS_SUCCESS)
|
|
{
|
|
if ((SystemUILang = GetSystemDefaultUILanguage()) == 0)
|
|
{
|
|
return (NLS_DEFAULT_UILANG);
|
|
}
|
|
return (SystemUILang);
|
|
}
|
|
return (DefaultUILang);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetSystemDefaultLangID
|
|
//
|
|
// Returns the default language for the system. If the registry value is
|
|
// not readable, then the chosen default language is used
|
|
// (NLS_DEFAULT_LANGID).
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LANGID WINAPI GetSystemDefaultLangID()
|
|
{
|
|
//
|
|
// Get the language id from the locale id stored in the cache
|
|
// and return it.
|
|
//
|
|
return (LANGIDFROMLCID(gSystemLocale));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetUserDefaultLangID
|
|
//
|
|
// Returns the default language for the current user. If the current user's
|
|
// language is not set, then the system default language id is returned.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LANGID WINAPI GetUserDefaultLangID()
|
|
{
|
|
//
|
|
// Get the language id from the locale id stored in the cache
|
|
// and return it.
|
|
//
|
|
return (LANGIDFROMLCID(GetUserDefaultLCID()));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetSystemDefaultLCID
|
|
//
|
|
// Returns the default locale for the system.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LCID WINAPI GetSystemDefaultLCID()
|
|
{
|
|
//
|
|
// Return the locale id stored in the cache.
|
|
//
|
|
return (gSystemLocale);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetUserDefaultLCID
|
|
//
|
|
// Returns the default locale for the current user. If current user's locale
|
|
// is not set, then the system default locale id is returned.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LCID WINAPI GetUserDefaultLCID()
|
|
{
|
|
LCID Lcid = NtCurrentTeb()->ImpersonationLocale;
|
|
|
|
switch (Lcid)
|
|
{
|
|
case ( -1 ) :
|
|
{
|
|
//
|
|
// Thread is being impersonated.
|
|
//
|
|
if (NT_SUCCESS( NlsGetUserLocale(&Lcid) ))
|
|
{
|
|
NtCurrentTeb()->ImpersonationLocale = Lcid;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we can't get it from the registry, then let's use the
|
|
// system locale since it won't be resolved by calling
|
|
// GetUserDefaultLCID() again.
|
|
//
|
|
Lcid = NtCurrentTeb()->ImpersonationLocale = gSystemLocale;
|
|
}
|
|
break;
|
|
}
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// Thread hasn't been impersonated.
|
|
// If we are running in the interactive logged on user, then
|
|
// use the one cached in CSRSS if the cache is valid. Otherwise,
|
|
// use the process cached locale.
|
|
//
|
|
if (gInteractiveLogonUserProcess == (BOOL) -1)
|
|
{
|
|
NlsIsInteractiveUserProcess();
|
|
}
|
|
|
|
if ((gInteractiveLogonUserProcess == FALSE) ||
|
|
((Lcid = pNlsUserInfo->UserLocaleId) == 0))
|
|
{
|
|
if (!gProcessLocale)
|
|
{
|
|
if (!NT_SUCCESS (NlsGetUserLocale(&gProcessLocale)) )
|
|
{
|
|
gProcessLocale = gSystemLocale;
|
|
}
|
|
}
|
|
|
|
Lcid = gProcessLocale;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (Lcid);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// VerLanguageNameW
|
|
//
|
|
// Returns the language name of the given language id in the language of
|
|
// the current user.
|
|
//
|
|
// 05-31-91 JulieB Moved and Rewrote from Version Library.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD WINAPI VerLanguageNameW(
|
|
DWORD wLang,
|
|
LPWSTR szLang,
|
|
DWORD wSize)
|
|
{
|
|
DWORD Length = 0; // length of string
|
|
WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
|
|
|
|
|
|
//
|
|
// Make sure we have a buffer.
|
|
//
|
|
if ((wSize == 0) || (szLang == NULL))
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Try to get the localized language name for the given ID.
|
|
//
|
|
pTemp[0] = 0;
|
|
if (!(Length = GetStringTableEntry( wLang,
|
|
0,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_LANGUAGE_NAME )))
|
|
{
|
|
//
|
|
// Can't get the name of the language id passed in, so get
|
|
// the "language neutral" name.
|
|
//
|
|
Length = GetStringTableEntry( LANG_NEUTRAL,
|
|
0,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_LANGUAGE_NAME );
|
|
}
|
|
|
|
//
|
|
// If the length is too big for the buffer, then reset the length
|
|
// to the size of the given buffer.
|
|
//
|
|
if (Length >= wSize)
|
|
{
|
|
Length = wSize - 1;
|
|
}
|
|
|
|
//
|
|
// Copy the string to the buffer and zero terminate it.
|
|
//
|
|
if (Length > 0)
|
|
{
|
|
wcsncpy(szLang, pTemp, Length);
|
|
szLang[Length] = 0;
|
|
}
|
|
|
|
//
|
|
// Return the number of characters in the string, NOT including
|
|
// the null termination.
|
|
//
|
|
return (Length);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// VerLanguageNameA
|
|
//
|
|
// Returns the language name of the given language id in the language of
|
|
// the current user.
|
|
//
|
|
// 05-31-91 JulieB Moved from Version Library.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD WINAPI VerLanguageNameA(
|
|
DWORD wLang,
|
|
LPSTR szLang,
|
|
DWORD wSize)
|
|
{
|
|
UNICODE_STRING Language; // unicode string buffer
|
|
ANSI_STRING AnsiString; // ansi string buffer
|
|
DWORD Status; // return status
|
|
|
|
|
|
//
|
|
// Make sure we have a buffer.
|
|
//
|
|
if ((wSize == 0) || (szLang == NULL))
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Allocate Unicode string structure and set the fields with the
|
|
// given parameters.
|
|
//
|
|
Language.Buffer = RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
sizeof(WCHAR) * wSize );
|
|
|
|
Language.MaximumLength = (USHORT)(wSize * sizeof(WCHAR));
|
|
|
|
//
|
|
// Make sure the allocation succeeded.
|
|
//
|
|
if (Language.Buffer == NULL)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the language name (in Unicode).
|
|
//
|
|
Status = VerLanguageNameW( wLang,
|
|
Language.Buffer,
|
|
wSize );
|
|
|
|
Language.Length = (USHORT)(Status * sizeof(WCHAR));
|
|
|
|
//
|
|
// Convert unicode string to ansi.
|
|
//
|
|
AnsiString.Buffer = szLang;
|
|
AnsiString.Length = AnsiString.MaximumLength = (USHORT)wSize;
|
|
RtlUnicodeStringToAnsiString(&AnsiString, &Language, FALSE);
|
|
Status = AnsiString.Length;
|
|
RtlFreeUnicodeString(&Language);
|
|
|
|
//
|
|
// Return the value returned from VerLanguageNameW.
|
|
//
|
|
return (Status);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetLocaleInfoW
|
|
//
|
|
// Returns one of the various pieces of information about a particular
|
|
// locale by querying the configuration registry. This call also indicates
|
|
// how much memory is necessary to contain the desired information.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int WINAPI GetLocaleInfoW(
|
|
LCID Locale,
|
|
LCTYPE LCType,
|
|
LPWSTR lpLCData,
|
|
int cchData)
|
|
{
|
|
PLOC_HASH pHashN; // ptr to LOC hash node
|
|
int Length = 0; // length of info string
|
|
LPWSTR pString; // ptr to the info string
|
|
LPWORD pStart; // ptr to starting point
|
|
BOOL UserOverride = TRUE; // use user override
|
|
BOOL ReturnNum = FALSE; // return number instead of string
|
|
LPWSTR pTmp; // tmp ptr to info string
|
|
int Repeat; // # repetitions of same letter
|
|
WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
|
|
UNICODE_STRING ObUnicodeStr; // value string
|
|
int Base = 0; // base for str to int conversion
|
|
static LANGID lidSystem = 0; // system default UI language
|
|
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - validate LCID
|
|
// - count is negative
|
|
// - NULL data pointer AND count is not zero
|
|
//
|
|
// NOTE: invalid type is checked in the switch statement below.
|
|
//
|
|
VALIDATE_LOCALE(Locale, pHashN, FALSE);
|
|
if ( (pHashN == NULL) ||
|
|
(cchData < 0) ||
|
|
((lpLCData == NULL) && (cchData != 0)) )
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Set the base value to add to in order to get the variable
|
|
// length strings.
|
|
//
|
|
pStart = (LPWORD)(pHashN->pLocaleHdr);
|
|
|
|
//
|
|
// Check for NO USER OVERRIDE flag and remove the USE CP ACP flag.
|
|
//
|
|
if (LCType & LOCALE_NOUSEROVERRIDE)
|
|
{
|
|
//
|
|
// Flag is set, so set the boolean value and remove the flag
|
|
// from the LCType parameter (for switch statement).
|
|
//
|
|
UserOverride = FALSE;
|
|
}
|
|
if (LCType & LOCALE_RETURN_NUMBER)
|
|
{
|
|
//
|
|
// Flag is set, so set the boolean value and remove the flag
|
|
// from the LCType parameter (for switch statement).
|
|
//
|
|
ReturnNum = TRUE;
|
|
}
|
|
LCType = NLS_GET_LCTYPE_VALUE(LCType);
|
|
|
|
//
|
|
// Initialize temp buffer.
|
|
//
|
|
pTemp[0] = 0;
|
|
|
|
//
|
|
// Return the appropriate information for the given LCTYPE.
|
|
// If user information exists for the given LCTYPE, then
|
|
// the user default is returned instead of the system default.
|
|
//
|
|
switch (LCType)
|
|
{
|
|
case ( LOCALE_ILANGUAGE ) :
|
|
{
|
|
Base = 16;
|
|
pString = pHashN->pLocaleFixed->szILanguage;
|
|
break;
|
|
}
|
|
case ( LOCALE_SLANGUAGE ) :
|
|
{
|
|
if (!lidSystem)
|
|
{
|
|
lidSystem = GetSystemDefaultUILanguage();
|
|
}
|
|
|
|
//
|
|
// Get the information from the RC file.
|
|
//
|
|
// Use system installed language resource if we're not under MUI.
|
|
// Otherwise, let resource loader load the default language resource.
|
|
//
|
|
Length = GetStringTableEntry( LANGIDFROMLCID(Locale),
|
|
GetUserDefaultUILanguage() == lidSystem? lidSystem : 0,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_LANGUAGE_NAME );
|
|
if (Length == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
pString = pTemp;
|
|
break;
|
|
}
|
|
case ( LOCALE_SENGLANGUAGE ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SEngLanguage;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVLANGNAME ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sAbbrevLangName),
|
|
NLS_VALUE_SLANGUAGE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevLang;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SISO639LANGNAME ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevLangISO;
|
|
break;
|
|
}
|
|
case ( LOCALE_SNATIVELANGNAME ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SNativeLang;
|
|
break;
|
|
}
|
|
case ( LOCALE_ICOUNTRY ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iCountry),
|
|
NLS_VALUE_ICOUNTRY,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szICountry;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SCOUNTRY ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sCountry),
|
|
NLS_VALUE_SCOUNTRY,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get the information from the RC file.
|
|
//
|
|
Length = GetStringTableEntry( LANGIDFROMLCID(Locale),
|
|
0,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_COUNTRY_NAME );
|
|
if (Length == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
pString = pTemp;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SENGCOUNTRY ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SEngCountry;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVCTRYNAME ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevCtry;
|
|
break;
|
|
}
|
|
case ( LOCALE_SISO3166CTRYNAME ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevCtryISO;
|
|
break;
|
|
}
|
|
case ( LOCALE_SNATIVECTRYNAME ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SNativeCtry;
|
|
break;
|
|
}
|
|
case ( LOCALE_IGEOID ) :
|
|
{
|
|
Base = 10;
|
|
pString = pHashN->pLocaleFixed->szIGeoID;
|
|
break;
|
|
}
|
|
case ( LOCALE_SSORTNAME ) :
|
|
{
|
|
//
|
|
// Get the information from the RC file.
|
|
//
|
|
Length = GetStringTableEntry( LANGIDFROMLCID(Locale),
|
|
0,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_SORT_NAMES + SORTIDFROMLCID(Locale) );
|
|
if (Length == 0)
|
|
{
|
|
//
|
|
// If the sort name doesn't exist for the given locale id,
|
|
// then try to get the Default name. This is stored in the
|
|
// 0x0000 entry.
|
|
//
|
|
Length = GetStringTableEntry( 0x0000,
|
|
0,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_SORT_NAMES + SORTIDFROMLCID(Locale) );
|
|
if (Length == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
}
|
|
pString = pTemp;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDEFAULTLANGUAGE ) :
|
|
{
|
|
Base = 16;
|
|
pString = pHashN->pLocaleFixed->szIDefaultLang;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDEFAULTCOUNTRY ) :
|
|
{
|
|
Base = 10;
|
|
pString = pHashN->pLocaleFixed->szIDefaultCtry;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDEFAULTANSICODEPAGE ) :
|
|
{
|
|
if (ReturnNum)
|
|
{
|
|
if (cchData < 2)
|
|
{
|
|
if (cchData == 0)
|
|
{
|
|
//
|
|
// DWORD is needed for this option (2 WORDS),
|
|
// so return 2.
|
|
//
|
|
return (2);
|
|
}
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Copy the value to lpLCData and return 2
|
|
// (2 WORDS = 1 DWORD).
|
|
//
|
|
*((LPDWORD)lpLCData) = (DWORD)(pHashN->pLocaleFixed->DefaultACP);
|
|
return (2);
|
|
}
|
|
|
|
pString = pHashN->pLocaleFixed->szIDefaultACP;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDEFAULTCODEPAGE ) :
|
|
{
|
|
Base = 10;
|
|
pString = pHashN->pLocaleFixed->szIDefaultOCP;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDEFAULTMACCODEPAGE ) :
|
|
{
|
|
Base = 10;
|
|
pString = pHashN->pLocaleFixed->szIDefaultMACCP;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDEFAULTEBCDICCODEPAGE ) :
|
|
{
|
|
Base = 10;
|
|
pString = pHashN->pLocaleFixed->szIDefaultEBCDICCP;
|
|
break;
|
|
}
|
|
case ( LOCALE_SLIST ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sList),
|
|
NLS_VALUE_SLIST,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SList;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IMEASURE ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iMeasure),
|
|
NLS_VALUE_IMEASURE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIMeasure;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IPAPERSIZE ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iPaperSize),
|
|
NLS_VALUE_IPAPERSIZE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIPaperSize;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SDECIMAL ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sDecimal),
|
|
NLS_VALUE_SDECIMAL,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDecimal;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_STHOUSAND ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sThousand),
|
|
NLS_VALUE_STHOUSAND,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SThousand;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SGROUPING ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sGrouping),
|
|
NLS_VALUE_SGROUPING,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SGrouping;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IDIGITS ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iDigits),
|
|
NLS_VALUE_IDIGITS,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIDigits;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_ILZERO ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iLZero),
|
|
NLS_VALUE_ILZERO,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szILZero;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGNUMBER ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iNegNumber),
|
|
NLS_VALUE_INEGNUMBER,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegNumber;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SNATIVEDIGITS ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sNativeDigits),
|
|
NLS_VALUE_SNATIVEDIGITS,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SNativeDigits;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IDIGITSUBSTITUTION ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iDigitSubstitution),
|
|
NLS_VALUE_IDIGITSUBST,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIDigitSubstitution;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SCURRENCY ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sCurrency),
|
|
NLS_VALUE_SCURRENCY,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SCurrency;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SINTLSYMBOL ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SIntlSymbol;
|
|
break;
|
|
}
|
|
case ( LOCALE_SENGCURRNAME ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SEngCurrName;
|
|
break;
|
|
}
|
|
case ( LOCALE_SNATIVECURRNAME ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SNativeCurrName;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONDECIMALSEP ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sMonDecSep),
|
|
NLS_VALUE_SMONDECIMALSEP,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonDecSep;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHOUSANDSEP ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sMonThouSep),
|
|
NLS_VALUE_SMONTHOUSANDSEP,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonThousSep;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONGROUPING ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sMonGrouping),
|
|
NLS_VALUE_SMONGROUPING,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonGrouping;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_ICURRDIGITS ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iCurrDigits),
|
|
NLS_VALUE_ICURRDIGITS,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szICurrDigits;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IINTLCURRDIGITS ) :
|
|
{
|
|
Base = 10;
|
|
pString = pHashN->pLocaleFixed->szIIntlCurrDigits;
|
|
break;
|
|
}
|
|
case ( LOCALE_ICURRENCY ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iCurrency),
|
|
NLS_VALUE_ICURRENCY,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szICurrency;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGCURR ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
|
|
NLS_VALUE_INEGCURR,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegCurr;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SPOSITIVESIGN ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sPosSign),
|
|
NLS_VALUE_SPOSITIVESIGN,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SPositiveSign;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SNEGATIVESIGN ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sNegSign),
|
|
NLS_VALUE_SNEGATIVESIGN,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SNegativeSign;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IPOSSIGNPOSN ) :
|
|
{
|
|
//
|
|
// Since there is no positive sign in any of the ICURRENCY
|
|
// options, use the INEGCURR options instead. All known
|
|
// locales would use the positive sign in the same position
|
|
// as the negative sign.
|
|
//
|
|
// NOTE: For the 2 options that use parenthesis, put the
|
|
// positive sign at the beginning of the string
|
|
// (where the opening parenthesis is).
|
|
//
|
|
// 1 => 4, 5, 8, 15
|
|
// 2 => 3, 11
|
|
// 3 => 0, 1, 6, 9, 13, 14
|
|
// 4 => 2, 7, 10, 12
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
|
|
NLS_VALUE_INEGCURR,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
switch (*pString)
|
|
{
|
|
case ( L'4' ) :
|
|
case ( L'5' ) :
|
|
case ( L'8' ) :
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'3' ) :
|
|
{
|
|
*pString = L'2';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'0' ) :
|
|
case ( L'6' ) :
|
|
case ( L'9' ) :
|
|
{
|
|
*pString = L'3';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'2' ) :
|
|
case ( L'7' ) :
|
|
{
|
|
*pString = L'4';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'1' ) :
|
|
{
|
|
switch (*(pString + 1))
|
|
{
|
|
case ( 0 ) :
|
|
case ( L'3' ) :
|
|
case ( L'4' ) :
|
|
default :
|
|
{
|
|
*pString = L'3';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'0' ) :
|
|
case ( L'2' ) :
|
|
{
|
|
*pString = L'4';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'1' ) :
|
|
{
|
|
*pString = L'2';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'5' ) :
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIPosSignPosn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIPosSignPosn;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGSIGNPOSN ) :
|
|
{
|
|
//
|
|
// Use the INEGCURR value from the user portion of the
|
|
// registry, if it exists.
|
|
//
|
|
// 0 => 0, 4, 14, 15
|
|
// 1 => 5, 8
|
|
// 2 => 3, 11
|
|
// 3 => 1, 6, 9, 13
|
|
// 4 => 2, 7, 10, 12
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
|
|
NLS_VALUE_INEGCURR,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
switch (*pString)
|
|
{
|
|
case ( L'0' ) :
|
|
case ( L'4' ) :
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'5' ) :
|
|
case ( L'8' ) :
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'3' ) :
|
|
{
|
|
*pString = L'2';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'6' ) :
|
|
case ( L'9' ) :
|
|
{
|
|
*pString = L'3';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'2' ) :
|
|
case ( L'7' ) :
|
|
{
|
|
*pString = L'4';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'1' ) :
|
|
{
|
|
switch (*(pString + 1))
|
|
{
|
|
case ( 0 ) :
|
|
case ( L'3' ) :
|
|
default :
|
|
{
|
|
*pString = L'3';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'0' ) :
|
|
case ( L'2' ) :
|
|
{
|
|
*pString = L'4';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'1' ) :
|
|
{
|
|
*pString = L'2';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'4' ) :
|
|
case ( L'5' ) :
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegSignPosn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegSignPosn;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IPOSSYMPRECEDES ) :
|
|
{
|
|
//
|
|
// Use the ICURRENCY value from the user portion of the
|
|
// registry, if it exists.
|
|
//
|
|
// 0 => 1, 3
|
|
// 1 => 0, 2
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iCurrency),
|
|
NLS_VALUE_ICURRENCY,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
switch (*pString)
|
|
{
|
|
case ( L'1' ) :
|
|
case ( L'3' ) :
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'0' ) :
|
|
case ( L'2' ) :
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIPosSymPrecedes;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIPosSymPrecedes;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IPOSSEPBYSPACE ) :
|
|
{
|
|
//
|
|
// Use the ICURRENCY value from the user portion of the
|
|
// registry, if it exists.
|
|
//
|
|
// 0 => 0, 1
|
|
// 1 => 2, 3
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iCurrency),
|
|
NLS_VALUE_ICURRENCY,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
switch (*pString)
|
|
{
|
|
case ( L'0' ) :
|
|
case ( L'1' ) :
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'2' ) :
|
|
case ( L'3' ) :
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIPosSepBySpace;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIPosSepBySpace;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGSYMPRECEDES ) :
|
|
{
|
|
//
|
|
// Use the INEGCURR value from the user portion of the
|
|
// registry, if it exists.
|
|
//
|
|
// 0 => 4, 5, 6, 7, 8, 10, 13, 15
|
|
// 1 => 0, 1, 2, 3, 9, 11, 12, 14
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
|
|
NLS_VALUE_INEGCURR,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
switch (*pString)
|
|
{
|
|
case ( L'4' ) :
|
|
case ( L'5' ) :
|
|
case ( L'6' ) :
|
|
case ( L'7' ) :
|
|
case ( L'8' ) :
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'0' ) :
|
|
case ( L'2' ) :
|
|
case ( L'3' ) :
|
|
case ( L'9' ) :
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'1' ) :
|
|
{
|
|
if ((*(pString + 1) == L'0') ||
|
|
(*(pString + 1) == L'3') ||
|
|
(*(pString + 1) == L'5'))
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
}
|
|
else
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegSymPrecedes;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegSymPrecedes;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGSEPBYSPACE ) :
|
|
{
|
|
//
|
|
// Use the INEGCURR value from the user portion of the
|
|
// registry, if it exists.
|
|
//
|
|
// 0 => 0, 1, 2, 3, 4, 5, 6, 7
|
|
// 1 => 8, 9, 10, 11, 12, 13, 14, 15
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
|
|
NLS_VALUE_INEGCURR,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
switch (*pString)
|
|
{
|
|
case ( L'0' ) :
|
|
case ( L'2' ) :
|
|
case ( L'3' ) :
|
|
case ( L'4' ) :
|
|
case ( L'5' ) :
|
|
case ( L'6' ) :
|
|
case ( L'7' ) :
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'8' ) :
|
|
case ( L'9' ) :
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
break;
|
|
}
|
|
case ( L'1' ) :
|
|
{
|
|
if (*(pString + 1) == 0)
|
|
{
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
}
|
|
else
|
|
{
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegSepBySpace;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szINegSepBySpace;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_STIMEFORMAT ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sTimeFormat),
|
|
NLS_VALUE_STIMEFORMAT,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->STimeFormat;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_STIME ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sTime),
|
|
NLS_VALUE_STIME,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->STime;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_ITIME ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iTime),
|
|
NLS_VALUE_ITIME,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szITime;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_ITLZERO ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iTLZero),
|
|
NLS_VALUE_ITLZERO,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szITLZero;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_ITIMEMARKPOSN ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iTimeMarkPosn),
|
|
NLS_VALUE_ITIMEMARKPOSN,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szITimeMarkPosn;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_S1159 ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, s1159),
|
|
NLS_VALUE_S1159,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->S1159;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_S2359 ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, s2359),
|
|
NLS_VALUE_S2359,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
FALSE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->S2359;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SSHORTDATE ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sShortDate),
|
|
NLS_VALUE_SSHORTDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SShortDate;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SDATE ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sDate),
|
|
NLS_VALUE_SDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDate;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IDATE ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iDate),
|
|
NLS_VALUE_IDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIDate;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_ICENTURY ) :
|
|
{
|
|
//
|
|
// Use the short date picture to get this information.
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sShortDate),
|
|
NLS_VALUE_SSHORTDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Find out how many y's in string.
|
|
// No need to ignore quotes in short date.
|
|
//
|
|
pTmp = pString;
|
|
while ((*pTmp) &&
|
|
(*pTmp != L'y'))
|
|
{
|
|
pTmp++;
|
|
}
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
if (*pTmp == L'y')
|
|
{
|
|
//
|
|
// Get the number of 'y' repetitions in the format string.
|
|
//
|
|
pTmp++;
|
|
for (Repeat = 0; (*pTmp == L'y'); Repeat++, pTmp++)
|
|
;
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
case ( 1 ) :
|
|
{
|
|
//
|
|
// Two-digit century with leading zero.
|
|
//
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 2 ) :
|
|
case ( 3 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Full century.
|
|
//
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use the system default value.
|
|
//
|
|
pString = pHashN->pLocaleFixed->szICentury;
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_IDAYLZERO ) :
|
|
{
|
|
//
|
|
// Use the short date picture to get this information.
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sShortDate),
|
|
NLS_VALUE_SSHORTDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Find out how many d's in string.
|
|
// No need to ignore quotes in short date.
|
|
//
|
|
pTmp = pString;
|
|
while ((*pTmp) &&
|
|
(*pTmp != L'd'))
|
|
{
|
|
pTmp++;
|
|
}
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
if (*pTmp == L'd')
|
|
{
|
|
//
|
|
// Get the number of 'd' repetitions in the format string.
|
|
//
|
|
pTmp++;
|
|
for (Repeat = 0; (*pTmp == L'd'); Repeat++, pTmp++)
|
|
;
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// No leading zero.
|
|
//
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Use leading zero.
|
|
//
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use the system default value.
|
|
//
|
|
pString = pHashN->pLocaleFixed->szIDayLZero;
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_IMONLZERO ) :
|
|
{
|
|
//
|
|
// Use the short date picture to get this information.
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sShortDate),
|
|
NLS_VALUE_SSHORTDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Find out how many M's in string.
|
|
// No need to ignore quotes in short date.
|
|
//
|
|
pTmp = pString;
|
|
while ((*pTmp) &&
|
|
(*pTmp != L'M'))
|
|
{
|
|
pTmp++;
|
|
}
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
if (*pTmp == L'M')
|
|
{
|
|
//
|
|
// Get the number of 'M' repetitions in the format string.
|
|
//
|
|
pTmp++;
|
|
for (Repeat = 0; (*pTmp == L'M'); Repeat++, pTmp++)
|
|
;
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// No leading zero.
|
|
//
|
|
*pString = L'0';
|
|
*(pString + 1) = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Use leading zero.
|
|
//
|
|
*pString = L'1';
|
|
*(pString + 1) = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use the system default value.
|
|
//
|
|
pString = pHashN->pLocaleFixed->szIMonLZero;
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_SYEARMONTH ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sYearMonth),
|
|
NLS_VALUE_SYEARMONTH,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SYearMonth;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SLONGDATE ) :
|
|
{
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sLongDate),
|
|
NLS_VALUE_SLONGDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SLongDate;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_ILDATE ) :
|
|
{
|
|
//
|
|
// Use the long date picture to get this information.
|
|
//
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sLongDate),
|
|
NLS_VALUE_SLONGDATE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
|
|
//
|
|
// Find out if d, M, or y is first, but ignore quotes.
|
|
// Also, if "ddd" or "dddd" is found, then skip it. Only
|
|
// want "d" or "dd".
|
|
//
|
|
pTmp = pString;
|
|
while (pTmp = wcspbrk(pTmp, L"dMy'"))
|
|
{
|
|
//
|
|
// Check special cases.
|
|
//
|
|
if (*pTmp == L'd')
|
|
{
|
|
//
|
|
// Check for d's. Ignore more than 2 d's.
|
|
//
|
|
for (Repeat = 0; (*pTmp == L'd'); Repeat++, pTmp++)
|
|
;
|
|
|
|
if (Repeat < 3)
|
|
{
|
|
//
|
|
// Break out of while loop. Found "d" or "dd".
|
|
//
|
|
pTmp--;
|
|
break;
|
|
}
|
|
}
|
|
else if (*pTmp == NLS_CHAR_QUOTE)
|
|
{
|
|
//
|
|
// Ignore quotes.
|
|
//
|
|
pTmp++;
|
|
while ((*pTmp) && (*pTmp != NLS_CHAR_QUOTE))
|
|
{
|
|
pTmp++;
|
|
}
|
|
pTmp++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Found one of the values, so break out of
|
|
// while loop.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the appropriate value in pString.
|
|
//
|
|
if (pTmp)
|
|
{
|
|
switch (*pTmp)
|
|
{
|
|
case ( L'd' ) :
|
|
{
|
|
*pString = L'1';
|
|
break;
|
|
}
|
|
case ( L'M' ) :
|
|
{
|
|
*pString = L'0';
|
|
break;
|
|
}
|
|
case ( L'y' ) :
|
|
{
|
|
*pString = L'2';
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Null terminate the string.
|
|
//
|
|
*(pString + 1) = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use the default value.
|
|
//
|
|
pString = pHashN->pLocaleFixed->szILDate;
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_ICALENDARTYPE ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iCalType),
|
|
NLS_VALUE_ICALENDARTYPE,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szICalendarType;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IOPTIONALCALENDAR ) :
|
|
{
|
|
Base = 10;
|
|
pString = pStart + pHashN->pLocaleHdr->IOptionalCal;
|
|
pString = ((POPT_CAL)pString)->pCalStr;
|
|
break;
|
|
}
|
|
case ( LOCALE_IFIRSTDAYOFWEEK ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iFirstDay),
|
|
NLS_VALUE_IFIRSTDAYOFWEEK,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIFirstDayOfWk;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_IFIRSTWEEKOFYEAR ) :
|
|
{
|
|
Base = 10;
|
|
if (UserOverride &&
|
|
GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, iFirstWeek),
|
|
NLS_VALUE_IFIRSTWEEKOFYEAR,
|
|
pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
TRUE ))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pString = pHashN->pLocaleFixed->szIFirstWkOfYr;
|
|
}
|
|
break;
|
|
}
|
|
case ( LOCALE_SDAYNAME1 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDayName1;
|
|
break;
|
|
}
|
|
case ( LOCALE_SDAYNAME2 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDayName2;
|
|
break;
|
|
}
|
|
case ( LOCALE_SDAYNAME3 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDayName3;
|
|
break;
|
|
}
|
|
case ( LOCALE_SDAYNAME4 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDayName4;
|
|
break;
|
|
}
|
|
case ( LOCALE_SDAYNAME5 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDayName5;
|
|
break;
|
|
}
|
|
case ( LOCALE_SDAYNAME6 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDayName6;
|
|
break;
|
|
}
|
|
case ( LOCALE_SDAYNAME7 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SDayName7;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVDAYNAME1 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName1;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVDAYNAME2 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName2;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVDAYNAME3 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName3;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVDAYNAME4 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName4;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVDAYNAME5 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName5;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVDAYNAME6 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName6;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVDAYNAME7 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName7;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME1 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName1;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME2 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName2;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME3 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName3;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME4 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName4;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME5 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName5;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME6 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName6;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME7 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName7;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME8 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName8;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME9 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName9;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME10 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName10;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME11 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName11;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME12 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName12;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHNAME13 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SMonthName13;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME1 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName1;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME2 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName2;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME3 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName3;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME4 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName4;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME5 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName5;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME6 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName6;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME7 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName7;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME8 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName8;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME9 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName9;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME10 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName10;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME11 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName11;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME12 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName12;
|
|
break;
|
|
}
|
|
case ( LOCALE_SABBREVMONTHNAME13 ) :
|
|
{
|
|
pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName13;
|
|
break;
|
|
}
|
|
case ( LOCALE_FONTSIGNATURE ) :
|
|
{
|
|
//
|
|
// Check cchData for size of given buffer.
|
|
//
|
|
if (cchData == 0)
|
|
{
|
|
return (MAX_FONTSIGNATURE);
|
|
}
|
|
else if (cchData < MAX_FONTSIGNATURE)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// This string does NOT get zero terminated.
|
|
//
|
|
pString = pHashN->pLocaleFixed->szFontSignature;
|
|
|
|
//
|
|
// Copy the string to lpLCData and return the number of
|
|
// characters copied.
|
|
//
|
|
RtlMoveMemory(lpLCData, pString, MAX_FONTSIGNATURE * sizeof(WCHAR));
|
|
return (MAX_FONTSIGNATURE);
|
|
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if the caller wants the value in the form of a number instead
|
|
// of a string.
|
|
//
|
|
if (ReturnNum)
|
|
{
|
|
//
|
|
// Make sure the flags are valid and there is enough buffer
|
|
// space.
|
|
//
|
|
if (Base == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
if (cchData < 2)
|
|
{
|
|
if (cchData == 0)
|
|
{
|
|
//
|
|
// DWORD is needed for this option (2 WORDS), so return 2.
|
|
//
|
|
return (2);
|
|
}
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Convert the string to an int and return 2 (1 DWORD = 2 WORDS).
|
|
//
|
|
RtlInitUnicodeString(&ObUnicodeStr, pString);
|
|
if (RtlUnicodeStringToInteger(&ObUnicodeStr, Base, (LPDWORD)lpLCData))
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
return (2);
|
|
}
|
|
|
|
//
|
|
// Get the length (in characters) of the string to copy.
|
|
//
|
|
if (Length == 0)
|
|
{
|
|
Length = NlsStrLenW(pString);
|
|
}
|
|
|
|
//
|
|
// Add one for the null termination. All strings should be null
|
|
// terminated.
|
|
//
|
|
Length++;
|
|
|
|
//
|
|
// Check cchData for size of given buffer.
|
|
//
|
|
if (cchData == 0)
|
|
{
|
|
//
|
|
// If cchData is 0, then we can't use lpLCData. In this
|
|
// case, we simply want to return the length (in characters) of
|
|
// the string to be copied.
|
|
//
|
|
return (Length);
|
|
}
|
|
else if (cchData < Length)
|
|
{
|
|
//
|
|
// The buffer is too small for the string, so return an error
|
|
// and zero bytes written.
|
|
//
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Copy the string to lpLCData and null terminate it.
|
|
// Return the number of characters copied.
|
|
//
|
|
wcsncpy(lpLCData, pString, Length - 1);
|
|
lpLCData[Length - 1] = 0;
|
|
return (Length);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetLocaleInfoW
|
|
//
|
|
// Sets one of the various pieces of information about a particular
|
|
// locale by making an entry in the user's portion of the configuration
|
|
// registry. This will only affect the user override portion of the locale
|
|
// settings. The system defaults will never be reset.
|
|
//
|
|
// 07-14-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI SetLocaleInfoW(
|
|
LCID Locale,
|
|
LCTYPE LCType,
|
|
LPCWSTR lpLCData)
|
|
{
|
|
PLOC_HASH pHashN; // ptr to LOC hash node
|
|
int cchData; // length of lpLCData
|
|
LPWSTR pString; // ptr to info string to change
|
|
LPWSTR pPos; // ptr to position in info string
|
|
LPWSTR pPos2; // ptr to position in info string
|
|
LPWSTR pSep; // ptr to separator string
|
|
WCHAR pTemp[MAX_PATH_LEN]; // ptr to temp storage buffer
|
|
WCHAR pOutput[MAX_REG_VAL_SIZE]; // ptr to output for GetInfo call
|
|
WCHAR pOutput2[MAX_REG_VAL_SIZE]; // ptr to output for GetInfo call
|
|
UINT Order; // date or time order value
|
|
UINT TLZero; // time leading zero value
|
|
UINT TimeMarkPosn; // time mark position value
|
|
WCHAR pFind[3]; // ptr to chars to find
|
|
int SepLen; // length of separator string
|
|
UNICODE_STRING ObUnicodeStr; // value string
|
|
int Value; // value
|
|
|
|
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - validate LCID
|
|
// - NULL data pointer
|
|
//
|
|
// NOTE: invalid type is checked in the switch statement below.
|
|
//
|
|
VALIDATE_LOCALE(Locale, pHashN, FALSE);
|
|
if ((pHashN == NULL) || (lpLCData == NULL))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the length of the buffer.
|
|
//
|
|
cchData = NlsStrLenW(lpLCData) + 1;
|
|
|
|
//
|
|
// Initialize temp buffer.
|
|
//
|
|
pTemp[0] = 0;
|
|
|
|
//
|
|
// Set the appropriate user information for the given LCTYPE.
|
|
//
|
|
LCType &= (~LOCALE_USE_CP_ACP);
|
|
switch (LCType)
|
|
{
|
|
case ( LOCALE_SLIST ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SLIST wide characters in length.
|
|
//
|
|
if (cchData > MAX_SLIST)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SLIST string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SLIST,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_IMEASURE ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_IMEASURE wide characters in length.
|
|
// It should be between 0 and MAX_VALUE_IMEASURE.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_IMEASURE is 2.
|
|
//
|
|
if ((cchData != MAX_IMEASURE) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_IMEASURE))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new IMEASURE string.
|
|
//
|
|
return (SetUserInfo( LOCALE_IMEASURE,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_IPAPERSIZE ) :
|
|
{
|
|
//
|
|
// Validate the new value.
|
|
// It should be between DMPAPER_LETTER and DMPAPER_LAST.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
RtlInitUnicodeString(&ObUnicodeStr, lpLCData);
|
|
if ((cchData < 2) ||
|
|
(RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
|
|
(Value < DMPAPER_LETTER) || (Value > DMPAPER_LAST))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new IPAPERSIZE string.
|
|
//
|
|
return (SetUserInfo( LOCALE_IPAPERSIZE,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SDECIMAL ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SDECIMAL wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_SDECIMAL,
|
|
FALSE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SDECIMAL string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SDECIMAL,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_STHOUSAND ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_STHOUSAND wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_STHOUSAND,
|
|
FALSE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new STHOUSAND string.
|
|
//
|
|
return (SetUserInfo( LOCALE_STHOUSAND,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SGROUPING ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SGROUPING wide characters in length and should
|
|
// contain alternating integer and semicolon values.
|
|
// (eg. 3;2;0 or 3;0 or 0)
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
if (!IsValidGroupingString( lpLCData,
|
|
MAX_SGROUPING,
|
|
TRUE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SGROUPING string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SGROUPING,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_IDIGITS ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_IDIGITS wide characters in length.
|
|
// The value should be between 0 and MAX_VALUE_IDIGITS.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_IDIGITS is 2.
|
|
//
|
|
if ((cchData != MAX_IDIGITS) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_IDIGITS))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new IDIGITS string.
|
|
//
|
|
return (SetUserInfo( LOCALE_IDIGITS,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_ILZERO ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_ILZERO wide characters in length.
|
|
// The value should be between 0 and MAX_VALUE_ILZERO.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_ILZERO is 2.
|
|
//
|
|
if ((cchData != MAX_ILZERO) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_ILZERO))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new ILZERO string.
|
|
//
|
|
return (SetUserInfo( LOCALE_ILZERO,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGNUMBER ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_INEGNUMBER wide characters in length.
|
|
// The value should be between 0 and MAX_VALUE_INEGNUMBER.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_INEGNUMBER is 2.
|
|
//
|
|
if ((cchData != MAX_INEGNUMBER) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_INEGNUMBER))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new INEGNUMBER string.
|
|
//
|
|
return (SetUserInfo( LOCALE_INEGNUMBER,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SNATIVEDIGITS ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be exactly
|
|
// MAX_SNATIVEDIGITS wide characters in length.
|
|
//
|
|
if (cchData != MAX_SNATIVEDIGITS)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SNATIVEDIGITS string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SNATIVEDIGITS,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_IDIGITSUBSTITUTION ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_IDIGITSUBST wide characters in length.
|
|
// The value should be between 0 and MAX_VALUE_IDIGITSUBST.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_IDIGITSUBST is 2.
|
|
//
|
|
if ((cchData != MAX_IDIGITSUBST) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_IDIGITSUBST))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new IDIGITSUBST string.
|
|
//
|
|
return (SetUserInfo( LOCALE_IDIGITSUBSTITUTION,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SCURRENCY ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SCURRENCY wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_SCURRENCY,
|
|
FALSE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SCURRENCY string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SCURRENCY,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONDECIMALSEP ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SMONDECSEP wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_SMONDECSEP,
|
|
FALSE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SMONDECIMALSEP string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SMONDECIMALSEP,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHOUSANDSEP ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SMONTHOUSEP wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_SMONTHOUSEP,
|
|
FALSE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SMONTHOUSANDSEP string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SMONTHOUSANDSEP,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONGROUPING ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SMONGROUPING wide characters in length and should
|
|
// contain alternating integer and semicolon values.
|
|
// (eg. 3;2;0 or 3;0 or 0)
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
if (!IsValidGroupingString( lpLCData,
|
|
MAX_SMONGROUPING,
|
|
TRUE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SMONGROUPING string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SMONGROUPING,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_ICURRDIGITS ) :
|
|
{
|
|
//
|
|
// Validate the new value.
|
|
// The value should be between 0 and MAX_VALUE_ICURRDIGITS.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
RtlInitUnicodeString(&ObUnicodeStr, lpLCData);
|
|
if ((cchData < 2) ||
|
|
(RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
|
|
(Value < 0) || (Value > MAX_VALUE_ICURRDIGITS) ||
|
|
((Value == 0) &&
|
|
((*lpLCData != NLS_CHAR_ZERO) || (cchData != 2))))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new ICURRDIGITS string.
|
|
//
|
|
return (SetUserInfo( LOCALE_ICURRDIGITS,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_ICURRENCY ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_ICURRENCY wide characters in length.
|
|
// The value should be between 0 and MAX_VALUE_ICURRENCY.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_ICURRENCY is 2.
|
|
//
|
|
if ((cchData != MAX_ICURRENCY) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_ICURRENCY))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new ICURRENCY string.
|
|
//
|
|
return (SetUserInfo( LOCALE_ICURRENCY,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGCURR ) :
|
|
{
|
|
//
|
|
// Validate the new value.
|
|
// The value should be between 0 and MAX_VALUE_INEGCURR.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
RtlInitUnicodeString(&ObUnicodeStr, lpLCData);
|
|
if ((cchData < 2) ||
|
|
(RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
|
|
(Value < 0) || (Value > MAX_VALUE_INEGCURR) ||
|
|
((Value == 0) &&
|
|
((*lpLCData != NLS_CHAR_ZERO) || (cchData != 2))))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new INEGCURR string.
|
|
//
|
|
return (SetUserInfo( LOCALE_INEGCURR,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SPOSITIVESIGN ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SPOSSIGN wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_SPOSSIGN,
|
|
FALSE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SPOSITIVESIGN string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SPOSITIVESIGN,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SNEGATIVESIGN ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SNEGSIGN wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_SNEGSIGN,
|
|
FALSE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SNEGATIVESIGN string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SNEGATIVESIGN,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_STIMEFORMAT ) :
|
|
{
|
|
BOOL bInsideQuotedString = FALSE;
|
|
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_STIMEFORMAT wide characters in length.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null). This is checked below
|
|
// in the check for whether or not there is an hour
|
|
// delimeter.
|
|
//
|
|
if (cchData > MAX_STIMEFORMAT)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// NOTE: Must link the STIME, ITIME, ITLZERO, and
|
|
// ITIMEMARKPOSN values in the registry.
|
|
//
|
|
|
|
//
|
|
// Search for H or h, so that iTime and iTLZero can be
|
|
// set. If no H or h exists, return an error. Note: the
|
|
// combinations "hH" or "Hh" are invalid.
|
|
//
|
|
pPos = (LPWSTR)lpLCData;
|
|
while ((pPos = wcspbrk(pPos, L"Hh'")))
|
|
{
|
|
if (*pPos == L'\'')
|
|
{
|
|
//
|
|
// Enter or leave a quoted string.
|
|
//
|
|
bInsideQuotedString &= ~TRUE;
|
|
}
|
|
else if (*pPos == L'H')
|
|
{
|
|
//
|
|
// Found an H.
|
|
//
|
|
if (!bInsideQuotedString)
|
|
{
|
|
//
|
|
// Get the appropriate ITIME value.
|
|
//
|
|
Order = 1;
|
|
|
|
//
|
|
// Get the appropriate ITLZERO value.
|
|
//
|
|
if (*(pPos + 1) == L'H')
|
|
{
|
|
TLZero = 1;
|
|
break;
|
|
}
|
|
else if (*(pPos + 1) == L'h')
|
|
{
|
|
//
|
|
// Invalid combination.
|
|
//
|
|
pPos = NULL;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TLZero = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (*pPos == L'h')
|
|
{
|
|
//
|
|
// Found an h.
|
|
//
|
|
if (!bInsideQuotedString)
|
|
{
|
|
//
|
|
// Get the appropriate ITIME value.
|
|
//
|
|
Order = 0;
|
|
|
|
//
|
|
// Get the appropriate ITLZERO value.
|
|
//
|
|
if (*(pPos + 1) == L'h')
|
|
{
|
|
TLZero = 1;
|
|
break;
|
|
}
|
|
else if (*(pPos + 1) == L'H')
|
|
{
|
|
//
|
|
// Invalid combination.
|
|
//
|
|
pPos = NULL;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TLZero = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pPos++;
|
|
}
|
|
|
|
//
|
|
// If pPos == NULL, then one of two things happened:
|
|
// - reached the end of the string without finding "H" or "h"
|
|
// - found an invalid combination like "hH" or "Hh"
|
|
//
|
|
if (!pPos)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Search for tt, so that ITIMEMARKPOSN can be
|
|
// set. If no tt exists, do not change the value.
|
|
//
|
|
bInsideQuotedString = FALSE;
|
|
pPos = (LPWSTR)lpLCData;
|
|
while ((pPos = wcspbrk(pPos, L"t'")))
|
|
{
|
|
if (*pPos == L'\'')
|
|
{
|
|
//
|
|
// Enter or leave a quoted string.
|
|
//
|
|
bInsideQuotedString &= ~TRUE;
|
|
}
|
|
else if (*(pPos + 1) == L't')
|
|
{
|
|
if (!bInsideQuotedString)
|
|
{
|
|
//
|
|
// The string "tt" is found.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
pPos++;
|
|
}
|
|
if (pPos)
|
|
{
|
|
//
|
|
// Get the appropriate ITIMEMARKPOSN value.
|
|
//
|
|
bInsideQuotedString = FALSE;
|
|
pPos2 = (LPWSTR)lpLCData;
|
|
while ((pPos2 = wcspbrk(pPos2, L"Hhmst'")))
|
|
{
|
|
if (*pPos == L'\'')
|
|
{
|
|
//
|
|
// Enter or leave a quoted string.
|
|
//
|
|
bInsideQuotedString &= ~TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (!bInsideQuotedString)
|
|
{
|
|
//
|
|
// Get the appropriate ITIMEMARKPOSN value.
|
|
//
|
|
TimeMarkPosn = (pPos == pPos2) ? 1 : 0;
|
|
break;
|
|
}
|
|
}
|
|
pPos2++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the time separator so that STIME can be set.
|
|
//
|
|
bInsideQuotedString = FALSE;
|
|
pPos = (LPWSTR)lpLCData;
|
|
while (pPos = wcspbrk(pPos, L"Hhms'"))
|
|
{
|
|
if (*pPos == L'\'')
|
|
{
|
|
//
|
|
// Enter or leave a quoted string.
|
|
//
|
|
bInsideQuotedString &= ~TRUE;
|
|
pPos++;
|
|
}
|
|
else
|
|
{
|
|
if (!bInsideQuotedString)
|
|
{
|
|
//
|
|
// Look for the beginning of the time separator.
|
|
//
|
|
pPos++;
|
|
while ((*pPos) && (wcschr(L"Hhms", *pPos)))
|
|
{
|
|
pPos++;
|
|
}
|
|
|
|
//
|
|
// Look for the end of the time separator.
|
|
//
|
|
if (*pPos)
|
|
{
|
|
//
|
|
// Find the end of the separator string.
|
|
//
|
|
pPos2 = wcspbrk(pPos, L"Hhmst");
|
|
if (pPos2)
|
|
{
|
|
if (*pPos2 == L't')
|
|
{
|
|
//
|
|
// Found a time marker, so need to start
|
|
// over in search for separator. There
|
|
// are no separators around the time
|
|
// marker.
|
|
//
|
|
pPos = pPos2 + 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Found end of separator, so break out of
|
|
// while loop.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the appropriate STIME string.
|
|
//
|
|
if (pPos)
|
|
{
|
|
//
|
|
// Copy to temp buffer so that it's zero terminated.
|
|
//
|
|
pString = pTemp;
|
|
while (pPos != pPos2)
|
|
{
|
|
//
|
|
// If there is a quoted string in the separator, then
|
|
// just put in a white space, since there is no meaning
|
|
// for time field separator anymore.
|
|
//
|
|
if (*pPos == L'\'')
|
|
{
|
|
pString = pTemp;
|
|
*pString++ = L' ';
|
|
break;
|
|
}
|
|
|
|
*pString = *pPos;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
*pString = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There is no time separator, so use NULL.
|
|
//
|
|
*pTemp = 0;
|
|
}
|
|
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_STIME wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
if (!IsValidSeparatorString( pTemp,
|
|
MAX_STIME,
|
|
TRUE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure that the time separator does NOT contain any
|
|
// of the special time picture characters - h, H, m, s, t, '.
|
|
//
|
|
if (wcspbrk(pTemp, L"Hhmst'"))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Call the server to set the registry.
|
|
//
|
|
return (SetMultipleUserInfo( LCType,
|
|
cchData,
|
|
lpLCData,
|
|
pTemp,
|
|
(Order == 0) ? L"0" : L"1",
|
|
(TLZero == 0) ? L"0" : L"1",
|
|
(TimeMarkPosn == 0) ? L"0" : L"1" ));
|
|
break;
|
|
}
|
|
case ( LOCALE_STIME ) :
|
|
{
|
|
//
|
|
// NOTE: Must link the STIMEFORMAT value in the registry.
|
|
//
|
|
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_STIME wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_STIME,
|
|
TRUE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure that the time separator does NOT contain any
|
|
// of the special time picture characters - h, H, m, s, t, '.
|
|
//
|
|
if (wcspbrk(lpLCData, L"Hhmst'"))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the current setting for STIMEFORMAT.
|
|
//
|
|
if (GetUserInfo( Locale,
|
|
LOCALE_STIMEFORMAT,
|
|
FIELD_OFFSET(NLS_USER_INFO, sTimeFormat),
|
|
NLS_VALUE_STIMEFORMAT,
|
|
pOutput,
|
|
ARRAYSIZE(pOutput),
|
|
TRUE ))
|
|
{
|
|
pString = pOutput;
|
|
}
|
|
else
|
|
{
|
|
pString = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->STimeFormat;
|
|
}
|
|
|
|
//
|
|
// Get the current setting for STIME.
|
|
//
|
|
if (GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sTime),
|
|
NLS_VALUE_STIME,
|
|
pOutput2,
|
|
ARRAYSIZE(pOutput2),
|
|
TRUE ))
|
|
{
|
|
pSep = pOutput2;
|
|
}
|
|
else
|
|
{
|
|
pSep = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->STime;
|
|
}
|
|
|
|
//
|
|
// Get the length of the separator string.
|
|
//
|
|
SepLen = NlsStrLenW(pSep);
|
|
|
|
//
|
|
// Setup the string containing the characters to find in
|
|
// the timeformat string.
|
|
//
|
|
pFind[0] = NLS_CHAR_QUOTE;
|
|
pFind[1] = *pSep;
|
|
pFind[2] = 0;
|
|
|
|
//
|
|
// Find the time separator in the STIMEFORMAT string and
|
|
// replace it with the new time separator.
|
|
//
|
|
// The new separator may be a different length than
|
|
// the old one, so must use a static buffer for the new
|
|
// time format string.
|
|
//
|
|
pPos = pTemp;
|
|
while (pPos2 = wcspbrk(pString, pFind))
|
|
{
|
|
//
|
|
// Copy format string up to pPos2.
|
|
//
|
|
while (pString < pPos2)
|
|
{
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
|
|
switch (*pPos2)
|
|
{
|
|
case ( NLS_CHAR_QUOTE ) :
|
|
{
|
|
//
|
|
// Copy the quote.
|
|
//
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
|
|
//
|
|
// Copy what's inside the quotes.
|
|
//
|
|
while ((*pString) && (*pString != NLS_CHAR_QUOTE))
|
|
{
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
|
|
//
|
|
// Copy the end quote.
|
|
//
|
|
*pPos = NLS_CHAR_QUOTE;
|
|
pPos++;
|
|
if (*pString)
|
|
{
|
|
pString++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
//
|
|
// Make sure it's the old separator.
|
|
//
|
|
if (NlsStrNEqualW(pString, pSep, SepLen))
|
|
{
|
|
//
|
|
// Adjust pointer to skip over old separator.
|
|
//
|
|
pString += SepLen;
|
|
|
|
//
|
|
// Copy the new separator.
|
|
//
|
|
pPos2 = (LPWSTR)lpLCData;
|
|
while (*pPos2)
|
|
{
|
|
*pPos = *pPos2;
|
|
pPos++;
|
|
pPos2++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Copy the code point and continue.
|
|
//
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy to the end of the string and null terminate it.
|
|
//
|
|
while (*pString)
|
|
{
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
*pPos = 0;
|
|
|
|
//
|
|
// Call the server to set the registry.
|
|
//
|
|
return (SetMultipleUserInfo( LCType,
|
|
cchData,
|
|
pTemp,
|
|
lpLCData,
|
|
NULL,
|
|
NULL,
|
|
NULL ));
|
|
break;
|
|
}
|
|
case ( LOCALE_ITIME ) :
|
|
{
|
|
//
|
|
// NOTE: Must link the STIMEFORMAT value in the registry.
|
|
//
|
|
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_ITIME wide characters in length.
|
|
// The value should be either 0 or MAX_VALUE_ITIME.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_ITIME is 2.
|
|
//
|
|
if ((cchData != MAX_ITIME) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_ITIME))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the current setting for STIMEFORMAT.
|
|
//
|
|
if (GetUserInfo( Locale,
|
|
LOCALE_STIMEFORMAT,
|
|
FIELD_OFFSET(NLS_USER_INFO, sTimeFormat),
|
|
NLS_VALUE_STIMEFORMAT,
|
|
pOutput,
|
|
ARRAYSIZE(pOutput),
|
|
TRUE ))
|
|
{
|
|
pString = pOutput;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Copy system default to temp buffer.
|
|
//
|
|
if(FAILED( StringCchCopyW( pTemp,
|
|
ARRAYSIZE(pTemp),
|
|
(LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->STimeFormat ) ))
|
|
{
|
|
//
|
|
// Failure should in theory be impossible, but if we ignore the
|
|
// return value, PREfast will complain.
|
|
//
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return(FALSE);
|
|
}
|
|
pString = pTemp;
|
|
}
|
|
|
|
//
|
|
// Search down the STIMEFORMAT string.
|
|
// If iTime = 0, then H -> h.
|
|
// If iTime = 1, then h -> H.
|
|
//
|
|
pPos = pString;
|
|
if (*lpLCData == NLS_CHAR_ZERO)
|
|
{
|
|
while (*pPos)
|
|
{
|
|
if (*pPos == L'H')
|
|
{
|
|
*pPos = L'h';
|
|
}
|
|
pPos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (*pPos)
|
|
{
|
|
if (*pPos == L'h')
|
|
{
|
|
*pPos = L'H';
|
|
}
|
|
pPos++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the server to set the registry.
|
|
//
|
|
return (SetMultipleUserInfo( LCType,
|
|
cchData,
|
|
pString,
|
|
NULL,
|
|
lpLCData,
|
|
NULL,
|
|
NULL ));
|
|
break;
|
|
}
|
|
case ( LOCALE_S1159 ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_S1159 wide characters in length.
|
|
//
|
|
if (cchData > MAX_S1159)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new S1159 string.
|
|
//
|
|
return (SetUserInfo( LOCALE_S1159,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_S2359 ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_S2359 wide characters in length.
|
|
//
|
|
if (cchData > MAX_S2359)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new S2359 string.
|
|
//
|
|
return (SetUserInfo( LOCALE_S2359,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SSHORTDATE ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SSHORTDATE wide characters in length.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null). This is checked below
|
|
// in the check for whether or not there is a date,
|
|
// month, or year delimeter.
|
|
//
|
|
if (cchData > MAX_SSHORTDATE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// NOTE: Must link the IDATE and SDATE values in the registry.
|
|
//
|
|
|
|
//
|
|
// Search for the 'd' or 'M' or 'y' sequence in the date format
|
|
// string to set the new IDATE value.
|
|
//
|
|
// If none of these symbols exist in the date format string,
|
|
// then return an error.
|
|
//
|
|
pPos = wcspbrk(lpLCData, L"dMy");
|
|
if (!pPos)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the appropriate IDATE string.
|
|
//
|
|
switch (*pPos)
|
|
{
|
|
case ( L'M' ) :
|
|
{
|
|
Order = 0;
|
|
break;
|
|
}
|
|
|
|
case ( L'd' ) :
|
|
{
|
|
Order = 1;
|
|
break;
|
|
}
|
|
|
|
case ( L'y' ) :
|
|
{
|
|
Order = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the registry with the appropriate SDATE string.
|
|
//
|
|
// The ptr "pPos" is pointing at either d, M, or y.
|
|
// Go to the next position past sequence of d, M, or y.
|
|
//
|
|
pPos++;
|
|
while ((*pPos) && (wcschr( L"dMy", *pPos )))
|
|
{
|
|
pPos++;
|
|
}
|
|
|
|
*pTemp = 0;
|
|
if (*pPos)
|
|
{
|
|
//
|
|
// Find the end of the separator string.
|
|
//
|
|
pPos2 = wcspbrk(pPos, L"dMy");
|
|
if (pPos2)
|
|
{
|
|
//
|
|
// Copy to temp buffer so that it's zero terminated.
|
|
//
|
|
pString = pTemp;
|
|
while (pPos != pPos2)
|
|
{
|
|
//
|
|
// If there is a quoted string in the separator, then
|
|
// just punch in a white space, since there is no meaning
|
|
// for short date field separator anymore.
|
|
//
|
|
if (*pPos == L'\'')
|
|
{
|
|
pString = pTemp;
|
|
*pString++ = L' ';
|
|
break;
|
|
}
|
|
*pString = *pPos;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
*pString = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Since the date separator (LOCALE_SDATE) is being set here, we
|
|
// should do the same validation as LOCALE_SDATE.
|
|
//
|
|
if (!IsValidSeparatorString( pTemp,
|
|
MAX_SDATE,
|
|
TRUE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure that the date separator does NOT contain any
|
|
// of the special date picture characters - d, M, y, g, '.
|
|
//
|
|
if (wcspbrk(pTemp, L"dMyg'"))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Call the server to set the registry.
|
|
//
|
|
return (SetMultipleUserInfo( LCType,
|
|
cchData,
|
|
lpLCData,
|
|
pTemp,
|
|
(Order == 0) ? L"0" :
|
|
((Order == 1) ? L"1" : L"2"),
|
|
NULL,
|
|
NULL ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SDATE ) :
|
|
{
|
|
//
|
|
// NOTE: Must link the SSHORTDATE value in the registry.
|
|
//
|
|
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SDATE wide characters in length and should not
|
|
// contain any integer values (L'0' thru L'9').
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
if (!IsValidSeparatorString( lpLCData,
|
|
MAX_SDATE,
|
|
TRUE ))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure that the date separator does NOT contain any
|
|
// of the special date picture characters - d, M, y, g, '.
|
|
//
|
|
if (wcspbrk(lpLCData, L"dMyg'"))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the current setting for SSHORTDATE.
|
|
//
|
|
if (GetUserInfo( Locale,
|
|
LOCALE_SSHORTDATE,
|
|
FIELD_OFFSET(NLS_USER_INFO, sShortDate),
|
|
NLS_VALUE_SSHORTDATE,
|
|
pOutput,
|
|
ARRAYSIZE(pOutput),
|
|
TRUE ))
|
|
{
|
|
pString = pOutput;
|
|
}
|
|
else
|
|
{
|
|
pString = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->SShortDate;
|
|
}
|
|
|
|
//
|
|
// Get the current setting for SDATE.
|
|
//
|
|
if (GetUserInfo( Locale,
|
|
LCType,
|
|
FIELD_OFFSET(NLS_USER_INFO, sDate),
|
|
NLS_VALUE_SDATE,
|
|
pOutput2,
|
|
ARRAYSIZE(pOutput2),
|
|
TRUE ))
|
|
{
|
|
pSep = pOutput2;
|
|
}
|
|
else
|
|
{
|
|
pSep = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->SDate;
|
|
}
|
|
|
|
//
|
|
// Get the length of the separator string.
|
|
//
|
|
SepLen = NlsStrLenW(pSep);
|
|
|
|
//
|
|
// Setup the string containing the characters to find in
|
|
// the shortdate string.
|
|
//
|
|
pFind[0] = NLS_CHAR_QUOTE;
|
|
pFind[1] = *pSep;
|
|
pFind[2] = 0;
|
|
|
|
//
|
|
// Find the date separator in the SSHORTDATE string and
|
|
// replace it with the new date separator.
|
|
//
|
|
// The new separator may be a different length than
|
|
// the old one, so must use a static buffer for the new
|
|
// short date format string.
|
|
//
|
|
pPos = pTemp;
|
|
while (pPos2 = wcspbrk(pString, pFind))
|
|
{
|
|
//
|
|
// Copy format string up to pPos2.
|
|
//
|
|
while (pString < pPos2)
|
|
{
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
|
|
switch (*pPos2)
|
|
{
|
|
case ( NLS_CHAR_QUOTE ) :
|
|
{
|
|
//
|
|
// Copy the quote.
|
|
//
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
|
|
//
|
|
// Copy what's inside the quotes.
|
|
//
|
|
while ((*pString) && (*pString != NLS_CHAR_QUOTE))
|
|
{
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
|
|
//
|
|
// Copy the end quote.
|
|
//
|
|
*pPos = NLS_CHAR_QUOTE;
|
|
pPos++;
|
|
if (*pString)
|
|
{
|
|
pString++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
//
|
|
// Make sure it's the old separator.
|
|
//
|
|
if (NlsStrNEqualW(pString, pSep, SepLen))
|
|
{
|
|
//
|
|
// Adjust pointer to skip over old separator.
|
|
//
|
|
pString += SepLen;
|
|
|
|
//
|
|
// Copy the new separator.
|
|
//
|
|
pPos2 = (LPWSTR)lpLCData;
|
|
while (*pPos2)
|
|
{
|
|
*pPos = *pPos2;
|
|
pPos++;
|
|
pPos2++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Copy the code point and continue.
|
|
//
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy to the end of the string and null terminate it.
|
|
//
|
|
while (*pString)
|
|
{
|
|
*pPos = *pString;
|
|
pPos++;
|
|
pString++;
|
|
}
|
|
*pPos = 0;
|
|
|
|
//
|
|
// Call the server to set the registry.
|
|
//
|
|
return (SetMultipleUserInfo( LCType,
|
|
cchData,
|
|
pTemp,
|
|
lpLCData,
|
|
NULL,
|
|
NULL,
|
|
NULL ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SYEARMONTH ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SYEARMONTH wide characters in length.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null). This is checked below
|
|
// in the check for whether or not there is a date,
|
|
// month, or year delimeter.
|
|
//
|
|
if (cchData > MAX_SYEARMONTH)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure one of 'M' or 'y' exists in the date
|
|
// format string. If it does not, then return an error.
|
|
//
|
|
pPos = wcspbrk(lpLCData, L"My");
|
|
if (!pPos)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SYEARMONTH string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SYEARMONTH,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_SLONGDATE ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_SLONGDATE wide characters in length.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null). This is checked below
|
|
// in the check for whether or not there is a date,
|
|
// month, or year delimeter.
|
|
//
|
|
if (cchData > MAX_SLONGDATE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure one of 'd' or 'M' or 'y' exists in the date
|
|
// format string. If it does not, then return an error.
|
|
//
|
|
pPos = wcspbrk(lpLCData, L"dMy");
|
|
if (!pPos)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new SLONGDATE string.
|
|
//
|
|
return (SetUserInfo( LOCALE_SLONGDATE,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_ICALENDARTYPE ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_ICALTYPE wide characters in length.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
if ((cchData < 2) || (cchData > MAX_ICALTYPE) ||
|
|
(!IsValidCalendarTypeStr(pHashN, lpLCData)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new ICALENDARTYPE string.
|
|
//
|
|
return (SetUserInfo( LOCALE_ICALENDARTYPE,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_IFIRSTDAYOFWEEK ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_IFIRSTDAY wide characters in length.
|
|
// The value should be between 0 and MAX_VALUE_IFIRSTDAY.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_IFIRSTDAY is 2.
|
|
//
|
|
if ((cchData != MAX_IFIRSTDAY) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_IFIRSTDAY))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new IFIRSTDAYOFWEEK string.
|
|
//
|
|
return (SetUserInfo( LOCALE_IFIRSTDAYOFWEEK,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
case ( LOCALE_IFIRSTWEEKOFYEAR ) :
|
|
{
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_IFIRSTWEEK wide characters in length.
|
|
// The value should be between 0 and MAX_VALUE_IFIRSTWEEK.
|
|
//
|
|
// NOTE: The string may not be NULL, so it must be at least
|
|
// 2 chars long (includes null).
|
|
//
|
|
// Optimized - since MAX_IFIRSTWEEK is 2.
|
|
//
|
|
if ((cchData != MAX_IFIRSTWEEK) ||
|
|
(*lpLCData < NLS_CHAR_ZERO) ||
|
|
(*lpLCData > MAX_CHAR_IFIRSTWEEK))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new IFIRSTWEEKOFYEAR string.
|
|
//
|
|
return (SetUserInfo( LOCALE_IFIRSTWEEKOFYEAR,
|
|
(LPWSTR)lpLCData,
|
|
cchData ));
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetCalendarInfoW
|
|
//
|
|
// Returns one of the various pieces of information about a particular
|
|
// calendar by querying the configuration registry. This call also
|
|
// indicates how much memory is necessary to contain the desired
|
|
// information.
|
|
//
|
|
// 12-17-97 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int WINAPI GetCalendarInfoW(
|
|
LCID Locale,
|
|
CALID Calendar,
|
|
CALTYPE CalType,
|
|
LPWSTR lpCalData,
|
|
int cchData,
|
|
LPDWORD lpValue)
|
|
{
|
|
PLOC_HASH pHashN; // ptr to LOC hash node
|
|
int Length = 0; // length of info string
|
|
LPWSTR pString; // ptr to the info string
|
|
BOOL UserOverride = TRUE; // use user override
|
|
BOOL ReturnNum = FALSE; // return number instead of string
|
|
WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
|
|
UNICODE_STRING ObUnicodeStr; // value string
|
|
int Base = 0; // base for str to int conversion
|
|
LPWSTR pOptCal; // ptr to optional calendar values
|
|
PCAL_INFO pCalInfo; // ptr to calendar info
|
|
|
|
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - validate LCID
|
|
// - count is negative
|
|
// - NULL data pointer AND count is not zero
|
|
//
|
|
VALIDATE_LOCALE(Locale, pHashN, FALSE);
|
|
if ((pHashN == NULL) ||
|
|
(cchData < 0) ||
|
|
((lpCalData == NULL) && (cchData != 0)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Need to check the parameters based on the CAL_RETURN_NUMBER
|
|
// CalType.
|
|
//
|
|
if (CalType & CAL_RETURN_NUMBER)
|
|
{
|
|
if ((lpCalData != NULL) || (cchData != 0) || (lpValue == NULL))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((lpValue != NULL) ||
|
|
(cchData < 0) ||
|
|
((lpCalData == NULL) && (cchData != 0)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for NO USER OVERRIDE flag and remove the USE CP ACP flag.
|
|
//
|
|
if (CalType & CAL_NOUSEROVERRIDE)
|
|
{
|
|
//
|
|
// Flag is set, so set the boolean value and remove the flag
|
|
// from the CalType parameter (for switch statement).
|
|
//
|
|
UserOverride = FALSE;
|
|
}
|
|
if (CalType & CAL_RETURN_NUMBER)
|
|
{
|
|
//
|
|
// Flag is set, so set the boolean value and remove the flag
|
|
// from the CalType parameter (for switch statement).
|
|
//
|
|
ReturnNum = TRUE;
|
|
}
|
|
CalType &= (~(CAL_NOUSEROVERRIDE | CAL_USE_CP_ACP | CAL_RETURN_NUMBER));
|
|
|
|
//
|
|
// Validate the Calendar parameter.
|
|
//
|
|
if (((CalType != CAL_ITWODIGITYEARMAX) &&
|
|
((pOptCal = IsValidCalendarType(pHashN, Calendar)) == NULL)) ||
|
|
(GetCalendar(Calendar, &pCalInfo) != NO_ERROR))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Return the appropriate information for the given CALTYPE.
|
|
// If user information exists for the given CALTYPE, then
|
|
// the user default is returned instead of the system default.
|
|
//
|
|
switch (CalType)
|
|
{
|
|
case ( CAL_ICALINTVALUE ) :
|
|
{
|
|
Base = 10;
|
|
|
|
//
|
|
// Get the integer value for the given calendar.
|
|
//
|
|
pString = ((POPT_CAL)pOptCal)->pCalStr;
|
|
|
|
break;
|
|
}
|
|
case ( CAL_SCALNAME ) :
|
|
{
|
|
//
|
|
// Get the calendar name for the given calendar.
|
|
//
|
|
pString = ((POPT_CAL)pOptCal)->pCalStr +
|
|
NlsStrLenW(((POPT_CAL)pOptCal)->pCalStr) + 1;
|
|
|
|
break;
|
|
}
|
|
case ( CAL_ITWODIGITYEARMAX ) :
|
|
{
|
|
Base = 10;
|
|
|
|
//
|
|
// Check if a policy is enforced for the current user,
|
|
// and if so, let's use it.
|
|
//
|
|
if (GetTwoDigitYearInfo(Calendar, pTemp, ARRAYSIZE(pTemp), NLS_POLICY_TWO_DIGIT_YEAR_KEY))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
if (UserOverride &&
|
|
GetTwoDigitYearInfo(Calendar, pTemp, ARRAYSIZE(pTemp), NLS_TWO_DIGIT_YEAR_KEY))
|
|
{
|
|
pString = pTemp;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use the default.
|
|
//
|
|
pString = (LPWORD)pCalInfo +
|
|
(((PCALENDAR_VAR)pCalInfo)->STwoDigitYearMax);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( CAL_IYEAROFFSETRANGE ) :
|
|
{
|
|
Base = 10;
|
|
|
|
//
|
|
// Get the pointer to the appropriate calendar string.
|
|
//
|
|
pString = (LPWORD)pCalInfo +
|
|
(((PCALENDAR_VAR)pCalInfo)->SEraRanges);
|
|
|
|
//
|
|
// Make sure the string is NOT empty.
|
|
//
|
|
if (*pString)
|
|
{
|
|
pString = ((PERA_RANGE)pString)->pYearStr;
|
|
}
|
|
else
|
|
{
|
|
pString = L"0";
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( CAL_SERASTRING ) :
|
|
{
|
|
//
|
|
// Get the pointer to the appropriate calendar string.
|
|
//
|
|
pString = (LPWORD)pCalInfo +
|
|
(((PCALENDAR_VAR)pCalInfo)->SEraRanges);
|
|
|
|
//
|
|
// Make sure the string is NOT empty. If it is, return the
|
|
// empty string.
|
|
//
|
|
if (*pString)
|
|
{
|
|
pString = ((PERA_RANGE)pString)->pYearStr +
|
|
NlsStrLenW(((PERA_RANGE)pString)->pYearStr) + 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( CAL_SSHORTDATE ) :
|
|
{
|
|
//
|
|
// Get the pointer to the appropriate calendar string.
|
|
//
|
|
pString = (LPWORD)pCalInfo +
|
|
(((PCALENDAR_VAR)pCalInfo)->SShortDate);
|
|
|
|
//
|
|
// Make sure the string is NOT empty. If it is, use the
|
|
// locale's short date string.
|
|
//
|
|
if (*pString == 0)
|
|
{
|
|
pString = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->SShortDate;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( CAL_SLONGDATE ) :
|
|
{
|
|
//
|
|
// Get the pointer to the appropriate calendar string.
|
|
//
|
|
pString = (LPWORD)pCalInfo +
|
|
(((PCALENDAR_VAR)pCalInfo)->SLongDate);
|
|
|
|
//
|
|
// Make sure the string is NOT empty. If it is, use the
|
|
// locale's long date string.
|
|
//
|
|
if (*pString == 0)
|
|
{
|
|
pString = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->SLongDate;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( CAL_SYEARMONTH ) :
|
|
{
|
|
//
|
|
// Get the pointer to the appropriate calendar string.
|
|
//
|
|
pString = (LPWORD)pCalInfo +
|
|
(((PCALENDAR_VAR)pCalInfo)->SYearMonth);
|
|
|
|
//
|
|
// Make sure the string is NOT empty. If it is, use the
|
|
// locale's year month string.
|
|
//
|
|
if (*pString == 0)
|
|
{
|
|
pString = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->SYearMonth;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( CAL_SDAYNAME1 ) :
|
|
case ( CAL_SDAYNAME2 ) :
|
|
case ( CAL_SDAYNAME3 ) :
|
|
case ( CAL_SDAYNAME4 ) :
|
|
case ( CAL_SDAYNAME5 ) :
|
|
case ( CAL_SDAYNAME6 ) :
|
|
case ( CAL_SDAYNAME7 ) :
|
|
case ( CAL_SABBREVDAYNAME1 ) :
|
|
case ( CAL_SABBREVDAYNAME2 ) :
|
|
case ( CAL_SABBREVDAYNAME3 ) :
|
|
case ( CAL_SABBREVDAYNAME4 ) :
|
|
case ( CAL_SABBREVDAYNAME5 ) :
|
|
case ( CAL_SABBREVDAYNAME6 ) :
|
|
case ( CAL_SABBREVDAYNAME7 ) :
|
|
case ( CAL_SMONTHNAME1 ) :
|
|
case ( CAL_SMONTHNAME2 ) :
|
|
case ( CAL_SMONTHNAME3 ) :
|
|
case ( CAL_SMONTHNAME4 ) :
|
|
case ( CAL_SMONTHNAME5 ) :
|
|
case ( CAL_SMONTHNAME6 ) :
|
|
case ( CAL_SMONTHNAME7 ) :
|
|
case ( CAL_SMONTHNAME8 ) :
|
|
case ( CAL_SMONTHNAME9 ) :
|
|
case ( CAL_SMONTHNAME10 ) :
|
|
case ( CAL_SMONTHNAME11 ) :
|
|
case ( CAL_SMONTHNAME12 ) :
|
|
case ( CAL_SMONTHNAME13 ) :
|
|
case ( CAL_SABBREVMONTHNAME1 ) :
|
|
case ( CAL_SABBREVMONTHNAME2 ) :
|
|
case ( CAL_SABBREVMONTHNAME3 ) :
|
|
case ( CAL_SABBREVMONTHNAME4 ) :
|
|
case ( CAL_SABBREVMONTHNAME5 ) :
|
|
case ( CAL_SABBREVMONTHNAME6 ) :
|
|
case ( CAL_SABBREVMONTHNAME7 ) :
|
|
case ( CAL_SABBREVMONTHNAME8 ) :
|
|
case ( CAL_SABBREVMONTHNAME9 ) :
|
|
case ( CAL_SABBREVMONTHNAME10 ) :
|
|
case ( CAL_SABBREVMONTHNAME11 ) :
|
|
case ( CAL_SABBREVMONTHNAME12 ) :
|
|
case ( CAL_SABBREVMONTHNAME13 ) :
|
|
{
|
|
//
|
|
// Get the pointer to the appropriate calendar string if the
|
|
// IfNames flag is set for the calendar.
|
|
//
|
|
pString = NULL;
|
|
if (((PCALENDAR_VAR)pCalInfo)->IfNames)
|
|
{
|
|
pString = (LPWORD)pCalInfo +
|
|
*((LPWORD)((LPBYTE)(pCalInfo) +
|
|
(FIELD_OFFSET(CALENDAR_VAR, SDayName1) +
|
|
((CalType - CAL_SDAYNAME1) * sizeof(WORD)))));
|
|
}
|
|
|
|
//
|
|
// Make sure the string is NOT empty. If it is, use the
|
|
// locale's string.
|
|
//
|
|
if ((pString == NULL) || (*pString == 0))
|
|
{
|
|
pString = (LPWORD)(pHashN->pLocaleHdr) +
|
|
*((LPWORD)((LPBYTE)(pHashN->pLocaleHdr) +
|
|
(FIELD_OFFSET(LOCALE_VAR, SDayName1) +
|
|
((CalType - CAL_SDAYNAME1) * sizeof(WORD)))));
|
|
}
|
|
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if the caller wants the value in the form of a number instead
|
|
// of a string.
|
|
//
|
|
if (ReturnNum)
|
|
{
|
|
//
|
|
// Make sure the flags are valid and that the DWORD buffer
|
|
// is not NULL.
|
|
//
|
|
if (Base == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
|
|
if (lpValue == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Convert the string to an int and return 2 (1 DWORD = 2 WORDS).
|
|
//
|
|
RtlInitUnicodeString(&ObUnicodeStr, pString);
|
|
if (RtlUnicodeStringToInteger(&ObUnicodeStr, Base, lpValue))
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
return (2);
|
|
}
|
|
|
|
//
|
|
// Get the length (in characters) of the string to copy.
|
|
//
|
|
if (Length == 0)
|
|
{
|
|
Length = NlsStrLenW(pString);
|
|
}
|
|
|
|
//
|
|
// Add one for the null termination. All strings should be null
|
|
// terminated.
|
|
//
|
|
Length++;
|
|
|
|
//
|
|
// Check cchData for size of given buffer.
|
|
//
|
|
if (cchData == 0)
|
|
{
|
|
//
|
|
// If cchData is 0, then we can't use lpCalData. In this
|
|
// case, we simply want to return the length (in characters) of
|
|
// the string to be copied.
|
|
//
|
|
return (Length);
|
|
}
|
|
else if (cchData < Length)
|
|
{
|
|
//
|
|
// The buffer is too small for the string, so return an error
|
|
// and zero bytes written.
|
|
//
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Copy the string to lpCalData and null terminate it.
|
|
// Return the number of characters copied.
|
|
//
|
|
wcsncpy(lpCalData, pString, Length - 1);
|
|
lpCalData[Length - 1] = 0;
|
|
return (Length);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetCalendarInfoW
|
|
//
|
|
// Sets one of the various pieces of information about a particular
|
|
// calendar by making an entry in the user's portion of the configuration
|
|
// registry. This will only affect the user override portion of the
|
|
// calendar settings. The system defaults will never be reset.
|
|
//
|
|
// 12-17-97 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI SetCalendarInfoW(
|
|
LCID Locale,
|
|
CALID Calendar,
|
|
CALTYPE CalType,
|
|
LPCWSTR lpCalData)
|
|
{
|
|
PLOC_HASH pHashN; // ptr to LOC hash node
|
|
int cchData; // length of lpLCData
|
|
PCAL_INFO pCalInfo; // ptr to calendar info
|
|
UNICODE_STRING ObUnicodeStr; // value string
|
|
DWORD Value; // value
|
|
|
|
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - validate LCID
|
|
// - NULL data pointer
|
|
//
|
|
// NOTE: invalid type is checked in the switch statement below.
|
|
//
|
|
VALIDATE_LOCALE(Locale, pHashN, FALSE);
|
|
if ((pHashN == NULL) || (lpCalData == NULL))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the length of the buffer.
|
|
//
|
|
cchData = NlsStrLenW(lpCalData) + 1;
|
|
|
|
//
|
|
// Validate the Calendar parameter.
|
|
//
|
|
if (GetCalendar(Calendar, &pCalInfo) != NO_ERROR)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the appropriate user information for the given CALTYPE.
|
|
//
|
|
CalType &= (~CAL_USE_CP_ACP);
|
|
switch (CalType)
|
|
{
|
|
case ( CAL_ITWODIGITYEARMAX ) :
|
|
{
|
|
//
|
|
// Get the default value to make sure the calendar is
|
|
// allowed to be set. Things like the Japanese Era calendar
|
|
// may not be set.
|
|
//
|
|
RtlInitUnicodeString( &ObUnicodeStr,
|
|
((LPWORD)pCalInfo +
|
|
(((PCALENDAR_VAR)pCalInfo)->STwoDigitYearMax)) );
|
|
RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value);
|
|
if (Value <= 99)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Convert the user data so it can be validated
|
|
//
|
|
Value = 0;
|
|
NLS_STRING_TO_INTEGER(Value, lpCalData);
|
|
|
|
//
|
|
// Validate the new value. It should be no longer than
|
|
// MAX_ITWODIGITYEAR wide characters in length.
|
|
// It should be between 99 and 9999.
|
|
//
|
|
if ((cchData > MAX_ITWODIGITYEAR) ||
|
|
(Value < 99) ||
|
|
(Value > 9999))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the registry with the new TwoDigitYearMax string.
|
|
//
|
|
return (SetTwoDigitYearInfo(Calendar, lpCalData, cchData));
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// INTERNAL ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetUserInfo
|
|
//
|
|
// This routine sets the given value in the registry with the given data.
|
|
// All values must be of the type REG_SZ.
|
|
//
|
|
// NOTE: The handle to the registry key must be closed by the CALLER if
|
|
// the return value is NO_ERROR.
|
|
//
|
|
// 07-14-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL SetUserInfo(
|
|
LCTYPE LCType,
|
|
LPWSTR pData,
|
|
ULONG DataLength)
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Get the length of the value string.
|
|
//
|
|
DataLength *= sizeof(WCHAR);
|
|
|
|
//
|
|
// If there is no logged on user or the current security context
|
|
// isn't the logged-on interactive user, then set the registry
|
|
// value directly.
|
|
//
|
|
if (! NT_SUCCESS( NlsCheckForInteractiveUser() ))
|
|
{
|
|
return (SetCurrentUserRegValue(LCType, pData, DataLength));
|
|
}
|
|
|
|
// Call into server side (csrss.exe) to set the registry and update the cache for the current user.
|
|
Status = CsrBasepNlsSetUserInfo(LCType,
|
|
pData,
|
|
DataLength);
|
|
|
|
//
|
|
// Check to see if the "set" operation succeeded.
|
|
//
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// We got a failure. Try using just the registry apis to set the
|
|
// registry. It's possible that the cache is not valid yet if this
|
|
// is called from setup or winlogon.
|
|
//
|
|
return (SetCurrentUserRegValue(LCType, pData, DataLength));
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetCurrentUserRegValue
|
|
//
|
|
// Set the registry value for the current security context. This routine
|
|
// is called when the current security context is different from the logged
|
|
// on user.
|
|
//
|
|
// 12-26-98 SamerA Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL SetCurrentUserRegValue(
|
|
LCTYPE LCType,
|
|
LPWSTR pData,
|
|
ULONG DataLength)
|
|
{
|
|
HANDLE hKey = NULL;
|
|
LPWSTR pValue;
|
|
LPWSTR pCache;
|
|
|
|
if (0 == ValidateLCType(pNlsUserInfo, LCType, &pValue, &pCache))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Open the registry for the current security context
|
|
//
|
|
OPEN_CPANEL_INTL_KEY(hKey, FALSE, KEY_READ | KEY_WRITE);
|
|
if (SetRegValue(hKey, pValue, pData, DataLength) != NO_ERROR)
|
|
{
|
|
CLOSE_REG_KEY(hKey);
|
|
SetLastError(ERROR_INVALID_ACCESS);
|
|
return (FALSE);
|
|
}
|
|
|
|
CLOSE_REG_KEY(hKey);
|
|
|
|
//
|
|
// Flush the process cache entry, if needed.
|
|
//
|
|
NlsFlushProcessCache(LCType);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetMultipleUserInfoInRegistry
|
|
//
|
|
// This routine sets the given multiple values in the registry with the
|
|
// given data. All values must be of the type REG_SZ.
|
|
//
|
|
// 06-11-98 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL SetMultipleUserInfoInRegistry(
|
|
DWORD dwFlags,
|
|
int cchData,
|
|
LPCWSTR pPicture,
|
|
LPCWSTR pSeparator,
|
|
LPCWSTR pOrder,
|
|
LPCWSTR pTLZero,
|
|
LPCWSTR pTimeMarkPosn)
|
|
{
|
|
HANDLE hKey = NULL;
|
|
ULONG rc = 0L;
|
|
|
|
|
|
//
|
|
// Open the Control Panel International registry key.
|
|
//
|
|
OPEN_CPANEL_INTL_KEY(hKey, FALSE, KEY_READ | KEY_WRITE);
|
|
|
|
//
|
|
// Save the appropriate values in the registry based on the flags.
|
|
//
|
|
switch (dwFlags)
|
|
{
|
|
case ( LOCALE_STIMEFORMAT ) :
|
|
{
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_STIMEFORMAT,
|
|
pPicture,
|
|
cchData * sizeof(WCHAR) );
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_STIMEFORMAT);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_STIME,
|
|
pSeparator,
|
|
(lstrlen(pSeparator) + 1) * sizeof(WCHAR) );
|
|
}
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_STIME);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_ITIME,
|
|
pOrder,
|
|
(lstrlen(pOrder) + 1) * sizeof(WCHAR) );
|
|
}
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_ITIME);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_ITLZERO,
|
|
pTLZero,
|
|
(lstrlen(pTLZero) + 1) * sizeof(WCHAR) );
|
|
}
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_ITLZERO);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_ITIMEMARKPOSN,
|
|
pTimeMarkPosn,
|
|
(lstrlen(pTimeMarkPosn) + 1) * sizeof(WCHAR) );
|
|
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_ITIMEMARKPOSN);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_STIME ) :
|
|
{
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_STIME,
|
|
pSeparator,
|
|
cchData * sizeof(WCHAR) );
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_STIME);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_STIMEFORMAT,
|
|
pPicture,
|
|
(lstrlen(pPicture) + 1) * sizeof(WCHAR) );
|
|
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_STIMEFORMAT);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_ITIME ) :
|
|
{
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_ITIME,
|
|
pOrder,
|
|
cchData * sizeof(WCHAR) );
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_ITIME);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_STIMEFORMAT,
|
|
pPicture,
|
|
(lstrlen(pPicture) + 1) * sizeof(WCHAR) );
|
|
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_STIMEFORMAT);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_SSHORTDATE ) :
|
|
{
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_SSHORTDATE,
|
|
pPicture,
|
|
cchData * sizeof(WCHAR) );
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_SSHORTDATE);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_SDATE,
|
|
pSeparator,
|
|
(lstrlen(pSeparator) + 1) * sizeof(WCHAR) );
|
|
}
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_SDATE);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_IDATE,
|
|
pOrder,
|
|
(lstrlen(pOrder) + 1) * sizeof(WCHAR) );
|
|
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_IDATE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( LOCALE_SDATE ) :
|
|
{
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_SDATE,
|
|
pSeparator,
|
|
cchData * sizeof(WCHAR) );
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_SDATE);
|
|
|
|
rc = SetRegValue( hKey,
|
|
NLS_VALUE_SSHORTDATE,
|
|
pPicture,
|
|
(lstrlen(pPicture) + 1) * sizeof(WCHAR) );
|
|
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
NlsFlushProcessCache(LOCALE_SSHORTDATE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
CLOSE_REG_KEY(hKey);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the registry key.
|
|
//
|
|
CLOSE_REG_KEY(hKey);
|
|
|
|
//
|
|
// Return the result.
|
|
//
|
|
return (rc == NO_ERROR);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetMultipleUserInfo
|
|
//
|
|
// This routine calls the server to set multiple registry values. This way,
|
|
// only one client/server transition is necessary.
|
|
//
|
|
// 08-19-94 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL SetMultipleUserInfo(
|
|
DWORD dwFlags,
|
|
int cchData,
|
|
LPCWSTR pPicture,
|
|
LPCWSTR pSeparator,
|
|
LPCWSTR pOrder,
|
|
LPCWSTR pTLZero,
|
|
LPCWSTR pTimeMarkPosn)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// If there is no logged on user or the current security context
|
|
// isn't the logged-on interactive user, then set the registry
|
|
// value directly.
|
|
//
|
|
if (! NT_SUCCESS( NlsCheckForInteractiveUser() ))
|
|
{
|
|
if (SetMultipleUserInfoInRegistry( dwFlags,
|
|
cchData,
|
|
pPicture,
|
|
pSeparator,
|
|
pOrder,
|
|
pTLZero,
|
|
pTimeMarkPosn ) == FALSE)
|
|
{
|
|
SetLastError(ERROR_INVALID_ACCESS);
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
Status = CsrBasepNlsSetMultipleUserInfo(dwFlags,
|
|
cchData,
|
|
pPicture,
|
|
pSeparator,
|
|
pOrder,
|
|
pTLZero,
|
|
pTimeMarkPosn
|
|
);
|
|
//
|
|
// Check to see if the "set" operation succeeded.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// We got a failure. Try using just the registry apis to set the
|
|
// registry. It's possible that the cache is not valid yet if this
|
|
// is called from setup or winlogon.
|
|
//
|
|
if (SetMultipleUserInfoInRegistry( dwFlags,
|
|
cchData,
|
|
pPicture,
|
|
pSeparator,
|
|
pOrder,
|
|
pTLZero,
|
|
pTimeMarkPosn ) == FALSE)
|
|
{
|
|
SetLastError(ERROR_INVALID_ACCESS);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetTwoDigitYearInfo
|
|
//
|
|
// This routine gets the two digit year info from the registry.
|
|
//
|
|
// 12-17-97 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL GetTwoDigitYearInfo(
|
|
CALID Calendar,
|
|
LPWSTR pYearInfo,
|
|
size_t cchYearInfo,
|
|
PWSTR pwszKeyPath)
|
|
{
|
|
HANDLE hKey = NULL; // handle to key
|
|
WCHAR pCalStr[MAX_PATH]; // ptr to calendar id string
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
|
|
BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
|
|
BOOL IfAlloc = FALSE; // if buffer was allocated
|
|
ULONG rc = 0L; // return code
|
|
BOOL bResult = FALSE; // result
|
|
UNICODE_STRING ObUnicodeStr; // year string
|
|
DWORD Year; // year value
|
|
|
|
|
|
//
|
|
// Open the Control Panel International registry key.
|
|
//
|
|
if (OpenRegKey( &hKey,
|
|
NULL,
|
|
pwszKeyPath,
|
|
KEY_READ ) != NO_ERROR)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Convert calendar value to Unicode string.
|
|
//
|
|
if (NlsConvertIntegerToString(Calendar, 10, 0, pCalStr, MAX_PATH))
|
|
{
|
|
NtClose(hKey);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Query the registry for the TwoDigitYearMax value.
|
|
//
|
|
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
|
|
rc = QueryRegValue( hKey,
|
|
pCalStr,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
&IfAlloc );
|
|
|
|
//
|
|
// Close the registry key.
|
|
//
|
|
NtClose(hKey);
|
|
|
|
//
|
|
// See if the TwoDigitYearMax value is present.
|
|
//
|
|
if (rc != NO_ERROR)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// See if the TwoDigitYearMax data is present.
|
|
//
|
|
if (pKeyValueFull->DataLength > 2)
|
|
{
|
|
//
|
|
// Copy the info
|
|
//
|
|
if(FAILED(StringCchCopyW(pYearInfo, cchYearInfo, GET_VALUE_DATA_PTR(pKeyValueFull))))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure the value is between 99 and 9999.
|
|
//
|
|
RtlInitUnicodeString(&ObUnicodeStr, pYearInfo);
|
|
if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Year) == NO_ERROR) &&
|
|
(Year >= 99) && (Year <= 9999))
|
|
{
|
|
bResult = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the buffer used for the query.
|
|
//
|
|
if (IfAlloc)
|
|
{
|
|
NLS_FREE_MEM(pKeyValueFull);
|
|
}
|
|
|
|
//
|
|
// Return the result.
|
|
//
|
|
return (bResult);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetTwoDigitYearInfo
|
|
//
|
|
// This routine sets the two digit year info in the registry.
|
|
//
|
|
// 12-17-97 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL SetTwoDigitYearInfo(
|
|
CALID Calendar,
|
|
LPCWSTR pYearInfo,
|
|
int cchData)
|
|
{
|
|
HANDLE hKey = NULL; // handle to key
|
|
WCHAR pCalStr[MAX_PATH]; // ptr to calendar id string
|
|
ULONG rc = 0L; // return code
|
|
|
|
|
|
//
|
|
// Open the Control Panel International registry key.
|
|
// If it doesn't exist, then we have to create each subkey
|
|
// separately.
|
|
//
|
|
if (OpenRegKey( &hKey,
|
|
NULL,
|
|
NLS_TWO_DIGIT_YEAR_KEY,
|
|
KEY_READ | KEY_WRITE ) != NO_ERROR)
|
|
{
|
|
//
|
|
// Registry key does not exist, so create each subkey
|
|
// separately.
|
|
//
|
|
if (CreateRegKey( &hKey,
|
|
NULL,
|
|
NLS_CALENDARS_KEY,
|
|
KEY_READ | KEY_WRITE ) == NO_ERROR)
|
|
{
|
|
NtClose(hKey);
|
|
if (CreateRegKey( &hKey,
|
|
NULL,
|
|
NLS_TWO_DIGIT_YEAR_KEY,
|
|
KEY_READ | KEY_WRITE ) != NO_ERROR)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure all Gregorian calendars are set to the same value.
|
|
//
|
|
switch (Calendar)
|
|
{
|
|
case ( 1 ) :
|
|
case ( 2 ) :
|
|
case ( 9 ) :
|
|
case ( 10 ) :
|
|
case ( 11 ) :
|
|
case ( 12 ) :
|
|
{
|
|
rc = SetRegValue(hKey, L"1", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
|
|
if (rc == NO_ERROR)
|
|
{
|
|
rc = SetRegValue(hKey, L"2", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
|
|
}
|
|
if (rc == NO_ERROR)
|
|
{
|
|
rc = SetRegValue(hKey, L"9", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
|
|
}
|
|
if (rc == NO_ERROR)
|
|
{
|
|
rc = SetRegValue(hKey, L"10", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
|
|
}
|
|
if (rc == NO_ERROR)
|
|
{
|
|
rc = SetRegValue(hKey, L"11", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
|
|
}
|
|
if (rc == NO_ERROR)
|
|
{
|
|
rc = SetRegValue(hKey, L"12", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
|
|
}
|
|
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
//
|
|
// Convert calendar value to Unicode string.
|
|
//
|
|
if (NlsConvertIntegerToString(Calendar, 10, 0, pCalStr, MAX_PATH))
|
|
{
|
|
NtClose(hKey);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the TwoDigitYearMax value in the registry.
|
|
//
|
|
rc = SetRegValue(hKey, pCalStr, pYearInfo, (ULONG)cchData * sizeof(WCHAR));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the NlsCacheUpdateCount inside csrss
|
|
//
|
|
if (rc == NO_ERROR)
|
|
{
|
|
CsrBasepNlsUpdateCacheCount();
|
|
}
|
|
|
|
//
|
|
// Close the registry key.
|
|
//
|
|
NtClose(hKey);
|
|
|
|
//
|
|
// Return the result.
|
|
//
|
|
return (rc == NO_ERROR);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetNLSVersion
|
|
//
|
|
// Return the version of a specific NLS function.
|
|
//
|
|
// 08-15-2001 LGuindon Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
BOOL WINAPI GetNLSVersion(
|
|
NLS_FUNCTION function,
|
|
LCID locale,
|
|
LPNLSVERSIONINFO lpVersionInformation)
|
|
{
|
|
PLOC_HASH pHashN;
|
|
|
|
//
|
|
// Invalid parameter check:
|
|
// - validate LCID
|
|
// - NULL data pointer
|
|
//
|
|
// NOTE: invalid function is checked in the switch statement below.
|
|
//
|
|
VALIDATE_LOCALE(locale, pHashN, FALSE);
|
|
if ((lpVersionInformation == NULL) || (pHashN == NULL))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Buffer size check.
|
|
//
|
|
if (lpVersionInformation->dwNLSVersionInfoSize != sizeof(NLSVERSIONINFO))
|
|
{
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure the appropriate tables are available. If not,
|
|
// return an error.
|
|
//
|
|
if (pTblPtrs->pSortVersion == NULL )
|
|
{
|
|
KdPrint(("NLSAPI: Appropriate Versioning Table Not Loaded.\n"));
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure the appropriate tables are available. If not,
|
|
// return an error.
|
|
//
|
|
if (pTblPtrs->pDefinedVersion == NULL)
|
|
{
|
|
KdPrint(("NLSAPI: Appropriate Defined Code Point Table Not Loaded.\n"));
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Check which NLS functionnality version is requested.
|
|
//
|
|
switch (function)
|
|
{
|
|
case (COMPARE_STRING):
|
|
{
|
|
UINT i;
|
|
|
|
//
|
|
// Get the Defined version. Always the first entry
|
|
// in the Defined Code Point version table. The first
|
|
// entry represent the current Defined version.
|
|
//
|
|
|
|
lpVersionInformation->dwDefinedVersion = (pTblPtrs->pDefinedVersion)[0].Version;
|
|
|
|
//
|
|
// Get the NLS sorting version. Search specific locale
|
|
// version info. Start from the second entry; after default
|
|
// value.
|
|
//
|
|
lpVersionInformation->dwNLSVersion = (pTblPtrs->pSortVersion)[0].Version;
|
|
for (i = 1; i < pTblPtrs->NumSortVersion; i++)
|
|
{
|
|
if (pHashN->Locale == (pTblPtrs->pSortVersion)[i].Locale)
|
|
{
|
|
lpVersionInformation->dwNLSVersion = (pTblPtrs->pSortVersion)[i].Version;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
// case (NORMALIZE_STRING):
|
|
// {
|
|
// //
|
|
// // Not implemented yet.
|
|
// //
|
|
// SetLastError(ERROR_NOT_SUPPORTED);
|
|
// return (FALSE);
|
|
// }
|
|
default:
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsNLSDefinedString
|
|
//
|
|
// This routine looks for code points inside a string to see if they are
|
|
// defined within the NSL context. If lpVersionInformation is NULL, the
|
|
// version is the current version. Same thing the dwDefinedVersion is equal
|
|
// to zero.
|
|
//
|
|
// 08-20-2001 LGuindon Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
BOOL WINAPI IsNLSDefinedString(
|
|
NLS_FUNCTION Function,
|
|
DWORD dwFlags,
|
|
LPNLSVERSIONINFO lpVersionInformation,
|
|
LPCWSTR lpString,
|
|
INT cchStr)
|
|
{
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - string is null
|
|
// - length of src string is 0
|
|
//
|
|
// NOTE:
|
|
// - invalid function is checked in the switch statement below.
|
|
// - version validity is checked below.
|
|
// - dwNLSVersionInfoSize is checked later in IsSortingCodePointDefined().
|
|
// - we don't check lpVersionInformation for NULL, since NULL means the current defined version.
|
|
//
|
|
if ((cchStr == 0) ||
|
|
(lpString == NULL))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Invalid Flag Check:
|
|
// - dwFlags is not zero
|
|
//
|
|
if (dwFlags != 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Buffer size check is checked below.
|
|
//
|
|
|
|
|
|
//
|
|
// Check string length.
|
|
//
|
|
if (cchStr <= (-1))
|
|
{
|
|
cchStr = NlsStrLenW(lpString);
|
|
}
|
|
|
|
//
|
|
// Check which NLS functionnality version is requested.
|
|
//
|
|
switch (Function)
|
|
{
|
|
case (COMPARE_STRING):
|
|
{
|
|
return (IsSortingCodePointDefined(lpVersionInformation, lpString, cchStr));
|
|
}
|
|
// case (NORMALIZE_STRING):
|
|
// {
|
|
//
|
|
// Not implemented yet.
|
|
//
|
|
// SetLastError(ERROR_NOT_SUPPORTED);
|
|
// return (FALSE);
|
|
// }
|
|
default:
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
|