/* * S Z S R C . C P P * * Multi-language string support * * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved */ #include "_davprs.h" #include /* * 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 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 { // // Friend declarations required by Singleton template // friend class Singleton; // // 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 m_cacheA; CMTCache m_cacheW; // // Buffers in which the strings in the caches are stored. // ChainedStringBuffer m_sbA; ChainedStringBuffer m_sbW; // NOT IMPLEMENTED // CResourceStringCache& operator=(const CResourceStringCache&); CResourceStringCache(const CResourceStringCache&); public: // STATICS // using Singleton::CreateInstance; using Singleton::DestroyInstance; using Singleton::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//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 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(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((strlen(psz) + 1) * sizeof(CHAR)); pszDup = static_cast(g_heap.Alloc (cb)); if (pszDup) CopyMemory (pszDup, psz, cb); return pszDup; } LPWSTR WszDupWsz (LPCWSTR psz) { Assert(psz); LPWSTR pszDup; UINT cb = static_cast((wcslen(psz) + 1) * sizeof(WCHAR)); pszDup = static_cast(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); }