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.
2119 lines
61 KiB
2119 lines
61 KiB
/*++
|
|
|
|
Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
util.c
|
|
|
|
Abstract:
|
|
|
|
This file contains utility functions that are shared across NLS's code
|
|
modules, but are not necessarily part of any of the existing code
|
|
modules.
|
|
|
|
Private APIs found in this file:
|
|
NlsGetCacheUpdateCount
|
|
|
|
External Routines found in this file:
|
|
IsValidSeparatorString
|
|
IsValidGroupingString
|
|
IsValidCalendarType
|
|
IsValidCalendarTypeStr
|
|
GetUserInfo
|
|
GetPreComposedChar
|
|
GetCompositeChars
|
|
InsertPreComposedForm
|
|
InsertFullWidthPreComposedForm
|
|
InsertCompositeForm
|
|
NlsConvertIntegerToString
|
|
NlsConvertIntegerToHexStringW
|
|
NlsConvertStringToIntegerW
|
|
NlsStrLenW
|
|
NlsStrEqualW
|
|
NlsStrNEqualW
|
|
GetStringTableEntry
|
|
NlsIsDll
|
|
|
|
|
|
Revision History:
|
|
|
|
05-31-91 JulieB Created.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
//
|
|
// Include Files.
|
|
//
|
|
|
|
#include "nls.h"
|
|
#include "nlssafe.h"
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// PRIVATE API ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsGetCacheUpdateCount
|
|
//
|
|
// Returns the current cache update count. The cache update count is
|
|
// updated whenever the HKCU\Control Panel\International settings are
|
|
// modified. This count allows the caller to see if the cache has been
|
|
// updated since the last time this function was called.
|
|
//
|
|
// This private api is needed by the Complex Script Language Pack
|
|
// (CSLPK) to enable it to quickly see if the international section of
|
|
// the registry has been modified.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG WINAPI NlsGetCacheUpdateCount(void)
|
|
{
|
|
return (pNlsUserInfo->ulCacheUpdateCount);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// EXTERNAL ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidSeparatorString
|
|
//
|
|
// Returns TRUE if the given string is valid. Otherwise, it returns FALSE.
|
|
//
|
|
// A valid string is one that does NOT contain any code points between
|
|
// L'0' and L'9', and does NOT have a length greater than the maximum.
|
|
//
|
|
// NOTE: The string must be a null terminated string.
|
|
//
|
|
// 10-12-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL IsValidSeparatorString(
|
|
LPCWSTR pString,
|
|
ULONG MaxLength,
|
|
BOOL fCheckZeroLen)
|
|
{
|
|
ULONG Length; // string length
|
|
LPWSTR pCur; // ptr to current position in string
|
|
|
|
|
|
//
|
|
// Search down the string to see if the chars are valid.
|
|
// Save the length of the string.
|
|
//
|
|
pCur = (LPWSTR)pString;
|
|
while (*pCur)
|
|
{
|
|
if ((*pCur >= NLS_CHAR_ZERO) && (*pCur <= NLS_CHAR_NINE))
|
|
{
|
|
//
|
|
// String is NOT valid.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
pCur++;
|
|
}
|
|
Length = (ULONG)(pCur - (LPWSTR)pString);
|
|
|
|
//
|
|
// Make sure the length is not greater than the maximum allowed.
|
|
// Also, check for 0 length string (if appropriate).
|
|
//
|
|
if ((Length >= MaxLength) ||
|
|
((fCheckZeroLen) && (Length == 0)))
|
|
{
|
|
//
|
|
// String is NOT valid.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// String is valid.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidGroupingString
|
|
//
|
|
// Returns TRUE if the given string is valid. Otherwise, it returns FALSE.
|
|
//
|
|
// A valid string is one that begins and ends with a number between
|
|
// L'0' and L'9', alternates between a number and a semicolon, and does
|
|
// NOT have a length greater than the maximum.
|
|
// (eg. 3;2;0 or 3;0 or 0 or 3)
|
|
//
|
|
// NOTE: The string must be a null terminated string.
|
|
//
|
|
// 01-05-98 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL IsValidGroupingString(
|
|
LPCWSTR pString,
|
|
ULONG MaxLength,
|
|
BOOL fCheckZeroLen)
|
|
{
|
|
ULONG Length; // string length
|
|
LPWSTR pCur; // ptr to current position in string
|
|
|
|
|
|
//
|
|
// Search down the string to see if the chars are valid.
|
|
// Save the length of the string.
|
|
//
|
|
pCur = (LPWSTR)pString;
|
|
while (*pCur)
|
|
{
|
|
if ((*pCur < NLS_CHAR_ZERO) || (*pCur > NLS_CHAR_NINE))
|
|
{
|
|
//
|
|
// String is NOT valid.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
pCur++;
|
|
|
|
if (*pCur)
|
|
{
|
|
if ((*pCur != NLS_CHAR_SEMICOLON) || (*(pCur + 1) == 0))
|
|
{
|
|
//
|
|
// String is NOT valid.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
pCur++;
|
|
}
|
|
}
|
|
Length = (ULONG)(pCur - (LPWSTR)pString);
|
|
|
|
//
|
|
// Make sure the length is not greater than the maximum allowed.
|
|
// Also, check for 0 length string (if appropriate).
|
|
//
|
|
if ((Length >= MaxLength) ||
|
|
((fCheckZeroLen) && (Length == 0)))
|
|
{
|
|
//
|
|
// String is NOT valid.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// String is valid.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidCalendarType
|
|
//
|
|
// Returns the pointer to the optional calendar structure if the given
|
|
// calendar type is valid for the given locale. Otherwise, it returns
|
|
// NULL.
|
|
//
|
|
// 10-12-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LPWORD IsValidCalendarType(
|
|
PLOC_HASH pHashN,
|
|
CALID CalId)
|
|
{
|
|
LPWORD pOptCal; // ptr to list of optional calendars
|
|
LPWORD pEndOptCal; // ptr to end of list of optional calendars
|
|
|
|
|
|
//
|
|
// Make sure the Cal Id is not zero, since that may be in the
|
|
// optional calendar section (meaning no optional calendars).
|
|
//
|
|
if (CalId == 0)
|
|
{
|
|
return (NULL);
|
|
}
|
|
|
|
//
|
|
// Search down the list of optional calendars.
|
|
//
|
|
pOptCal = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->IOptionalCal;
|
|
pEndOptCal = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SDayName1;
|
|
while (pOptCal < pEndOptCal)
|
|
{
|
|
//
|
|
// Check the calendar ids.
|
|
//
|
|
if (CalId == ((POPT_CAL)pOptCal)->CalId)
|
|
{
|
|
//
|
|
// Calendar id is valid for the given locale.
|
|
//
|
|
return (pOptCal);
|
|
}
|
|
|
|
//
|
|
// Increment to the next optional calendar.
|
|
//
|
|
pOptCal += ((POPT_CAL)pOptCal)->Offset;
|
|
}
|
|
|
|
//
|
|
// Calendar id is NOT valid if this point is reached.
|
|
//
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidCalendarTypeStr
|
|
//
|
|
// Converts the calendar string to an integer and validates the calendar
|
|
// id for the given locale. It return a pointer to the optional calendar
|
|
// structure, or null if the calendar id was invalid.
|
|
//
|
|
// 10-19-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LPWORD IsValidCalendarTypeStr(
|
|
PLOC_HASH pHashN,
|
|
LPCWSTR pCalStr)
|
|
{
|
|
UNICODE_STRING ObUnicodeStr; // value string
|
|
CALID CalNum; // calendar id
|
|
|
|
|
|
//
|
|
// Convert the string to an integer value.
|
|
//
|
|
RtlInitUnicodeString(&ObUnicodeStr, pCalStr);
|
|
if (RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &CalNum))
|
|
{
|
|
return (NULL);
|
|
}
|
|
|
|
//
|
|
// Validate the calendar id and return the pointer to the
|
|
// optional calendar structure.
|
|
//
|
|
return (IsValidCalendarType(pHashN, CalNum));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetCPFileNameFromRegistry
|
|
//
|
|
// Gets the name of the code page file from the registry. If pResultBuf
|
|
// or Size == 0, then just return true if it exists in the registry, but
|
|
// don't return the actual value.
|
|
//
|
|
// 05-31-2002 ShawnSte Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL GetCPFileNameFromRegistry(
|
|
UINT CodePage,
|
|
LPWSTR pResultBuf,
|
|
UINT Size)
|
|
{
|
|
// Working things.
|
|
WCHAR pTmpBuf[MAX_SMALL_BUF_LEN]; // temp buffer
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
|
|
BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
|
|
|
|
//
|
|
// Convert value to unicode string.
|
|
//
|
|
if (!NT_SUCCESS(NlsConvertIntegerToString( CodePage,
|
|
10,
|
|
0,
|
|
pTmpBuf,
|
|
MAX_SMALL_BUF_LEN )))
|
|
{
|
|
// Didn't work. (Don't bother closing key though, its used globally)
|
|
return (FALSE);
|
|
}
|
|
|
|
// Open hCodePageKey, return false if it fails
|
|
OPEN_CODEPAGE_KEY(FALSE);
|
|
|
|
//
|
|
// Query the registry value for that code page.
|
|
//
|
|
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
|
|
if ( NO_ERROR != QueryRegValue( hCodePageKey,
|
|
pTmpBuf,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL ) )
|
|
{
|
|
// Didn't work. (Don't bother closing key though, its used globally)
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure there is data with this value.
|
|
//
|
|
if (GET_VALUE_DATA_PTR(pKeyValueFull)[0] == 0)
|
|
{
|
|
// Nope, no file name for this code page. (Not installed).
|
|
return (FALSE);
|
|
}
|
|
|
|
// It worked, see if that's all they wanted.
|
|
if (!pResultBuf || Size == 0)
|
|
{
|
|
// Caller didn't want the name, just to know if it was there
|
|
return (TRUE);
|
|
}
|
|
|
|
// Now we have to copy the name to their buffer for them.
|
|
if ( FAILED(StringCchCopyW(pResultBuf, Size, GET_VALUE_DATA_PTR(pKeyValueFull))))
|
|
{
|
|
// Couldn't make the string right, so fail
|
|
return (FALSE);
|
|
}
|
|
|
|
// Yea, it worked
|
|
return (TRUE);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetUserInfoFromRegistry
|
|
//
|
|
// Gets the information from the registry for the given value entry.
|
|
//
|
|
// 06-11-98 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL GetUserInfoFromRegistry(
|
|
LPWSTR pValue,
|
|
LPWSTR pOutput,
|
|
size_t cchOutput,
|
|
LCID Locale)
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
|
|
BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
|
|
HANDLE hKey = NULL; // handle to intl key
|
|
ULONG rc = 0L; // return code
|
|
|
|
|
|
//
|
|
// Open the Control Panel International registry key.
|
|
//
|
|
OPEN_CPANEL_INTL_KEY(hKey, FALSE, KEY_READ);
|
|
|
|
//
|
|
// Initialize the output string.
|
|
//
|
|
*pOutput = 0;
|
|
|
|
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
|
|
|
|
//
|
|
// Check to be sure the current user is running in the given locale.
|
|
//
|
|
if (Locale)
|
|
{
|
|
if (NO_ERROR == QueryRegValue( hKey,
|
|
L"Locale",
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL ))
|
|
{
|
|
UINT uiLocale;
|
|
|
|
if (NlsConvertStringToIntegerW(GET_VALUE_DATA_PTR(pKeyValueFull), 16, -1, &uiLocale) &&
|
|
uiLocale != Locale)
|
|
{
|
|
CLOSE_REG_KEY(hKey);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query the registry value.
|
|
//
|
|
rc = QueryRegValue( hKey,
|
|
pValue,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL );
|
|
|
|
//
|
|
// Close the registry key.
|
|
//
|
|
CLOSE_REG_KEY(hKey);
|
|
|
|
//
|
|
// If the query failed or if the output buffer is not large enough,
|
|
// then return failure.
|
|
//
|
|
if ((rc != NO_ERROR) ||
|
|
(pKeyValueFull->DataLength > (MAX_REG_VAL_SIZE * sizeof(WCHAR))))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Save the string in pOutput.
|
|
//
|
|
if(FAILED(StringCchCopyW(pOutput, cchOutput, GET_VALUE_DATA_PTR(pKeyValueFull))))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetUserInfo
|
|
//
|
|
// Gets the information from the registry for the given locale and user
|
|
// value entry.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL GetUserInfo(
|
|
LCID Locale,
|
|
LCTYPE LCType,
|
|
SIZE_T CacheOffset,
|
|
LPWSTR pValue,
|
|
LPWSTR pOutput,
|
|
size_t cchOutput,
|
|
BOOL fCheckNull)
|
|
{
|
|
LCID UserLocale;
|
|
HRESULT hr; // return val for string copy
|
|
LPWSTR pCacheString;
|
|
|
|
|
|
//
|
|
// Check if the current thread/process is impersonating
|
|
// or running in the context of a user other than the
|
|
// interactive one.
|
|
//
|
|
if (NT_SUCCESS( NlsGetCurrentUserNlsInfo( Locale,
|
|
LCType,
|
|
pValue,
|
|
pOutput,
|
|
cchOutput,
|
|
FALSE )))
|
|
{
|
|
//
|
|
// See if we need to check for a null string.
|
|
//
|
|
if ((fCheckNull) && (*pOutput == 0))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Running in the same security context as the logged-on user.
|
|
//
|
|
|
|
|
|
RtlEnterCriticalSection(&gcsNlsProcessCache);
|
|
if (pNlsUserInfo->ulCacheUpdateCount != pServerNlsUserInfo->ulCacheUpdateCount)
|
|
{
|
|
//
|
|
// The cache content is out of date. Server has the latest copy of the cache, call server to update the
|
|
// the cache.
|
|
//
|
|
{
|
|
if (!NT_SUCCESS(CsrBasepNlsGetUserInfo(pNlsUserInfo, sizeof(NLS_USER_INFO))))
|
|
{
|
|
RtlLeaveCriticalSection(&gcsNlsProcessCache);
|
|
//
|
|
// The call to client failed, try to get the data from table.
|
|
return (FALSE);
|
|
}
|
|
}
|
|
//
|
|
// If the call to server side succeeds, now we garantee that we have a complete
|
|
// cache data, that is copied from the server side cache. It will have the same
|
|
// ulCacheUpdateCount in the time when the call to server side happens.
|
|
//
|
|
}
|
|
|
|
//
|
|
// We are in critical section here to check UserLocale to make sure that LCID and the data are in sync.
|
|
//
|
|
UserLocale = pNlsUserInfo->UserLocaleId;
|
|
|
|
//
|
|
// Check to be sure cached user locale is the same as the given locale.
|
|
//
|
|
if (Locale != UserLocale)
|
|
{
|
|
RtlLeaveCriticalSection(&gcsNlsProcessCache);
|
|
return (FALSE);
|
|
}
|
|
|
|
pCacheString = (LPWSTR)((LPBYTE)pNlsUserInfo + CacheOffset);
|
|
hr = StringCchCopyW(pOutput, MAX_REG_VAL_SIZE, pCacheString);
|
|
RtlLeaveCriticalSection(&gcsNlsProcessCache);
|
|
|
|
//
|
|
// Make sure the cache is valid.
|
|
//
|
|
// Also, check for an invalid entry. An invalid entry is marked
|
|
// with NLS_INVALID_INFO_CHAR in the first position of the string
|
|
// array.
|
|
//
|
|
if (FAILED(hr) || (*pOutput == NLS_INVALID_INFO_CHAR))
|
|
{
|
|
//
|
|
// The cache is invalid, so try getting the information directly
|
|
// from the registry.
|
|
//
|
|
return (GetUserInfoFromRegistry(pValue, pOutput, cchOutput, Locale));
|
|
}
|
|
|
|
//
|
|
// See if we need to check for a null string.
|
|
//
|
|
if ((fCheckNull) && (*pOutput == 0))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetPreComposedChar
|
|
//
|
|
// Gets the precomposed character form of a given base character and
|
|
// nonspacing character. If there is no precomposed form for the given
|
|
// character, it returns 0.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
WCHAR FASTCALL GetPreComposedChar(
|
|
WCHAR wcNonSp,
|
|
WCHAR wcBase)
|
|
{
|
|
PCOMP_INFO pComp; // ptr to composite information
|
|
WORD BSOff = 0; // offset of base char in grid
|
|
WORD NSOff = 0; // offset of nonspace char in grid
|
|
int Index; // index into grid
|
|
|
|
|
|
//
|
|
// Store the ptr to the composite information. No need to check if
|
|
// it's a NULL pointer since all tables in the Unicode file are
|
|
// constructed during initialization.
|
|
//
|
|
pComp = pTblPtrs->pComposite;
|
|
|
|
//
|
|
// Traverse 8:4:4 table for Base character offset.
|
|
//
|
|
BSOff = TRAVERSE_844_W(pComp->pBase, wcBase);
|
|
if (!BSOff)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Traverse 8:4:4 table for NonSpace character offset.
|
|
//
|
|
NSOff = TRAVERSE_844_W(pComp->pNonSp, wcNonSp);
|
|
if (!NSOff)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Get wide character value out of 2D grid.
|
|
// If there is no precomposed character at the location in the
|
|
// grid, it will return 0.
|
|
//
|
|
Index = (BSOff - 1) * pComp->NumNonSp + (NSOff - 1);
|
|
return ((pComp->pGrid)[Index]);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetCompositeChars
|
|
//
|
|
// Gets the composite characters of a given wide character. If the
|
|
// composite form is found, it returns TRUE. Otherwise, it returns
|
|
// FALSE.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL FASTCALL GetCompositeChars(
|
|
WCHAR wch,
|
|
WCHAR *pNonSp,
|
|
WCHAR *pBase)
|
|
{
|
|
PPRECOMP pPreComp; // ptr to precomposed information
|
|
|
|
|
|
//
|
|
// Store the ptr to the precomposed information. No need to check if
|
|
// it's a NULL pointer since all tables in the Unicode file are
|
|
// constructed during initialization.
|
|
//
|
|
pPreComp = pTblPtrs->pPreComposed;
|
|
|
|
//
|
|
// Traverse 8:4:4 table for base and nonspace character translation.
|
|
//
|
|
TRAVERSE_844_D(pPreComp, wch, *pNonSp, *pBase);
|
|
|
|
//
|
|
// Return success if found. Otherwise, error.
|
|
//
|
|
return ((*pNonSp) && (*pBase));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InsertPreComposedForm
|
|
//
|
|
// Gets the precomposed form of a given wide character string, places it in
|
|
// the given wide character, and returns the number of composite characters
|
|
// used to form the precomposed form. If there is no precomposed form for
|
|
// the given character, nothing is written into pPreComp and it returns 1
|
|
// for the number of characters used.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int FASTCALL InsertPreComposedForm(
|
|
LPCWSTR pWCStr,
|
|
LPWSTR pEndWCStr,
|
|
LPWSTR pPreComp)
|
|
{
|
|
WCHAR wch; // precomposed character
|
|
LPWSTR pPos; // ptr to position in string
|
|
|
|
|
|
//
|
|
// If no precomposed form can be found, return 1 character used
|
|
// (base character).
|
|
//
|
|
if (((pWCStr + 1) >= pEndWCStr) ||
|
|
(!(wch = GetPreComposedChar(*(pWCStr + 1), *pWCStr))))
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
//
|
|
// Get the precomposed character from the given wide character string.
|
|
// Must check for multiple nonspacing characters for the same
|
|
// precomposed character.
|
|
//
|
|
*pPreComp = wch;
|
|
pPos = (LPWSTR)pWCStr + 2;
|
|
while ((pPos < pEndWCStr) &&
|
|
(wch = GetPreComposedChar(*pPos, *pPreComp)))
|
|
{
|
|
*pPreComp = wch;
|
|
pPos++;
|
|
}
|
|
|
|
//
|
|
// Return the number of characters used to form the precomposed
|
|
// character.
|
|
//
|
|
return ((int)(pPos - (LPWSTR)pWCStr));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InsertFullWidthPreComposedForm
|
|
//
|
|
// Gets the full width precomposed form of a given wide character string,
|
|
// places it in the given wide character, and returns the number of
|
|
// composite characters used to form the precomposed form. If there is
|
|
// no precomposed form for the given character, only the full width conversion
|
|
// of the first code point is written into pPreComp and it returns 1 for
|
|
// the number of characters used.
|
|
//
|
|
// 11-04-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int FASTCALL InsertFullWidthPreComposedForm(
|
|
LPCWSTR pWCStr,
|
|
LPWSTR pEndWCStr,
|
|
LPWSTR pPreComp,
|
|
PCASE pCase)
|
|
{
|
|
WCHAR wch; // nonspace character
|
|
LPWSTR pPos; // ptr to position in string
|
|
|
|
|
|
//
|
|
// Get the case (if necessary).
|
|
//
|
|
*pPreComp = (pCase) ? GET_LOWER_UPPER_CASE(pCase, *pWCStr) : *pWCStr;
|
|
|
|
//
|
|
// Get the full width.
|
|
//
|
|
*pPreComp = GET_FULL_WIDTH(pTblPtrs->pFullWidth, *pPreComp);
|
|
|
|
if ((pPos = ((LPWSTR)pWCStr + 1)) >= pEndWCStr)
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
while (pPos < pEndWCStr)
|
|
{
|
|
wch = (pCase) ? GET_LOWER_UPPER_CASE(pCase, *pPos) : *pPos;
|
|
wch = GET_FULL_WIDTH(pTblPtrs->pFullWidth, wch);
|
|
if (wch = GetPreComposedChar(wch, *pPreComp))
|
|
{
|
|
*pPreComp = wch;
|
|
pPos++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the number of characters used to form the precomposed
|
|
// character.
|
|
//
|
|
return ((int)(pPos - (LPWSTR)pWCStr));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InsertCompositeForm
|
|
//
|
|
// Gets the composite form of a given wide character, places it in the
|
|
// wide character string, and returns the number of characters written.
|
|
// If there is no composite form for the given character, the wide character
|
|
// string is not touched. It will return 1 for the number of characters
|
|
// written, since the base character was already written.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int FASTCALL InsertCompositeForm(
|
|
LPWSTR pWCStr,
|
|
LPWSTR pEndWCStr)
|
|
{
|
|
WCHAR Base; // base character
|
|
WCHAR NonSp; // non space character
|
|
int wcCount = 0; // number of wide characters written
|
|
LPWSTR pEndComp; // ptr to end of composite form
|
|
int ctr; // loop counter
|
|
|
|
|
|
//
|
|
// If no composite form can be found, return 1 for the base
|
|
// character that was already written.
|
|
//
|
|
if (!GetCompositeChars(*pWCStr, &NonSp, &Base))
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
//
|
|
// Get the composite characters and write them to the pWCStr
|
|
// buffer. Must check for multiple breakdowns of the precomposed
|
|
// character into more than 2 characters (multiple nonspacing
|
|
// characters).
|
|
//
|
|
pEndComp = pWCStr;
|
|
do
|
|
{
|
|
//
|
|
// Make sure pWCStr is big enough to hold the nonspacing
|
|
// character.
|
|
//
|
|
if (pEndComp < (pEndWCStr - 1))
|
|
{
|
|
//
|
|
// Addition of next breakdown of nonspacing characters
|
|
// are to be added right after the base character. So,
|
|
// move all nonspacing characters ahead one position
|
|
// to make room for the next nonspacing character.
|
|
//
|
|
pEndComp++;
|
|
for (ctr = 0; ctr < wcCount; ctr++)
|
|
{
|
|
*(pEndComp - ctr) = *(pEndComp - (ctr + 1));
|
|
}
|
|
|
|
//
|
|
// Fill in the new base form and the new nonspacing character.
|
|
//
|
|
*pWCStr = Base;
|
|
*(pWCStr + 1) = NonSp;
|
|
wcCount++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Make sure we don't get into an infinite loop if the
|
|
// destination buffer isn't large enough.
|
|
//
|
|
break;
|
|
}
|
|
} while (GetCompositeChars(*pWCStr, &NonSp, &Base));
|
|
|
|
//
|
|
// Return number of wide characters written. Add 1 to include the
|
|
// base character.
|
|
//
|
|
return (wcCount + 1);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsConvertIntegerToString
|
|
//
|
|
// This routine converts an integer to a Unicode string.
|
|
//
|
|
// 11-15-96 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG NlsConvertIntegerToString(
|
|
UINT Value,
|
|
UINT Base,
|
|
UINT Padding,
|
|
LPWSTR pResultBuf,
|
|
UINT Size)
|
|
{
|
|
UNICODE_STRING ObString; // value string
|
|
UINT ctr; // loop counter
|
|
LPWSTR pBufPtr; // ptr to result buffer
|
|
WCHAR pTmpBuf[MAX_PATH_LEN]; // ptr to temp buffer
|
|
ULONG rc = 0L; // return code
|
|
|
|
//
|
|
// Set up the Unicode string structure.
|
|
//
|
|
ObString.Length = (USHORT)(Size * sizeof(WCHAR));
|
|
ObString.MaximumLength = (USHORT)(Size * sizeof(WCHAR));
|
|
ObString.Buffer = pTmpBuf;
|
|
|
|
//
|
|
// Get the value as a string.
|
|
//
|
|
if (rc = RtlIntegerToUnicodeString(Value, Base, &ObString))
|
|
{
|
|
return (rc);
|
|
}
|
|
|
|
//
|
|
// Pad the string with the appropriate number of zeros.
|
|
//
|
|
pBufPtr = pResultBuf;
|
|
for (ctr = GET_WC_COUNT(ObString.Length);
|
|
ctr < Padding;
|
|
ctr++, pBufPtr++, Size--)
|
|
{
|
|
if( Size < 1 )
|
|
{
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
*pBufPtr = NLS_CHAR_ZERO;
|
|
}
|
|
|
|
if(FAILED(StringCchCopyW(pBufPtr, Size, ObString.Buffer)))
|
|
{
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsConvertIntegerToHexStringW
|
|
// Convert an integer value to an Unicode null-terminated string WITH
|
|
// leading zeros. E.g. 0x409 with Width 5 will be converted to L"0409".
|
|
// This function is faster than NlsConvertIntegerToString(), but it
|
|
// only supports hex numbers.
|
|
//
|
|
// Parameters:
|
|
// Value The number to be converted.
|
|
// UpperCase If TRUE, the hex digit will be uppercase.
|
|
// Str The buffer for the converted Unicode string.
|
|
// Width The character count of the buffer. The value should be the total
|
|
// heximal digit number plus one for null-terminiated.
|
|
// E.g. if the value is from 0x0000 - 0xffff, the Width should be 5.
|
|
//
|
|
// Return:
|
|
// TRUE if successful. FALSE if the width is not big enough to hold the converted string.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL FASTCALL NlsConvertIntegerToHexStringW(UINT Value, BOOL UpperCase, PWSTR Str, UINT CharCount)
|
|
{
|
|
int Digit;
|
|
PWSTR p;
|
|
|
|
if(Str == NULL)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
p = Str + CharCount - 1;
|
|
*p-- = L'\0';
|
|
while (p >= Str)
|
|
{
|
|
Digit = Value & 0xf;
|
|
if (Digit < 10)
|
|
{
|
|
Digit = Digit + L'0';
|
|
}
|
|
else
|
|
{
|
|
Digit = Digit - 10 + (UpperCase ? L'A' : L'a');
|
|
}
|
|
*p-- = (WCHAR)Digit;
|
|
Value >>= 4;
|
|
}
|
|
|
|
if (Value > 0)
|
|
{
|
|
//
|
|
// There are still digit remaining.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsConvertStringToIntegerW
|
|
//
|
|
// Parameters:
|
|
// Str the hex string to be converted.
|
|
// Base base
|
|
// CharCount
|
|
// the character count of the string (excluding the terminiated-null, if any).
|
|
// If the value is -1, this function assumes that
|
|
// Str is a null-terminated string.
|
|
// Result the pointer to the result.
|
|
//
|
|
// Result:
|
|
// TRUE if the operation is successful. FALSE if there is non-hex
|
|
// character in the string.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL FASTCALL NlsConvertStringToIntegerW(PWSTR Str, UINT Base, int CharCount, UINT* Result)
|
|
{
|
|
int i;
|
|
WCHAR Digit;
|
|
WCHAR c;
|
|
|
|
if (Str == NULL || Result == NULL)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
*Result = 0;
|
|
|
|
if (CharCount == -1)
|
|
{
|
|
while (c = *Str)
|
|
{
|
|
c = *Str;
|
|
if (c >= L'0' && c <= L'9')
|
|
{
|
|
Digit = c - L'0';
|
|
}
|
|
else if(Base == 16)
|
|
{
|
|
if (c >= L'A' && c <= L'F')
|
|
{
|
|
Digit = c - L'A' + 10;
|
|
}
|
|
else if (c >= L'a' && c <= L'f')
|
|
{
|
|
Digit = c - L'a' + 10;
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
if (Base == 16)
|
|
{
|
|
*Result = (*Result << 4) | Digit;
|
|
}
|
|
else
|
|
{
|
|
*Result = *Result*10 + Digit;
|
|
}
|
|
|
|
Str++;
|
|
}
|
|
} else
|
|
{
|
|
for (i=0; i< CharCount; i++) {
|
|
c = *Str++;
|
|
if (c >= L'0' && c <= L'9')
|
|
{
|
|
Digit = c - L'0';
|
|
}
|
|
else if(Base == 16)
|
|
{
|
|
if (c >= L'A' && c <= L'F')
|
|
{
|
|
Digit = c - L'A' + 10;
|
|
}
|
|
else if (c >= L'a' && c <= L'f')
|
|
{
|
|
Digit = c - L'a' + 10;
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
if (Base == 16)
|
|
{
|
|
*Result = (*Result << 4) | Digit;
|
|
}
|
|
else
|
|
{
|
|
*Result = *Result*10 + Digit;
|
|
}
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsStrLenW
|
|
//
|
|
// This routine returns the length of the given wide character string.
|
|
// The length does NOT include the null terminator.
|
|
//
|
|
// NOTE: This routine is here to avoid any dependencies on other DLLs
|
|
// during initialization.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int FASTCALL NlsStrLenW(
|
|
LPCWSTR pwsz)
|
|
{
|
|
LPCWSTR pwszStart = pwsz; // ptr to beginning of string
|
|
|
|
loop:
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
if (*pwsz) pwsz++; else goto done;
|
|
|
|
goto loop;
|
|
|
|
done:
|
|
return ((int)(pwsz - pwszStart));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsStrEqualW
|
|
//
|
|
// This routine compares two strings to see if they are exactly identical.
|
|
// It returns 1 if they are identical, 0 if they are different.
|
|
//
|
|
// NOTE: This routine is here to avoid any dependencies on other DLLs
|
|
// during initialization.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int FASTCALL NlsStrEqualW(
|
|
LPCWSTR pwszFirst,
|
|
LPCWSTR pwszSecond)
|
|
{
|
|
loop:
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
|
|
goto loop;
|
|
|
|
error:
|
|
//
|
|
// Return error.
|
|
//
|
|
return (0);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsStrNEqualW
|
|
//
|
|
// This routine compares two strings to see if they are exactly identical
|
|
// for the count of characters given.
|
|
// It returns 1 if they are identical, 0 if they are different.
|
|
//
|
|
// NOTE: This routine is here to avoid any dependencies on other DLLs
|
|
// during initialization.
|
|
//
|
|
// 05-31-91 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int FASTCALL NlsStrNEqualW(
|
|
LPCWSTR pwszFirst,
|
|
LPCWSTR pwszSecond,
|
|
int Count)
|
|
{
|
|
loop:
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
if (Count == 0) return (1);
|
|
if (*pwszFirst != *pwszSecond) goto error;
|
|
if (!*pwszFirst) return (1);
|
|
pwszFirst++;
|
|
pwszSecond++;
|
|
Count--;
|
|
|
|
goto loop;
|
|
|
|
error:
|
|
//
|
|
// Return error.
|
|
//
|
|
return (0);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetDefaultSortkeySize
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG GetDefaultSortkeySize(
|
|
PLARGE_INTEGER pSize)
|
|
{
|
|
*pSize = pTblPtrs->DefaultSortkeySize;
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetLinguistLangSize
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG GetLinguistLangSize(
|
|
PLARGE_INTEGER pSize)
|
|
{
|
|
*pSize = pTblPtrs->LinguistLangSize;
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ValidateLocale
|
|
//
|
|
// Internal routine, called from server. Validates that a locale is
|
|
// present in the registry. This code comes from IsValidLocale, but
|
|
// does not check the internal data to prevent recursive calls to the
|
|
// server.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL ValidateLocale(
|
|
LCID Locale)
|
|
{
|
|
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 Locale Check.
|
|
//
|
|
if (IS_INVALID_LOCALE(Locale))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// 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 the result.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ValidateLCType
|
|
//
|
|
// This routine is called from the server (and also from locale.c) in
|
|
// order to get a Registry key name and a field pointer in the NlsInfo
|
|
// structure given an LCType.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL ValidateLCType(
|
|
PNLS_USER_INFO pInfo,
|
|
LCTYPE LCType,
|
|
LPWSTR *ppwReg,
|
|
LPWSTR *ppwCache)
|
|
{
|
|
switch (LCType)
|
|
{
|
|
case ( LOCALE_IFIRSTWEEKOFYEAR ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_IFIRSTWEEKOFYEAR;
|
|
*ppwCache = pInfo->iFirstWeek;
|
|
break;
|
|
}
|
|
case ( LOCALE_IFIRSTDAYOFWEEK ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_IFIRSTDAYOFWEEK;
|
|
*ppwCache = pInfo->iFirstDay;
|
|
break;
|
|
}
|
|
case ( LOCALE_ICALENDARTYPE ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_ICALENDARTYPE;
|
|
*ppwCache = pInfo->iCalType;
|
|
break;
|
|
}
|
|
case ( LOCALE_SLONGDATE ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SLONGDATE;
|
|
*ppwCache = pInfo->sLongDate;
|
|
break;
|
|
}
|
|
case ( LOCALE_SYEARMONTH ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SYEARMONTH;
|
|
*ppwCache = pInfo->sYearMonth;
|
|
break;
|
|
}
|
|
case ( LOCALE_S1159 ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_S1159;
|
|
*ppwCache = pInfo->s1159;
|
|
break;
|
|
}
|
|
case ( LOCALE_SNEGATIVESIGN ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SNEGATIVESIGN;
|
|
*ppwCache = pInfo->sNegSign;
|
|
break;
|
|
}
|
|
case ( LOCALE_SPOSITIVESIGN ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SPOSITIVESIGN;
|
|
*ppwCache = pInfo->sPosSign;
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGCURR ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_INEGCURR;
|
|
*ppwCache = pInfo->iNegCurr;
|
|
break;
|
|
}
|
|
case ( LOCALE_ICURRENCY ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_ICURRENCY;
|
|
*ppwCache = pInfo->iCurrency;
|
|
break;
|
|
}
|
|
case ( LOCALE_ICURRDIGITS ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_ICURRDIGITS;
|
|
*ppwCache = pInfo->iCurrDigits;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONGROUPING ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SMONGROUPING;
|
|
*ppwCache = pInfo->sMonGrouping;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONTHOUSANDSEP ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SMONTHOUSANDSEP;
|
|
*ppwCache = pInfo->sMonThouSep;
|
|
break;
|
|
}
|
|
case ( LOCALE_SMONDECIMALSEP ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SMONDECIMALSEP;
|
|
*ppwCache = pInfo->sMonDecSep;
|
|
break;
|
|
}
|
|
case ( LOCALE_SCURRENCY ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SCURRENCY;
|
|
*ppwCache = pInfo->sCurrency;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDIGITSUBSTITUTION ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_IDIGITSUBST;
|
|
*ppwCache = pInfo->iDigitSubstitution;
|
|
break;
|
|
}
|
|
case ( LOCALE_SNATIVEDIGITS ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SNATIVEDIGITS;
|
|
*ppwCache = pInfo->sNativeDigits;
|
|
break;
|
|
}
|
|
case ( LOCALE_INEGNUMBER ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_INEGNUMBER;
|
|
*ppwCache = pInfo->iNegNumber;
|
|
break;
|
|
}
|
|
case ( LOCALE_ILZERO ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_ILZERO;
|
|
*ppwCache = pInfo->iLZero;
|
|
break;
|
|
}
|
|
case ( LOCALE_IDIGITS ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_IDIGITS;
|
|
*ppwCache = pInfo->iDigits;
|
|
break;
|
|
}
|
|
case ( LOCALE_SGROUPING ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SGROUPING;
|
|
*ppwCache = pInfo->sGrouping;
|
|
break;
|
|
}
|
|
case ( LOCALE_STHOUSAND ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_STHOUSAND;
|
|
*ppwCache = pInfo->sThousand;
|
|
break;
|
|
}
|
|
case ( LOCALE_SDECIMAL ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SDECIMAL;
|
|
*ppwCache = pInfo->sDecimal;
|
|
break;
|
|
}
|
|
case ( LOCALE_IPAPERSIZE ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_IPAPERSIZE;
|
|
*ppwCache = pInfo->iPaperSize;
|
|
break;
|
|
}
|
|
case ( LOCALE_IMEASURE ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_IMEASURE;
|
|
*ppwCache = pInfo->iMeasure;
|
|
break;
|
|
}
|
|
case ( LOCALE_SLIST ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_SLIST;
|
|
*ppwCache = pInfo->sList;
|
|
break;
|
|
}
|
|
case ( LOCALE_S2359 ) :
|
|
{
|
|
*ppwReg = NLS_VALUE_S2359;
|
|
*ppwCache = pInfo->s2359;
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetStringTableEntry
|
|
//
|
|
// Returns the localized version of the strings for the given resource
|
|
// id. It gets the information from the resource file in the language that
|
|
// the current user is using.
|
|
//
|
|
// The string table contains a series of strings in the following order:
|
|
// Language Name
|
|
// Country Name
|
|
// Language Group Name
|
|
// Code Page Name (decimal values converted to hex values)
|
|
// Region (Geo) Friendly Name (decimal values converted to hex values)
|
|
// Region (Geo) Official Name (decimal values converted to hex values)
|
|
// Sorting Names (in order starting with 0, separated by $)
|
|
//
|
|
// Each string is separated by $. The final string is terminated with
|
|
// a null.
|
|
//
|
|
// The sorting names are in order of the sort ids, starting with 0.
|
|
//
|
|
// For example,
|
|
// "Language$Country$LangGrp$CodePage$Geo1$Geo2$Sort0$Sort1" or
|
|
// "Language$Country" or
|
|
// "$$LangGrp$CodePage" or
|
|
// "$$$CodePage" or
|
|
// "$$$$Geo1$Geo2"
|
|
//
|
|
// 11-17-00 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int GetStringTableEntry(
|
|
UINT ResourceID,
|
|
LANGID UILangId,
|
|
LPWSTR pBuffer,
|
|
int cchBuffer,
|
|
int WhichString)
|
|
{
|
|
HANDLE hFindRes; // handle from find resource
|
|
HANDLE hLoadRes; // handle from load resource
|
|
LPWSTR pSearch, pSearchEnd; // ptrs to search for correct string
|
|
LPWSTR pString; // ptr to final string
|
|
int cchCount = 0; // count of characters
|
|
|
|
|
|
//
|
|
// Make sure the buffer is ok.
|
|
//
|
|
if ((pBuffer == NULL) || (cchBuffer == 0))
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Make sure we're not hitting the GEO ID that is out of bounds.
|
|
//
|
|
// !!! NOTE !!! This is needed because the East Timor Geo Id
|
|
// is out of bounds and wraps to 0x60e7.
|
|
//
|
|
if (ResourceID == 0x60e7)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Set the UI Language Id.
|
|
//
|
|
if (UILangId == 0)
|
|
{
|
|
UILangId = GetUserDefaultUILanguage();
|
|
}
|
|
|
|
//
|
|
// String Tables are broken up into 16 string segments. Find the
|
|
// resource containing the string we want.
|
|
//
|
|
if ((!(hFindRes = FindResourceExW( hModule,
|
|
RT_STRING,
|
|
(LPWSTR)UlongToPtr((ULONG)(((USHORT)ResourceID >> 4) + 1)),
|
|
(WORD)UILangId ))))
|
|
{
|
|
//
|
|
// Could not find resource. Try NEUTRAL language id.
|
|
//
|
|
if ((!(hFindRes = FindResourceExW( hModule,
|
|
RT_STRING,
|
|
(LPWSTR)UlongToPtr((ULONG)(((USHORT)ResourceID >> 4) + 1)),
|
|
(WORD)0 ))))
|
|
{
|
|
//
|
|
// Could not find resource. Return 0.
|
|
//
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load the resource.
|
|
//
|
|
if (hLoadRes = LoadResource(hModule, hFindRes))
|
|
{
|
|
//
|
|
// Lock the resource. Store the found pointer in the given
|
|
// pointer.
|
|
//
|
|
if (pSearch = (LPWSTR)LockResource(hLoadRes))
|
|
{
|
|
//
|
|
// Move past the other strings in this segment.
|
|
// (16 strings in a segment -> & 0x0F)
|
|
//
|
|
ResourceID &= 0x0F;
|
|
|
|
//
|
|
// Find the correct string in this segment.
|
|
//
|
|
while (TRUE)
|
|
{
|
|
cchCount = *((WORD *)pSearch++);
|
|
if (ResourceID-- == 0)
|
|
{
|
|
break;
|
|
}
|
|
pSearch += cchCount;
|
|
}
|
|
|
|
//
|
|
// Mark the end of the resource string since it is not
|
|
// NULL terminated.
|
|
//
|
|
pSearchEnd = pSearch + cchCount;
|
|
|
|
//
|
|
// Get to the appropriate string.
|
|
//
|
|
while ((WhichString > 0) && (pSearch < pSearchEnd))
|
|
{
|
|
do
|
|
{
|
|
if (*pSearch == RC_STRING_SEPARATOR)
|
|
{
|
|
pSearch++;
|
|
break;
|
|
}
|
|
pSearch++;
|
|
|
|
} while (pSearch < pSearchEnd);
|
|
|
|
WhichString--;
|
|
}
|
|
|
|
//
|
|
// Count the number of characters for this string.
|
|
//
|
|
pString = pSearch;
|
|
cchCount = 0;
|
|
while ((pSearch < pSearchEnd) && (*pSearch != RC_STRING_SEPARATOR))
|
|
{
|
|
pSearch++;
|
|
cchCount++;
|
|
}
|
|
|
|
//
|
|
// See if there is anything to copy.
|
|
//
|
|
if (cchCount > 0)
|
|
{
|
|
//
|
|
// Don't copy more than the max allowed.
|
|
//
|
|
if (cchCount >= cchBuffer)
|
|
{
|
|
cchCount = cchBuffer - 1;
|
|
}
|
|
|
|
//
|
|
// Copy the string into the buffer and NULL terminate it.
|
|
//
|
|
CopyMemory(pBuffer, pString, cchCount * sizeof(WCHAR));
|
|
pBuffer[cchCount] = 0;
|
|
|
|
//
|
|
// Return the number of characters in the string, not
|
|
// including the NULL terminator.
|
|
//
|
|
return (cchCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return failure.
|
|
//
|
|
return (0);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NlsIsDll
|
|
//
|
|
// Check if file extension is DLL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define DLL_SUFFIX_LENGTH 4 // (.XXX)
|
|
|
|
BOOL FASTCALL NlsIsDll(
|
|
LPCWSTR pFileName
|
|
)
|
|
{
|
|
BOOL bIsDll = FALSE;
|
|
|
|
|
|
if (pFileName)
|
|
{
|
|
size_t iLen = 0;
|
|
|
|
if(SUCCEEDED(StringCchLengthW(pFileName, MAX_PATH, &iLen)))
|
|
{
|
|
//
|
|
// Check DLL extension, save the trouble of calling lstricmpW
|
|
//
|
|
// REVIEW: lstricmpW would not be an appropriate function to
|
|
// call here anyway, since user locale collation
|
|
// semantics != file system collation semantics.
|
|
//
|
|
if (iLen > DLL_SUFFIX_LENGTH)
|
|
{
|
|
pFileName += iLen - DLL_SUFFIX_LENGTH;
|
|
|
|
//
|
|
// File names are lower case in setup, so optimize for that
|
|
// by putting them first.
|
|
//
|
|
if ((pFileName[0] == L'.') &&
|
|
(pFileName[1] == L'd' || pFileName[1] == L'D') &&
|
|
(pFileName[2] == L'l' || pFileName[2] == L'L') &&
|
|
(pFileName[3] == L'l' || pFileName[3] == L'L'))
|
|
{
|
|
bIsDll = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bIsDll;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsCodePointDefined
|
|
//
|
|
// Check if the code point is defined.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL FASTCALL IsSortingCodePointDefined(
|
|
LPNLSVERSIONINFO lpVersionInformation,
|
|
LPCWSTR lpString,
|
|
INT cchStr
|
|
)
|
|
{
|
|
PNLSDEFINED pDefinedCodePoints = NULL;
|
|
LPCWSTR pStringEnd;
|
|
|
|
//
|
|
// Make sure the appropriate tables are available. If not,
|
|
// return an error.
|
|
//
|
|
if ((pTblPtrs->pDefinedVersion == NULL) ||
|
|
(pTblPtrs->pSortingTableFileBase == NULL) ||
|
|
(pTblPtrs->pDefaultSortkey == NULL))
|
|
{
|
|
KdPrint(("NLSAPI: Appropriate Tables (Defined, Base and/or Default) Not Loaded.\n"));
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the version.
|
|
//
|
|
if (lpVersionInformation != NULL)
|
|
{
|
|
UINT idx;
|
|
|
|
//
|
|
// Buffer size check.
|
|
//
|
|
if (lpVersionInformation->dwNLSVersionInfoSize != sizeof(NLSVERSIONINFO))
|
|
{
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((lpVersionInformation->dwDefinedVersion == 0L) ||
|
|
(lpVersionInformation->dwDefinedVersion == (pTblPtrs->pDefinedVersion)[0].Version))
|
|
{
|
|
// Use the current version.
|
|
// Do nothing here. We let pDefinedCodePoints to be NULL, so that current table is used.
|
|
}
|
|
else
|
|
{
|
|
if (lpVersionInformation->dwDefinedVersion < pTblPtrs->NumDefinedVersion) {
|
|
//
|
|
// Not the default version, get the the requested version.
|
|
//
|
|
pDefinedCodePoints = (PNLSDEFINED)(pTblPtrs->pSortingTableFileBase + (pTblPtrs->pDefinedVersion)[lpVersionInformation->dwDefinedVersion].dwOffset);
|
|
}
|
|
//
|
|
// Check if the version requested is valid.
|
|
//
|
|
if (pDefinedCodePoints == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
pStringEnd = lpString + cchStr;
|
|
//
|
|
// Check if we deal with the current version.
|
|
//
|
|
if (pDefinedCodePoints == NULL)
|
|
{
|
|
//
|
|
// Use the default table.
|
|
//
|
|
// For each code point verify is they exist
|
|
// in the table.
|
|
//
|
|
while (lpString < pStringEnd)
|
|
{
|
|
//
|
|
// Check is the fist script member is defined for this codepoint.
|
|
//
|
|
if ((pTblPtrs->pDefaultSortkey)[*lpString].UW.SM_AW.Script == UNSORTABLE)
|
|
{
|
|
//
|
|
// Check for the NULL case and formatting characters case. Not
|
|
// defined but valid.
|
|
//
|
|
if ((*lpString == L'\x0000') ||
|
|
(*lpString == L'\x0640') ||
|
|
((*lpString >= L'\x180B') && (*lpString <= L'\x180E')) ||
|
|
((*lpString >= L'\x200C') && (*lpString <= L'\x200F')) ||
|
|
((*lpString >= L'\x202A') && (*lpString <= L'\x202E')) ||
|
|
((*lpString >= L'\x206A') && (*lpString <= L'\x206F')) ||
|
|
(*lpString == L'\xFEFF') ||
|
|
(*lpString == L'\xFFF9') ||
|
|
((*lpString >= L'\xFFFA') && (*lpString <= L'\xFFFD')))
|
|
{
|
|
lpString++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Eliminate Private Use characters. They are defined but cannot be considered
|
|
// valid.
|
|
//
|
|
if ((*lpString >= L'\xE000') && (*lpString <= L'\xF8FF'))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Eliminate invalid surogates pairs or single surrogates.
|
|
//
|
|
if ((*lpString >= L'\xDC00') && (*lpString <= L'\xDFFF')) // Leading low surrogate
|
|
{
|
|
return (FALSE);
|
|
}
|
|
else if ((*lpString >= L'\xD800') && (*lpString <= L'\xDBFF')) // Leading high surrogate
|
|
{
|
|
if ( ((lpString + 1) < pStringEnd) && // Surrogates not the last character
|
|
(*(lpString + 1) >= L'\xDC00') && (*(lpString + 1) <= L'\xDFFF')) // Low surrogate
|
|
{
|
|
lpString++; // Valid surrogates pair, High followed by a low surrogate. Skip the pair!
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
lpString++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WORD wIndex;
|
|
BYTE wMod32Val;
|
|
|
|
while (lpString < pStringEnd)
|
|
{
|
|
//
|
|
// Compute the modulo 32 of the code point value.
|
|
//
|
|
wMod32Val = (BYTE)(*lpString & 0x0000001f); // 0x1fff => 5 bits
|
|
|
|
//
|
|
// Compute the DWORD index that contain the desired code point.
|
|
//
|
|
wIndex = (WORD)(*lpString >> 5);
|
|
|
|
//
|
|
// Get the DWORD aligned entry that contain the desired code point.
|
|
//
|
|
// Note: We need to get a DWORD aligned value to make sure that we
|
|
// that we don't access memory outside the table especially at the
|
|
// end of the table.
|
|
//
|
|
|
|
//
|
|
// Shift the value to retrieve information about code point at the
|
|
// position 0.
|
|
//
|
|
//
|
|
// Check is the code point is defined or not.
|
|
//
|
|
if ((pDefinedCodePoints[wIndex] >> wMod32Val) == 0)
|
|
{
|
|
// NOTENOTE YSLin: In NLSTrans, make sure that we mark U+0000 as 1, instead of 0.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+E000-U+F8FF as 0.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+070F as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+0640 as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+180B-U+180E as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+200C-U+200F as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+202A-U+202E as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+206A-U+206F as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+FEFF as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+FFF9 as 1.
|
|
// NOTENOTE lguindon: In NLSTrans, make sure that we mark U+FFFA-U+FFFD as 1.
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Eliminate invalid surogates pairs or single surrogates.
|
|
//
|
|
if ((*lpString >= L'\xDC00') && (*lpString <= L'\xDFFF')) // Leading low surrogate
|
|
{
|
|
return (FALSE);
|
|
}
|
|
else if ((*lpString >= L'\xD800') && (*lpString <= L'\xDBFF')) // Leading high surrogate
|
|
{
|
|
if ( ((lpString + 1) < pStringEnd) && // Surrogates not the last character
|
|
(*(lpString + 1) >= L'\xDC00') && (*(lpString + 1) <= L'\xDFFF')) // Low surrogate
|
|
{
|
|
lpString++; // Valid surrogates pair, High followed by a low surrogate. Skip the pair!
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
lpString++;
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|