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

  1. /*
  2. * S Z S R C . C P P
  3. *
  4. * Multi-language string support
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davprs.h"
  9. #include <langid.h>
  10. /*
  11. * FLookupWSz()
  12. *
  13. * Purpose:
  14. *
  15. * Looks up a string given a specific language ID.
  16. *
  17. * Parameters:
  18. *
  19. * rid [in] resource ID of string in localized table
  20. * lcid [in] locale ID
  21. * ppwsz [out] pointer to reequested string
  22. */
  23. BOOL
  24. FLookupWSz (UINT rid, LCID lcid, LPWSTR pwsz, UINT cch)
  25. {
  26. safe_lcid sl(lcid);
  27. // Try the requested language identifier
  28. //
  29. if (!LoadStringW (g_inst.Hinst(), rid, pwsz, cch))
  30. {
  31. DebugTrace ("Dav: Failed to find requested language string\n");
  32. // If that wasn't there, then try try stripping the sub-lang
  33. //
  34. SetThreadLocale (MAKELCID (MAKELANGID (PRIMARYLANGID (lcid),
  35. SUBLANG_DEFAULT),
  36. SORT_DEFAULT));
  37. if (!LoadStringW (g_inst.Hinst(), rid, pwsz, cch))
  38. {
  39. DebugTrace ("Dav: Failed to find next best language string\n");
  40. return FALSE;
  41. }
  42. }
  43. return TRUE;
  44. }
  45. /*
  46. * FLoadLangSpecificString()
  47. *
  48. * Purpose:
  49. *
  50. * Loads a language specific string. If lang is unavailable, try
  51. * LANG_ENGLISH.
  52. *
  53. * Parameters:
  54. *
  55. * rid [in] string resource ID
  56. * lcid [in] locale ID
  57. * rgchBuf [out] loaded string
  58. * cbBuf [in] size of rgchBuf
  59. */
  60. BOOL
  61. FLoadLangSpecificString (UINT rid, LCID lcid, LPSTR psz, UINT cch)
  62. {
  63. CStackBuffer<WCHAR,128> pwsz(cch * sizeof(WCHAR));
  64. // Try the requested language
  65. //
  66. if (!FLookupWSz (rid, lcid, pwsz.get(), cch))
  67. {
  68. // If we couldn't find the requested language, then
  69. // try for the machine default.
  70. //
  71. lcid = MAKELCID (GetSystemDefaultLangID(), SORT_DEFAULT);
  72. if (!FLookupWSz (rid, lcid, pwsz.get(), cch))
  73. {
  74. // And lastly try for english, cause we know that one
  75. // is there.
  76. //
  77. lcid = MAKELCID (MAKELANGID (LANG_ENGLISH,
  78. SUBLANG_ENGLISH_US),
  79. SORT_DEFAULT);
  80. if (!FLookupWSz (rid, lcid, pwsz.get(), cch))
  81. return FALSE;
  82. }
  83. }
  84. return WideCharToMultiByte (CP_UTF8,
  85. 0,
  86. pwsz.get(),
  87. -1,
  88. psz,
  89. cch,
  90. NULL,
  91. NULL);
  92. }
  93. BOOL
  94. FLoadLangSpecificStringW (UINT rid, LCID lcid, LPWSTR pwsz, UINT cch)
  95. {
  96. // Try the requested language
  97. //
  98. if (!FLookupWSz (rid, lcid, pwsz, cch))
  99. {
  100. // If we couldn't find the requested language, then
  101. // go for US English
  102. //
  103. lcid = MAKELCID (MAKELANGID (LANG_ENGLISH,
  104. SUBLANG_ENGLISH_US),
  105. SORT_DEFAULT);
  106. if (!FLookupWSz (rid, lcid, pwsz, cch))
  107. return FALSE;
  108. }
  109. return TRUE;
  110. }
  111. // ========================================================================
  112. //
  113. // CLASS CIDPair
  114. //
  115. // Key class used with the caches in CResourceStringCache below. Each key
  116. // is just a pair of IDs: the resource ID and the LCID.
  117. //
  118. class CIDPair
  119. {
  120. public:
  121. UINT m_uiResourceID;
  122. LCID m_lcid;
  123. CIDPair(UINT uiResourceID,
  124. LCID lcid) :
  125. m_uiResourceID(uiResourceID),
  126. m_lcid(lcid)
  127. {
  128. }
  129. // operators for use with the hash cache
  130. //
  131. int hash( const int rhs ) const
  132. {
  133. //
  134. // Ignore the LCID and hash only on resource ID.
  135. // Typically the server will only deal with one language.
  136. //
  137. return m_uiResourceID % rhs;
  138. }
  139. bool isequal( const CIDPair& rhs ) const
  140. {
  141. return (m_lcid == rhs.m_lcid) &&
  142. (m_uiResourceID == rhs.m_uiResourceID);
  143. }
  144. };
  145. // ========================================================================
  146. //
  147. // CLASS CResourceStringCache
  148. //
  149. // Cache of resource strings to minimize expensive LoadString() calls.
  150. //
  151. class CResourceStringCache : private Singleton<CResourceStringCache>
  152. {
  153. //
  154. // Friend declarations required by Singleton template
  155. //
  156. friend class Singleton<CResourceStringCache>;
  157. //
  158. // Caches for ANSI and Unicode strings, keyed by LCID/ResourceID pair.
  159. // These must be multithread-safe caches as accesses and additions
  160. // can occur simultaneously from multiple threads.
  161. //
  162. CMTCache<CIDPair, LPSTR> m_cacheA;
  163. CMTCache<CIDPair, LPWSTR> m_cacheW;
  164. //
  165. // Buffers in which the strings in the caches are stored.
  166. //
  167. ChainedStringBuffer<CHAR> m_sbA;
  168. ChainedStringBuffer<WCHAR> m_sbW;
  169. // NOT IMPLEMENTED
  170. //
  171. CResourceStringCache& operator=(const CResourceStringCache&);
  172. CResourceStringCache(const CResourceStringCache&);
  173. public:
  174. // STATICS
  175. //
  176. using Singleton<CResourceStringCache>::CreateInstance;
  177. using Singleton<CResourceStringCache>::DestroyInstance;
  178. using Singleton<CResourceStringCache>::Instance;
  179. // CREATORS
  180. //
  181. CResourceStringCache() {}
  182. BOOL FInitialize() { return TRUE; } //$NYI Planning for when CMTCache initialization can fail...
  183. // MANIPULATORS
  184. //
  185. BOOL FFetchStringA( UINT uiResourceID,
  186. LCID lcid,
  187. LPSTR lpszBuf,
  188. INT cchBuf );
  189. BOOL FFetchStringW( UINT uiResourceID,
  190. LCID lcid,
  191. LPWSTR lpwszBuf,
  192. INT cchBuf );
  193. };
  194. /*
  195. * CResourceStringCache::FFetchStringA()
  196. *
  197. * Purpose:
  198. *
  199. * Fetches an ANSI string from the cache, faulting the string into
  200. * the cache on first access.
  201. */
  202. BOOL
  203. CResourceStringCache::FFetchStringA(
  204. UINT uiResourceID,
  205. LCID lcid,
  206. LPSTR lpszBuf,
  207. INT cchBuf )
  208. {
  209. CIDPair ids( uiResourceID, lcid );
  210. LPSTR lpszCached = NULL;
  211. Assert( lpszBuf );
  212. //
  213. // Lookup the string in the cache. If it isn't there, then fault it in.
  214. //
  215. while ( !m_cacheA.FFetch( ids, &lpszCached ) )
  216. {
  217. //
  218. // Use an init gate. If there are multiple threads all trying
  219. // to fault in the same string, this will block all but the first
  220. // one through until we're done. Use the full LCID/Resource ID
  221. // pair when naming the init gate to minimize possible contention
  222. // on the gate to just those threads that are trying to fault
  223. // in this particular string.
  224. //
  225. WCHAR rgwchIDs[(sizeof(LCID) + sizeof(UINT)) * 2 + 1];
  226. swprintf( rgwchIDs, L"%x%lx", lcid, uiResourceID );
  227. CInitGate ig( L"DAV/CResourceStringCache::FFetchStringA/", rgwchIDs );
  228. if ( ig.FInit() )
  229. {
  230. //
  231. // We be the thread to fault in the string. Load up the string
  232. // and cache it. Since we load the string into the caller-supplied
  233. // buffer directly, we can return as soon as we're done adding
  234. // to the cache. No need for another lookup.
  235. //
  236. if ( FLoadLangSpecificString( uiResourceID, lcid, lpszBuf, cchBuf ) )
  237. {
  238. m_cacheA.Add( ids, m_sbA.AppendWithNull(lpszBuf) );
  239. return TRUE;
  240. }
  241. else
  242. {
  243. return FALSE;
  244. }
  245. }
  246. }
  247. Assert( lpszCached );
  248. //
  249. // Copy up to cchBuf characters from the cached string into
  250. // the provided buffer. If the cached string is longer than
  251. // the buffer, then the copied string is truncated
  252. //
  253. strncpy( lpszBuf, lpszCached, cchBuf );
  254. //
  255. // Make sure the copied string is null-terminated, which
  256. // it may not have been if it was truncated above.
  257. //
  258. lpszBuf[cchBuf-1] = '\0';
  259. return TRUE;
  260. }
  261. /*
  262. * CResourceStringCache::FFetchStringW()
  263. *
  264. * Purpose:
  265. *
  266. *
  267. * Fetches a UNICODE string from the cache, faulting the string into
  268. * the cache on first access.
  269. */
  270. BOOL
  271. CResourceStringCache::FFetchStringW(
  272. UINT uiResourceID,
  273. LCID lcid,
  274. LPWSTR lpwszBuf,
  275. INT cchBuf )
  276. {
  277. CIDPair ids( uiResourceID, lcid );
  278. LPWSTR lpwszCached = NULL;
  279. Assert( lpwszBuf );
  280. //
  281. // Lookup the string in the cache. If it isn't there, then fault it in.
  282. //
  283. while ( !m_cacheW.FFetch( ids, &lpwszCached ) )
  284. {
  285. //
  286. // Use an init gate. If there are multiple threads all trying
  287. // to fault in the same string, this will block all but the first
  288. // one through until we're done. Use the full LCID/Resource ID
  289. // pair when naming the init gate to minimize possible contention
  290. // on the gate to just those threads that are trying to fault
  291. // in this particular string.
  292. //
  293. WCHAR rgwchIDs[(sizeof(LCID) + sizeof(UINT)) * 2 + 1];
  294. swprintf( rgwchIDs, L"%x%lx", lcid, uiResourceID );
  295. CInitGate ig( L"DAV/CResourceStringCache::FFetchStringW/", rgwchIDs );
  296. if ( ig.FInit() )
  297. {
  298. //
  299. // We be the thread to fault in the string. Load up the string
  300. // and cache it. Since we load the string into the caller-supplied
  301. // buffer directly, we can return as soon as we're done adding
  302. // to the cache. No need for another lookup.
  303. //
  304. if ( FLoadLangSpecificStringW( uiResourceID, lcid, lpwszBuf, cchBuf ) )
  305. {
  306. m_cacheW.Add( ids, m_sbW.AppendWithNull(lpwszBuf) );
  307. return TRUE;
  308. }
  309. else
  310. {
  311. return FALSE;
  312. }
  313. }
  314. }
  315. Assert( lpwszCached );
  316. //
  317. // Copy up to cchBuf characters from the cached string into
  318. // the provided buffer. If the cached string is longer than
  319. // the buffer, then the copied string is truncated
  320. //
  321. wcsncpy( lpwszBuf, lpwszCached, cchBuf );
  322. //
  323. // Make sure the copied string is null-terminated, which
  324. // it may not have been if it was truncated above.
  325. //
  326. lpwszBuf[cchBuf-1] = L'\0';
  327. return TRUE;
  328. }
  329. /*
  330. * FInitResourceStringCache()
  331. *
  332. * Purpose:
  333. *
  334. * Initializes the resource string pool.
  335. */
  336. BOOL
  337. FInitResourceStringCache()
  338. {
  339. return CResourceStringCache::CreateInstance().FInitialize();
  340. }
  341. /*
  342. * DeinitResourceStringCache()
  343. *
  344. * Purpose:
  345. *
  346. * Deinitializes the resource string pool.
  347. */
  348. VOID
  349. DeinitResourceStringCache()
  350. {
  351. CResourceStringCache::DestroyInstance();
  352. }
  353. /*
  354. * LpszLoadString()
  355. *
  356. * Purpose:
  357. *
  358. * Loads a string based on localization.
  359. */
  360. LPSTR
  361. LpszLoadString (UINT uiResourceID,
  362. ULONG lcid,
  363. LPSTR lpszBuf,
  364. INT cchBuf)
  365. {
  366. if (!CResourceStringCache::Instance().FFetchStringA(uiResourceID, lcid, lpszBuf, cchBuf))
  367. {
  368. DebugTrace ("LpszLoadString() - Could not load string for resource ID %u (%d)\n",
  369. uiResourceID,
  370. GetLastError());
  371. throw CDAVException();
  372. }
  373. return lpszBuf;
  374. }
  375. /*
  376. * LpwszLoadString()
  377. *
  378. * Purpose:
  379. *
  380. * Loads a string based on localization.
  381. */
  382. LPWSTR
  383. LpwszLoadString (UINT uiResourceID,
  384. ULONG lcid,
  385. LPWSTR lpwszBuf,
  386. INT cchBuf)
  387. {
  388. if (!CResourceStringCache::Instance().FFetchStringW(uiResourceID, lcid, lpwszBuf, cchBuf))
  389. {
  390. DebugTrace ("LpszLoadString() - Could not load string for resource ID %u (%d)\n",
  391. uiResourceID,
  392. GetLastError());
  393. throw CDAVException();
  394. }
  395. return lpwszBuf;
  396. }
  397. /*
  398. * LInstFromVroot()
  399. *
  400. * Purpose:
  401. *
  402. * Compute the server ID from the vroot (format of the vroot
  403. * is "/lm/w3svc/<ID>/root/vroot/..."). The computation should
  404. * be robust -- if for whatever reason the server ID can't
  405. * be determined from the vroot, leave it with a value of 0.
  406. *
  407. */
  408. LONG LInstFromVroot( LPCWSTR pwszServerId )
  409. {
  410. LONG lServerId = 0;
  411. CStackBuffer<WCHAR> pwszInstance;
  412. Assert(pwszServerId);
  413. // Make sure the vroot begins with "/lm/w3svc"
  414. //
  415. if ( wcsstr( pwszServerId, gc_wsz_Lm_W3Svc) == pwszServerId )
  416. {
  417. //
  418. // If it does, then skip past that part and try to
  419. // locate the '/' which should follow it. If there
  420. // isn't one, that's fine; we'll just be unable to
  421. // convert whatever is there to a number and we'll
  422. // end up with a server id of 0, which as we said
  423. // above is just fine.
  424. //
  425. pwszServerId += gc_cch_Lm_W3Svc;
  426. if (L'/' == *pwszServerId)
  427. {
  428. pwszServerId++;
  429. }
  430. //
  431. // At this point, pwszServerId should look like
  432. // "1/root/vroot/..." Locate the first '/' (should
  433. // be immediately following the number) and null
  434. // it out. Again, if for some oddball reason
  435. // we don't find a '/', then we'll just try to
  436. // convert whatever is there and end up with
  437. // a server ID of 0.
  438. //
  439. WCHAR * pwch = wcschr( pwszServerId, L'/' );
  440. if ( pwch )
  441. {
  442. // Reallocate the string with server ID on the stack
  443. // so that we do not mess up the one passed in
  444. //
  445. UINT cchInstance = static_cast<UINT>(pwch - pwszServerId);
  446. pwszInstance.resize(CbSizeWsz(cchInstance));
  447. // Copy the string and terminate it
  448. //
  449. memcpy(pwszInstance.get(), pwszServerId, cchInstance * sizeof(WCHAR));
  450. pwszInstance[cchInstance] = L'\0';
  451. // Swap the pointer
  452. //
  453. pwszServerId = pwszInstance.get();
  454. }
  455. //
  456. // If we nulled out the '/', our pwszServerId
  457. // should now just be a number formatted as
  458. // decimal integer string. Attempt to convert
  459. // it to its corresponding binary value to get
  460. // the ServerId. Conveniently, atoi returns 0
  461. // if it can't convert the string, which is
  462. // exactly what we would want.
  463. //
  464. lServerId = _wtoi(pwszServerId);
  465. }
  466. return lServerId;
  467. }
  468. /*
  469. * LpszAutoDupSz()
  470. *
  471. * Purpose:
  472. *
  473. * Duplicates a string
  474. */
  475. LPSTR
  476. LpszAutoDupSz (LPCSTR psz)
  477. {
  478. Assert(psz);
  479. LPSTR pszDup;
  480. UINT cb = static_cast<UINT>((strlen(psz) + 1) * sizeof(CHAR));
  481. pszDup = static_cast<LPSTR>(g_heap.Alloc (cb));
  482. if (pszDup)
  483. CopyMemory (pszDup, psz, cb);
  484. return pszDup;
  485. }
  486. LPWSTR WszDupWsz (LPCWSTR psz)
  487. {
  488. Assert(psz);
  489. LPWSTR pszDup;
  490. UINT cb = static_cast<UINT>((wcslen(psz) + 1) * sizeof(WCHAR));
  491. pszDup = static_cast<LPWSTR>(g_heap.Alloc (cb));
  492. if (pszDup)
  493. CopyMemory (pszDup, psz, cb);
  494. return pszDup;
  495. }
  496. // Language to LANGID mapping ------------------------------------------------
  497. //
  498. /*
  499. * FLookupLCID()
  500. *
  501. * Purpose:
  502. *
  503. * Looks up a locale identifier given a particular language.
  504. *
  505. * Parameters:
  506. *
  507. * psz [in] pointer to the language name
  508. * plcid [out] locale identifier
  509. *
  510. * Returns:
  511. *
  512. * TRUE if a locale identifier for the language is found. Its
  513. * value is returned in plcid.
  514. */
  515. BOOL
  516. FLookupLCID (LPCSTR psz, ULONG * plcid)
  517. {
  518. // Find it in the cache
  519. //
  520. *plcid = CLangIDCache::LcidFind (psz);
  521. return (0 != *plcid);
  522. }