Leaked source code of windows server 2003
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.
 
 
 
 
 
 

588 lines
14 KiB

/*
* S Z S R C . C P P
*
* Multi-language string support
*
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
*/
#include "_davprs.h"
#include <langid.h>
/*
* FLookupWSz()
*
* Purpose:
*
* Looks up a string given a specific language ID.
*
* Parameters:
*
* rid [in] resource ID of string in localized table
* lcid [in] locale ID
* ppwsz [out] pointer to reequested string
*/
BOOL
FLookupWSz (UINT rid, LCID lcid, LPWSTR pwsz, UINT cch)
{
safe_lcid sl(lcid);
// Try the requested language identifier
//
if (!LoadStringW (g_inst.Hinst(), rid, pwsz, cch))
{
DebugTrace ("Dav: Failed to find requested language string\n");
// If that wasn't there, then try try stripping the sub-lang
//
SetThreadLocale (MAKELCID (MAKELANGID (PRIMARYLANGID (lcid),
SUBLANG_DEFAULT),
SORT_DEFAULT));
if (!LoadStringW (g_inst.Hinst(), rid, pwsz, cch))
{
DebugTrace ("Dav: Failed to find next best language string\n");
return FALSE;
}
}
return TRUE;
}
/*
* FLoadLangSpecificString()
*
* Purpose:
*
* Loads a language specific string. If lang is unavailable, try
* LANG_ENGLISH.
*
* Parameters:
*
* rid [in] string resource ID
* lcid [in] locale ID
* rgchBuf [out] loaded string
* cbBuf [in] size of rgchBuf
*/
BOOL
FLoadLangSpecificString (UINT rid, LCID lcid, LPSTR psz, UINT cch)
{
CStackBuffer<WCHAR,128> pwsz(cch * sizeof(WCHAR));
// Try the requested language
//
if (!FLookupWSz (rid, lcid, pwsz.get(), cch))
{
// If we couldn't find the requested language, then
// try for the machine default.
//
lcid = MAKELCID (GetSystemDefaultLangID(), SORT_DEFAULT);
if (!FLookupWSz (rid, lcid, pwsz.get(), cch))
{
// And lastly try for english, cause we know that one
// is there.
//
lcid = MAKELCID (MAKELANGID (LANG_ENGLISH,
SUBLANG_ENGLISH_US),
SORT_DEFAULT);
if (!FLookupWSz (rid, lcid, pwsz.get(), cch))
return FALSE;
}
}
return WideCharToMultiByte (CP_UTF8,
0,
pwsz.get(),
-1,
psz,
cch,
NULL,
NULL);
}
BOOL
FLoadLangSpecificStringW (UINT rid, LCID lcid, LPWSTR pwsz, UINT cch)
{
// Try the requested language
//
if (!FLookupWSz (rid, lcid, pwsz, cch))
{
// If we couldn't find the requested language, then
// go for US English
//
lcid = MAKELCID (MAKELANGID (LANG_ENGLISH,
SUBLANG_ENGLISH_US),
SORT_DEFAULT);
if (!FLookupWSz (rid, lcid, pwsz, cch))
return FALSE;
}
return TRUE;
}
// ========================================================================
//
// CLASS CIDPair
//
// Key class used with the caches in CResourceStringCache below. Each key
// is just a pair of IDs: the resource ID and the LCID.
//
class CIDPair
{
public:
UINT m_uiResourceID;
LCID m_lcid;
CIDPair(UINT uiResourceID,
LCID lcid) :
m_uiResourceID(uiResourceID),
m_lcid(lcid)
{
}
// operators for use with the hash cache
//
int hash( const int rhs ) const
{
//
// Ignore the LCID and hash only on resource ID.
// Typically the server will only deal with one language.
//
return m_uiResourceID % rhs;
}
bool isequal( const CIDPair& rhs ) const
{
return (m_lcid == rhs.m_lcid) &&
(m_uiResourceID == rhs.m_uiResourceID);
}
};
// ========================================================================
//
// CLASS CResourceStringCache
//
// Cache of resource strings to minimize expensive LoadString() calls.
//
class CResourceStringCache : private Singleton<CResourceStringCache>
{
//
// Friend declarations required by Singleton template
//
friend class Singleton<CResourceStringCache>;
//
// Caches for ANSI and Unicode strings, keyed by LCID/ResourceID pair.
// These must be multithread-safe caches as accesses and additions
// can occur simultaneously from multiple threads.
//
CMTCache<CIDPair, LPSTR> m_cacheA;
CMTCache<CIDPair, LPWSTR> m_cacheW;
//
// Buffers in which the strings in the caches are stored.
//
ChainedStringBuffer<CHAR> m_sbA;
ChainedStringBuffer<WCHAR> m_sbW;
// NOT IMPLEMENTED
//
CResourceStringCache& operator=(const CResourceStringCache&);
CResourceStringCache(const CResourceStringCache&);
public:
// STATICS
//
using Singleton<CResourceStringCache>::CreateInstance;
using Singleton<CResourceStringCache>::DestroyInstance;
using Singleton<CResourceStringCache>::Instance;
// CREATORS
//
CResourceStringCache() {}
BOOL FInitialize() { return TRUE; } //$NYI Planning for when CMTCache initialization can fail...
// MANIPULATORS
//
BOOL FFetchStringA( UINT uiResourceID,
LCID lcid,
LPSTR lpszBuf,
INT cchBuf );
BOOL FFetchStringW( UINT uiResourceID,
LCID lcid,
LPWSTR lpwszBuf,
INT cchBuf );
};
/*
* CResourceStringCache::FFetchStringA()
*
* Purpose:
*
* Fetches an ANSI string from the cache, faulting the string into
* the cache on first access.
*/
BOOL
CResourceStringCache::FFetchStringA(
UINT uiResourceID,
LCID lcid,
LPSTR lpszBuf,
INT cchBuf )
{
CIDPair ids( uiResourceID, lcid );
LPSTR lpszCached = NULL;
Assert( lpszBuf );
//
// Lookup the string in the cache. If it isn't there, then fault it in.
//
while ( !m_cacheA.FFetch( ids, &lpszCached ) )
{
//
// Use an init gate. If there are multiple threads all trying
// to fault in the same string, this will block all but the first
// one through until we're done. Use the full LCID/Resource ID
// pair when naming the init gate to minimize possible contention
// on the gate to just those threads that are trying to fault
// in this particular string.
//
WCHAR rgwchIDs[(sizeof(LCID) + sizeof(UINT)) * 2 + 1];
swprintf( rgwchIDs, L"%x%lx", lcid, uiResourceID );
CInitGate ig( L"DAV/CResourceStringCache::FFetchStringA/", rgwchIDs );
if ( ig.FInit() )
{
//
// We be the thread to fault in the string. Load up the string
// and cache it. Since we load the string into the caller-supplied
// buffer directly, we can return as soon as we're done adding
// to the cache. No need for another lookup.
//
if ( FLoadLangSpecificString( uiResourceID, lcid, lpszBuf, cchBuf ) )
{
m_cacheA.Add( ids, m_sbA.AppendWithNull(lpszBuf) );
return TRUE;
}
else
{
return FALSE;
}
}
}
Assert( lpszCached );
//
// Copy up to cchBuf characters from the cached string into
// the provided buffer. If the cached string is longer than
// the buffer, then the copied string is truncated
//
strncpy( lpszBuf, lpszCached, cchBuf );
//
// Make sure the copied string is null-terminated, which
// it may not have been if it was truncated above.
//
lpszBuf[cchBuf-1] = '\0';
return TRUE;
}
/*
* CResourceStringCache::FFetchStringW()
*
* Purpose:
*
*
* Fetches a UNICODE string from the cache, faulting the string into
* the cache on first access.
*/
BOOL
CResourceStringCache::FFetchStringW(
UINT uiResourceID,
LCID lcid,
LPWSTR lpwszBuf,
INT cchBuf )
{
CIDPair ids( uiResourceID, lcid );
LPWSTR lpwszCached = NULL;
Assert( lpwszBuf );
//
// Lookup the string in the cache. If it isn't there, then fault it in.
//
while ( !m_cacheW.FFetch( ids, &lpwszCached ) )
{
//
// Use an init gate. If there are multiple threads all trying
// to fault in the same string, this will block all but the first
// one through until we're done. Use the full LCID/Resource ID
// pair when naming the init gate to minimize possible contention
// on the gate to just those threads that are trying to fault
// in this particular string.
//
WCHAR rgwchIDs[(sizeof(LCID) + sizeof(UINT)) * 2 + 1];
swprintf( rgwchIDs, L"%x%lx", lcid, uiResourceID );
CInitGate ig( L"DAV/CResourceStringCache::FFetchStringW/", rgwchIDs );
if ( ig.FInit() )
{
//
// We be the thread to fault in the string. Load up the string
// and cache it. Since we load the string into the caller-supplied
// buffer directly, we can return as soon as we're done adding
// to the cache. No need for another lookup.
//
if ( FLoadLangSpecificStringW( uiResourceID, lcid, lpwszBuf, cchBuf ) )
{
m_cacheW.Add( ids, m_sbW.AppendWithNull(lpwszBuf) );
return TRUE;
}
else
{
return FALSE;
}
}
}
Assert( lpwszCached );
//
// Copy up to cchBuf characters from the cached string into
// the provided buffer. If the cached string is longer than
// the buffer, then the copied string is truncated
//
wcsncpy( lpwszBuf, lpwszCached, cchBuf );
//
// Make sure the copied string is null-terminated, which
// it may not have been if it was truncated above.
//
lpwszBuf[cchBuf-1] = L'\0';
return TRUE;
}
/*
* FInitResourceStringCache()
*
* Purpose:
*
* Initializes the resource string pool.
*/
BOOL
FInitResourceStringCache()
{
return CResourceStringCache::CreateInstance().FInitialize();
}
/*
* DeinitResourceStringCache()
*
* Purpose:
*
* Deinitializes the resource string pool.
*/
VOID
DeinitResourceStringCache()
{
CResourceStringCache::DestroyInstance();
}
/*
* LpszLoadString()
*
* Purpose:
*
* Loads a string based on localization.
*/
LPSTR
LpszLoadString (UINT uiResourceID,
ULONG lcid,
LPSTR lpszBuf,
INT cchBuf)
{
if (!CResourceStringCache::Instance().FFetchStringA(uiResourceID, lcid, lpszBuf, cchBuf))
{
DebugTrace ("LpszLoadString() - Could not load string for resource ID %u (%d)\n",
uiResourceID,
GetLastError());
throw CDAVException();
}
return lpszBuf;
}
/*
* LpwszLoadString()
*
* Purpose:
*
* Loads a string based on localization.
*/
LPWSTR
LpwszLoadString (UINT uiResourceID,
ULONG lcid,
LPWSTR lpwszBuf,
INT cchBuf)
{
if (!CResourceStringCache::Instance().FFetchStringW(uiResourceID, lcid, lpwszBuf, cchBuf))
{
DebugTrace ("LpszLoadString() - Could not load string for resource ID %u (%d)\n",
uiResourceID,
GetLastError());
throw CDAVException();
}
return lpwszBuf;
}
/*
* LInstFromVroot()
*
* Purpose:
*
* Compute the server ID from the vroot (format of the vroot
* is "/lm/w3svc/<ID>/root/vroot/..."). The computation should
* be robust -- if for whatever reason the server ID can't
* be determined from the vroot, leave it with a value of 0.
*
*/
LONG LInstFromVroot( LPCWSTR pwszServerId )
{
LONG lServerId = 0;
CStackBuffer<WCHAR> pwszInstance;
Assert(pwszServerId);
// Make sure the vroot begins with "/lm/w3svc"
//
if ( wcsstr( pwszServerId, gc_wsz_Lm_W3Svc) == pwszServerId )
{
//
// If it does, then skip past that part and try to
// locate the '/' which should follow it. If there
// isn't one, that's fine; we'll just be unable to
// convert whatever is there to a number and we'll
// end up with a server id of 0, which as we said
// above is just fine.
//
pwszServerId += gc_cch_Lm_W3Svc;
if (L'/' == *pwszServerId)
{
pwszServerId++;
}
//
// At this point, pwszServerId should look like
// "1/root/vroot/..." Locate the first '/' (should
// be immediately following the number) and null
// it out. Again, if for some oddball reason
// we don't find a '/', then we'll just try to
// convert whatever is there and end up with
// a server ID of 0.
//
WCHAR * pwch = wcschr( pwszServerId, L'/' );
if ( pwch )
{
// Reallocate the string with server ID on the stack
// so that we do not mess up the one passed in
//
UINT cchInstance = static_cast<UINT>(pwch - pwszServerId);
pwszInstance.resize(CbSizeWsz(cchInstance));
// Copy the string and terminate it
//
memcpy(pwszInstance.get(), pwszServerId, cchInstance * sizeof(WCHAR));
pwszInstance[cchInstance] = L'\0';
// Swap the pointer
//
pwszServerId = pwszInstance.get();
}
//
// If we nulled out the '/', our pwszServerId
// should now just be a number formatted as
// decimal integer string. Attempt to convert
// it to its corresponding binary value to get
// the ServerId. Conveniently, atoi returns 0
// if it can't convert the string, which is
// exactly what we would want.
//
lServerId = _wtoi(pwszServerId);
}
return lServerId;
}
/*
* LpszAutoDupSz()
*
* Purpose:
*
* Duplicates a string
*/
LPSTR
LpszAutoDupSz (LPCSTR psz)
{
Assert(psz);
LPSTR pszDup;
UINT cb = static_cast<UINT>((strlen(psz) + 1) * sizeof(CHAR));
pszDup = static_cast<LPSTR>(g_heap.Alloc (cb));
if (pszDup)
CopyMemory (pszDup, psz, cb);
return pszDup;
}
LPWSTR WszDupWsz (LPCWSTR psz)
{
Assert(psz);
LPWSTR pszDup;
UINT cb = static_cast<UINT>((wcslen(psz) + 1) * sizeof(WCHAR));
pszDup = static_cast<LPWSTR>(g_heap.Alloc (cb));
if (pszDup)
CopyMemory (pszDup, psz, cb);
return pszDup;
}
// Language to LANGID mapping ------------------------------------------------
//
/*
* FLookupLCID()
*
* Purpose:
*
* Looks up a locale identifier given a particular language.
*
* Parameters:
*
* psz [in] pointer to the language name
* plcid [out] locale identifier
*
* Returns:
*
* TRUE if a locale identifier for the language is found. Its
* value is returned in plcid.
*/
BOOL
FLookupLCID (LPCSTR psz, ULONG * plcid)
{
// Find it in the cache
//
*plcid = CLangIDCache::LcidFind (psz);
return (0 != *plcid);
}