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.

2119 lines
61 KiB

  1. /*++
  2. Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
  3. Module Name:
  4. util.c
  5. Abstract:
  6. This file contains utility functions that are shared across NLS's code
  7. modules, but are not necessarily part of any of the existing code
  8. modules.
  9. Private APIs found in this file:
  10. NlsGetCacheUpdateCount
  11. External Routines found in this file:
  12. IsValidSeparatorString
  13. IsValidGroupingString
  14. IsValidCalendarType
  15. IsValidCalendarTypeStr
  16. GetUserInfo
  17. GetPreComposedChar
  18. GetCompositeChars
  19. InsertPreComposedForm
  20. InsertFullWidthPreComposedForm
  21. InsertCompositeForm
  22. NlsConvertIntegerToString
  23. NlsConvertIntegerToHexStringW
  24. NlsConvertStringToIntegerW
  25. NlsStrLenW
  26. NlsStrEqualW
  27. NlsStrNEqualW
  28. GetStringTableEntry
  29. NlsIsDll
  30. Revision History:
  31. 05-31-91 JulieB Created.
  32. --*/
  33. //
  34. // Include Files.
  35. //
  36. #include "nls.h"
  37. #include "nlssafe.h"
  38. //-------------------------------------------------------------------------//
  39. // PRIVATE API ROUTINES //
  40. //-------------------------------------------------------------------------//
  41. ////////////////////////////////////////////////////////////////////////////
  42. //
  43. // NlsGetCacheUpdateCount
  44. //
  45. // Returns the current cache update count. The cache update count is
  46. // updated whenever the HKCU\Control Panel\International settings are
  47. // modified. This count allows the caller to see if the cache has been
  48. // updated since the last time this function was called.
  49. //
  50. // This private api is needed by the Complex Script Language Pack
  51. // (CSLPK) to enable it to quickly see if the international section of
  52. // the registry has been modified.
  53. //
  54. ////////////////////////////////////////////////////////////////////////////
  55. ULONG WINAPI NlsGetCacheUpdateCount(void)
  56. {
  57. return (pNlsUserInfo->ulCacheUpdateCount);
  58. }
  59. //-------------------------------------------------------------------------//
  60. // EXTERNAL ROUTINES //
  61. //-------------------------------------------------------------------------//
  62. ////////////////////////////////////////////////////////////////////////////
  63. //
  64. // IsValidSeparatorString
  65. //
  66. // Returns TRUE if the given string is valid. Otherwise, it returns FALSE.
  67. //
  68. // A valid string is one that does NOT contain any code points between
  69. // L'0' and L'9', and does NOT have a length greater than the maximum.
  70. //
  71. // NOTE: The string must be a null terminated string.
  72. //
  73. // 10-12-93 JulieB Created.
  74. ////////////////////////////////////////////////////////////////////////////
  75. BOOL IsValidSeparatorString(
  76. LPCWSTR pString,
  77. ULONG MaxLength,
  78. BOOL fCheckZeroLen)
  79. {
  80. ULONG Length; // string length
  81. LPWSTR pCur; // ptr to current position in string
  82. //
  83. // Search down the string to see if the chars are valid.
  84. // Save the length of the string.
  85. //
  86. pCur = (LPWSTR)pString;
  87. while (*pCur)
  88. {
  89. if ((*pCur >= NLS_CHAR_ZERO) && (*pCur <= NLS_CHAR_NINE))
  90. {
  91. //
  92. // String is NOT valid.
  93. //
  94. return (FALSE);
  95. }
  96. pCur++;
  97. }
  98. Length = (ULONG)(pCur - (LPWSTR)pString);
  99. //
  100. // Make sure the length is not greater than the maximum allowed.
  101. // Also, check for 0 length string (if appropriate).
  102. //
  103. if ((Length >= MaxLength) ||
  104. ((fCheckZeroLen) && (Length == 0)))
  105. {
  106. //
  107. // String is NOT valid.
  108. //
  109. return (FALSE);
  110. }
  111. //
  112. // String is valid.
  113. //
  114. return (TRUE);
  115. }
  116. ////////////////////////////////////////////////////////////////////////////
  117. //
  118. // IsValidGroupingString
  119. //
  120. // Returns TRUE if the given string is valid. Otherwise, it returns FALSE.
  121. //
  122. // A valid string is one that begins and ends with a number between
  123. // L'0' and L'9', alternates between a number and a semicolon, and does
  124. // NOT have a length greater than the maximum.
  125. // (eg. 3;2;0 or 3;0 or 0 or 3)
  126. //
  127. // NOTE: The string must be a null terminated string.
  128. //
  129. // 01-05-98 JulieB Created.
  130. ////////////////////////////////////////////////////////////////////////////
  131. BOOL IsValidGroupingString(
  132. LPCWSTR pString,
  133. ULONG MaxLength,
  134. BOOL fCheckZeroLen)
  135. {
  136. ULONG Length; // string length
  137. LPWSTR pCur; // ptr to current position in string
  138. //
  139. // Search down the string to see if the chars are valid.
  140. // Save the length of the string.
  141. //
  142. pCur = (LPWSTR)pString;
  143. while (*pCur)
  144. {
  145. if ((*pCur < NLS_CHAR_ZERO) || (*pCur > NLS_CHAR_NINE))
  146. {
  147. //
  148. // String is NOT valid.
  149. //
  150. return (FALSE);
  151. }
  152. pCur++;
  153. if (*pCur)
  154. {
  155. if ((*pCur != NLS_CHAR_SEMICOLON) || (*(pCur + 1) == 0))
  156. {
  157. //
  158. // String is NOT valid.
  159. //
  160. return (FALSE);
  161. }
  162. pCur++;
  163. }
  164. }
  165. Length = (ULONG)(pCur - (LPWSTR)pString);
  166. //
  167. // Make sure the length is not greater than the maximum allowed.
  168. // Also, check for 0 length string (if appropriate).
  169. //
  170. if ((Length >= MaxLength) ||
  171. ((fCheckZeroLen) && (Length == 0)))
  172. {
  173. //
  174. // String is NOT valid.
  175. //
  176. return (FALSE);
  177. }
  178. //
  179. // String is valid.
  180. //
  181. return (TRUE);
  182. }
  183. ////////////////////////////////////////////////////////////////////////////
  184. //
  185. // IsValidCalendarType
  186. //
  187. // Returns the pointer to the optional calendar structure if the given
  188. // calendar type is valid for the given locale. Otherwise, it returns
  189. // NULL.
  190. //
  191. // 10-12-93 JulieB Created.
  192. ////////////////////////////////////////////////////////////////////////////
  193. LPWORD IsValidCalendarType(
  194. PLOC_HASH pHashN,
  195. CALID CalId)
  196. {
  197. LPWORD pOptCal; // ptr to list of optional calendars
  198. LPWORD pEndOptCal; // ptr to end of list of optional calendars
  199. //
  200. // Make sure the Cal Id is not zero, since that may be in the
  201. // optional calendar section (meaning no optional calendars).
  202. //
  203. if (CalId == 0)
  204. {
  205. return (NULL);
  206. }
  207. //
  208. // Search down the list of optional calendars.
  209. //
  210. pOptCal = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->IOptionalCal;
  211. pEndOptCal = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SDayName1;
  212. while (pOptCal < pEndOptCal)
  213. {
  214. //
  215. // Check the calendar ids.
  216. //
  217. if (CalId == ((POPT_CAL)pOptCal)->CalId)
  218. {
  219. //
  220. // Calendar id is valid for the given locale.
  221. //
  222. return (pOptCal);
  223. }
  224. //
  225. // Increment to the next optional calendar.
  226. //
  227. pOptCal += ((POPT_CAL)pOptCal)->Offset;
  228. }
  229. //
  230. // Calendar id is NOT valid if this point is reached.
  231. //
  232. return (NULL);
  233. }
  234. ////////////////////////////////////////////////////////////////////////////
  235. //
  236. // IsValidCalendarTypeStr
  237. //
  238. // Converts the calendar string to an integer and validates the calendar
  239. // id for the given locale. It return a pointer to the optional calendar
  240. // structure, or null if the calendar id was invalid.
  241. //
  242. // 10-19-93 JulieB Created.
  243. ////////////////////////////////////////////////////////////////////////////
  244. LPWORD IsValidCalendarTypeStr(
  245. PLOC_HASH pHashN,
  246. LPCWSTR pCalStr)
  247. {
  248. UNICODE_STRING ObUnicodeStr; // value string
  249. CALID CalNum; // calendar id
  250. //
  251. // Convert the string to an integer value.
  252. //
  253. RtlInitUnicodeString(&ObUnicodeStr, pCalStr);
  254. if (RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &CalNum))
  255. {
  256. return (NULL);
  257. }
  258. //
  259. // Validate the calendar id and return the pointer to the
  260. // optional calendar structure.
  261. //
  262. return (IsValidCalendarType(pHashN, CalNum));
  263. }
  264. ////////////////////////////////////////////////////////////////////////////
  265. //
  266. // GetCPFileNameFromRegistry
  267. //
  268. // Gets the name of the code page file from the registry. If pResultBuf
  269. // or Size == 0, then just return true if it exists in the registry, but
  270. // don't return the actual value.
  271. //
  272. // 05-31-2002 ShawnSte Created.
  273. ////////////////////////////////////////////////////////////////////////////
  274. BOOL GetCPFileNameFromRegistry(
  275. UINT CodePage,
  276. LPWSTR pResultBuf,
  277. UINT Size)
  278. {
  279. // Working things.
  280. WCHAR pTmpBuf[MAX_SMALL_BUF_LEN]; // temp buffer
  281. PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
  282. BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
  283. //
  284. // Convert value to unicode string.
  285. //
  286. if (!NT_SUCCESS(NlsConvertIntegerToString( CodePage,
  287. 10,
  288. 0,
  289. pTmpBuf,
  290. MAX_SMALL_BUF_LEN )))
  291. {
  292. // Didn't work. (Don't bother closing key though, its used globally)
  293. return (FALSE);
  294. }
  295. // Open hCodePageKey, return false if it fails
  296. OPEN_CODEPAGE_KEY(FALSE);
  297. //
  298. // Query the registry value for that code page.
  299. //
  300. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
  301. if ( NO_ERROR != QueryRegValue( hCodePageKey,
  302. pTmpBuf,
  303. &pKeyValueFull,
  304. MAX_KEY_VALUE_FULLINFO,
  305. NULL ) )
  306. {
  307. // Didn't work. (Don't bother closing key though, its used globally)
  308. return (FALSE);
  309. }
  310. //
  311. // Make sure there is data with this value.
  312. //
  313. if (GET_VALUE_DATA_PTR(pKeyValueFull)[0] == 0)
  314. {
  315. // Nope, no file name for this code page. (Not installed).
  316. return (FALSE);
  317. }
  318. // It worked, see if that's all they wanted.
  319. if (!pResultBuf || Size == 0)
  320. {
  321. // Caller didn't want the name, just to know if it was there
  322. return (TRUE);
  323. }
  324. // Now we have to copy the name to their buffer for them.
  325. if ( FAILED(StringCchCopyW(pResultBuf, Size, GET_VALUE_DATA_PTR(pKeyValueFull))))
  326. {
  327. // Couldn't make the string right, so fail
  328. return (FALSE);
  329. }
  330. // Yea, it worked
  331. return (TRUE);
  332. }
  333. ////////////////////////////////////////////////////////////////////////////
  334. //
  335. // GetUserInfoFromRegistry
  336. //
  337. // Gets the information from the registry for the given value entry.
  338. //
  339. // 06-11-98 JulieB Created.
  340. ////////////////////////////////////////////////////////////////////////////
  341. BOOL GetUserInfoFromRegistry(
  342. LPWSTR pValue,
  343. LPWSTR pOutput,
  344. size_t cchOutput,
  345. LCID Locale)
  346. {
  347. PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
  348. BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
  349. HANDLE hKey = NULL; // handle to intl key
  350. ULONG rc = 0L; // return code
  351. //
  352. // Open the Control Panel International registry key.
  353. //
  354. OPEN_CPANEL_INTL_KEY(hKey, FALSE, KEY_READ);
  355. //
  356. // Initialize the output string.
  357. //
  358. *pOutput = 0;
  359. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
  360. //
  361. // Check to be sure the current user is running in the given locale.
  362. //
  363. if (Locale)
  364. {
  365. if (NO_ERROR == QueryRegValue( hKey,
  366. L"Locale",
  367. &pKeyValueFull,
  368. MAX_KEY_VALUE_FULLINFO,
  369. NULL ))
  370. {
  371. UINT uiLocale;
  372. if (NlsConvertStringToIntegerW(GET_VALUE_DATA_PTR(pKeyValueFull), 16, -1, &uiLocale) &&
  373. uiLocale != Locale)
  374. {
  375. CLOSE_REG_KEY(hKey);
  376. return FALSE;
  377. }
  378. }
  379. }
  380. //
  381. // Query the registry value.
  382. //
  383. rc = QueryRegValue( hKey,
  384. pValue,
  385. &pKeyValueFull,
  386. MAX_KEY_VALUE_FULLINFO,
  387. NULL );
  388. //
  389. // Close the registry key.
  390. //
  391. CLOSE_REG_KEY(hKey);
  392. //
  393. // If the query failed or if the output buffer is not large enough,
  394. // then return failure.
  395. //
  396. if ((rc != NO_ERROR) ||
  397. (pKeyValueFull->DataLength > (MAX_REG_VAL_SIZE * sizeof(WCHAR))))
  398. {
  399. return (FALSE);
  400. }
  401. //
  402. // Save the string in pOutput.
  403. //
  404. if(FAILED(StringCchCopyW(pOutput, cchOutput, GET_VALUE_DATA_PTR(pKeyValueFull))))
  405. {
  406. return (FALSE);
  407. }
  408. //
  409. // Return success.
  410. //
  411. return (TRUE);
  412. }
  413. ////////////////////////////////////////////////////////////////////////////
  414. //
  415. // GetUserInfo
  416. //
  417. // Gets the information from the registry for the given locale and user
  418. // value entry.
  419. //
  420. // 05-31-91 JulieB Created.
  421. ////////////////////////////////////////////////////////////////////////////
  422. BOOL GetUserInfo(
  423. LCID Locale,
  424. LCTYPE LCType,
  425. SIZE_T CacheOffset,
  426. LPWSTR pValue,
  427. LPWSTR pOutput,
  428. size_t cchOutput,
  429. BOOL fCheckNull)
  430. {
  431. LCID UserLocale;
  432. HRESULT hr; // return val for string copy
  433. LPWSTR pCacheString;
  434. //
  435. // Check if the current thread/process is impersonating
  436. // or running in the context of a user other than the
  437. // interactive one.
  438. //
  439. if (NT_SUCCESS( NlsGetCurrentUserNlsInfo( Locale,
  440. LCType,
  441. pValue,
  442. pOutput,
  443. cchOutput,
  444. FALSE )))
  445. {
  446. //
  447. // See if we need to check for a null string.
  448. //
  449. if ((fCheckNull) && (*pOutput == 0))
  450. {
  451. return (FALSE);
  452. }
  453. return (TRUE);
  454. }
  455. //
  456. // Running in the same security context as the logged-on user.
  457. //
  458. RtlEnterCriticalSection(&gcsNlsProcessCache);
  459. if (pNlsUserInfo->ulCacheUpdateCount != pServerNlsUserInfo->ulCacheUpdateCount)
  460. {
  461. //
  462. // The cache content is out of date. Server has the latest copy of the cache, call server to update the
  463. // the cache.
  464. //
  465. {
  466. if (!NT_SUCCESS(CsrBasepNlsGetUserInfo(pNlsUserInfo, sizeof(NLS_USER_INFO))))
  467. {
  468. RtlLeaveCriticalSection(&gcsNlsProcessCache);
  469. //
  470. // The call to client failed, try to get the data from table.
  471. return (FALSE);
  472. }
  473. }
  474. //
  475. // If the call to server side succeeds, now we garantee that we have a complete
  476. // cache data, that is copied from the server side cache. It will have the same
  477. // ulCacheUpdateCount in the time when the call to server side happens.
  478. //
  479. }
  480. //
  481. // We are in critical section here to check UserLocale to make sure that LCID and the data are in sync.
  482. //
  483. UserLocale = pNlsUserInfo->UserLocaleId;
  484. //
  485. // Check to be sure cached user locale is the same as the given locale.
  486. //
  487. if (Locale != UserLocale)
  488. {
  489. RtlLeaveCriticalSection(&gcsNlsProcessCache);
  490. return (FALSE);
  491. }
  492. pCacheString = (LPWSTR)((LPBYTE)pNlsUserInfo + CacheOffset);
  493. hr = StringCchCopyW(pOutput, MAX_REG_VAL_SIZE, pCacheString);
  494. RtlLeaveCriticalSection(&gcsNlsProcessCache);
  495. //
  496. // Make sure the cache is valid.
  497. //
  498. // Also, check for an invalid entry. An invalid entry is marked
  499. // with NLS_INVALID_INFO_CHAR in the first position of the string
  500. // array.
  501. //
  502. if (FAILED(hr) || (*pOutput == NLS_INVALID_INFO_CHAR))
  503. {
  504. //
  505. // The cache is invalid, so try getting the information directly
  506. // from the registry.
  507. //
  508. return (GetUserInfoFromRegistry(pValue, pOutput, cchOutput, Locale));
  509. }
  510. //
  511. // See if we need to check for a null string.
  512. //
  513. if ((fCheckNull) && (*pOutput == 0))
  514. {
  515. return (FALSE);
  516. }
  517. //
  518. // Return success.
  519. //
  520. return (TRUE);
  521. }
  522. ////////////////////////////////////////////////////////////////////////////
  523. //
  524. // GetPreComposedChar
  525. //
  526. // Gets the precomposed character form of a given base character and
  527. // nonspacing character. If there is no precomposed form for the given
  528. // character, it returns 0.
  529. //
  530. // 05-31-91 JulieB Created.
  531. ////////////////////////////////////////////////////////////////////////////
  532. WCHAR FASTCALL GetPreComposedChar(
  533. WCHAR wcNonSp,
  534. WCHAR wcBase)
  535. {
  536. PCOMP_INFO pComp; // ptr to composite information
  537. WORD BSOff = 0; // offset of base char in grid
  538. WORD NSOff = 0; // offset of nonspace char in grid
  539. int Index; // index into grid
  540. //
  541. // Store the ptr to the composite information. No need to check if
  542. // it's a NULL pointer since all tables in the Unicode file are
  543. // constructed during initialization.
  544. //
  545. pComp = pTblPtrs->pComposite;
  546. //
  547. // Traverse 8:4:4 table for Base character offset.
  548. //
  549. BSOff = TRAVERSE_844_W(pComp->pBase, wcBase);
  550. if (!BSOff)
  551. {
  552. return (0);
  553. }
  554. //
  555. // Traverse 8:4:4 table for NonSpace character offset.
  556. //
  557. NSOff = TRAVERSE_844_W(pComp->pNonSp, wcNonSp);
  558. if (!NSOff)
  559. {
  560. return (0);
  561. }
  562. //
  563. // Get wide character value out of 2D grid.
  564. // If there is no precomposed character at the location in the
  565. // grid, it will return 0.
  566. //
  567. Index = (BSOff - 1) * pComp->NumNonSp + (NSOff - 1);
  568. return ((pComp->pGrid)[Index]);
  569. }
  570. ////////////////////////////////////////////////////////////////////////////
  571. //
  572. // GetCompositeChars
  573. //
  574. // Gets the composite characters of a given wide character. If the
  575. // composite form is found, it returns TRUE. Otherwise, it returns
  576. // FALSE.
  577. //
  578. // 05-31-91 JulieB Created.
  579. ////////////////////////////////////////////////////////////////////////////
  580. BOOL FASTCALL GetCompositeChars(
  581. WCHAR wch,
  582. WCHAR *pNonSp,
  583. WCHAR *pBase)
  584. {
  585. PPRECOMP pPreComp; // ptr to precomposed information
  586. //
  587. // Store the ptr to the precomposed information. No need to check if
  588. // it's a NULL pointer since all tables in the Unicode file are
  589. // constructed during initialization.
  590. //
  591. pPreComp = pTblPtrs->pPreComposed;
  592. //
  593. // Traverse 8:4:4 table for base and nonspace character translation.
  594. //
  595. TRAVERSE_844_D(pPreComp, wch, *pNonSp, *pBase);
  596. //
  597. // Return success if found. Otherwise, error.
  598. //
  599. return ((*pNonSp) && (*pBase));
  600. }
  601. ////////////////////////////////////////////////////////////////////////////
  602. //
  603. // InsertPreComposedForm
  604. //
  605. // Gets the precomposed form of a given wide character string, places it in
  606. // the given wide character, and returns the number of composite characters
  607. // used to form the precomposed form. If there is no precomposed form for
  608. // the given character, nothing is written into pPreComp and it returns 1
  609. // for the number of characters used.
  610. //
  611. // 05-31-91 JulieB Created.
  612. ////////////////////////////////////////////////////////////////////////////
  613. int FASTCALL InsertPreComposedForm(
  614. LPCWSTR pWCStr,
  615. LPWSTR pEndWCStr,
  616. LPWSTR pPreComp)
  617. {
  618. WCHAR wch; // precomposed character
  619. LPWSTR pPos; // ptr to position in string
  620. //
  621. // If no precomposed form can be found, return 1 character used
  622. // (base character).
  623. //
  624. if (((pWCStr + 1) >= pEndWCStr) ||
  625. (!(wch = GetPreComposedChar(*(pWCStr + 1), *pWCStr))))
  626. {
  627. return (1);
  628. }
  629. //
  630. // Get the precomposed character from the given wide character string.
  631. // Must check for multiple nonspacing characters for the same
  632. // precomposed character.
  633. //
  634. *pPreComp = wch;
  635. pPos = (LPWSTR)pWCStr + 2;
  636. while ((pPos < pEndWCStr) &&
  637. (wch = GetPreComposedChar(*pPos, *pPreComp)))
  638. {
  639. *pPreComp = wch;
  640. pPos++;
  641. }
  642. //
  643. // Return the number of characters used to form the precomposed
  644. // character.
  645. //
  646. return ((int)(pPos - (LPWSTR)pWCStr));
  647. }
  648. ////////////////////////////////////////////////////////////////////////////
  649. //
  650. // InsertFullWidthPreComposedForm
  651. //
  652. // Gets the full width precomposed form of a given wide character string,
  653. // places it in the given wide character, and returns the number of
  654. // composite characters used to form the precomposed form. If there is
  655. // no precomposed form for the given character, only the full width conversion
  656. // of the first code point is written into pPreComp and it returns 1 for
  657. // the number of characters used.
  658. //
  659. // 11-04-93 JulieB Created.
  660. ////////////////////////////////////////////////////////////////////////////
  661. int FASTCALL InsertFullWidthPreComposedForm(
  662. LPCWSTR pWCStr,
  663. LPWSTR pEndWCStr,
  664. LPWSTR pPreComp,
  665. PCASE pCase)
  666. {
  667. WCHAR wch; // nonspace character
  668. LPWSTR pPos; // ptr to position in string
  669. //
  670. // Get the case (if necessary).
  671. //
  672. *pPreComp = (pCase) ? GET_LOWER_UPPER_CASE(pCase, *pWCStr) : *pWCStr;
  673. //
  674. // Get the full width.
  675. //
  676. *pPreComp = GET_FULL_WIDTH(pTblPtrs->pFullWidth, *pPreComp);
  677. if ((pPos = ((LPWSTR)pWCStr + 1)) >= pEndWCStr)
  678. {
  679. return (1);
  680. }
  681. while (pPos < pEndWCStr)
  682. {
  683. wch = (pCase) ? GET_LOWER_UPPER_CASE(pCase, *pPos) : *pPos;
  684. wch = GET_FULL_WIDTH(pTblPtrs->pFullWidth, wch);
  685. if (wch = GetPreComposedChar(wch, *pPreComp))
  686. {
  687. *pPreComp = wch;
  688. pPos++;
  689. }
  690. else
  691. {
  692. break;
  693. }
  694. }
  695. //
  696. // Return the number of characters used to form the precomposed
  697. // character.
  698. //
  699. return ((int)(pPos - (LPWSTR)pWCStr));
  700. }
  701. ////////////////////////////////////////////////////////////////////////////
  702. //
  703. // InsertCompositeForm
  704. //
  705. // Gets the composite form of a given wide character, places it in the
  706. // wide character string, and returns the number of characters written.
  707. // If there is no composite form for the given character, the wide character
  708. // string is not touched. It will return 1 for the number of characters
  709. // written, since the base character was already written.
  710. //
  711. // 05-31-91 JulieB Created.
  712. ////////////////////////////////////////////////////////////////////////////
  713. int FASTCALL InsertCompositeForm(
  714. LPWSTR pWCStr,
  715. LPWSTR pEndWCStr)
  716. {
  717. WCHAR Base; // base character
  718. WCHAR NonSp; // non space character
  719. int wcCount = 0; // number of wide characters written
  720. LPWSTR pEndComp; // ptr to end of composite form
  721. int ctr; // loop counter
  722. //
  723. // If no composite form can be found, return 1 for the base
  724. // character that was already written.
  725. //
  726. if (!GetCompositeChars(*pWCStr, &NonSp, &Base))
  727. {
  728. return (1);
  729. }
  730. //
  731. // Get the composite characters and write them to the pWCStr
  732. // buffer. Must check for multiple breakdowns of the precomposed
  733. // character into more than 2 characters (multiple nonspacing
  734. // characters).
  735. //
  736. pEndComp = pWCStr;
  737. do
  738. {
  739. //
  740. // Make sure pWCStr is big enough to hold the nonspacing
  741. // character.
  742. //
  743. if (pEndComp < (pEndWCStr - 1))
  744. {
  745. //
  746. // Addition of next breakdown of nonspacing characters
  747. // are to be added right after the base character. So,
  748. // move all nonspacing characters ahead one position
  749. // to make room for the next nonspacing character.
  750. //
  751. pEndComp++;
  752. for (ctr = 0; ctr < wcCount; ctr++)
  753. {
  754. *(pEndComp - ctr) = *(pEndComp - (ctr + 1));
  755. }
  756. //
  757. // Fill in the new base form and the new nonspacing character.
  758. //
  759. *pWCStr = Base;
  760. *(pWCStr + 1) = NonSp;
  761. wcCount++;
  762. }
  763. else
  764. {
  765. //
  766. // Make sure we don't get into an infinite loop if the
  767. // destination buffer isn't large enough.
  768. //
  769. break;
  770. }
  771. } while (GetCompositeChars(*pWCStr, &NonSp, &Base));
  772. //
  773. // Return number of wide characters written. Add 1 to include the
  774. // base character.
  775. //
  776. return (wcCount + 1);
  777. }
  778. ////////////////////////////////////////////////////////////////////////////
  779. //
  780. // NlsConvertIntegerToString
  781. //
  782. // This routine converts an integer to a Unicode string.
  783. //
  784. // 11-15-96 JulieB Created.
  785. ////////////////////////////////////////////////////////////////////////////
  786. ULONG NlsConvertIntegerToString(
  787. UINT Value,
  788. UINT Base,
  789. UINT Padding,
  790. LPWSTR pResultBuf,
  791. UINT Size)
  792. {
  793. UNICODE_STRING ObString; // value string
  794. UINT ctr; // loop counter
  795. LPWSTR pBufPtr; // ptr to result buffer
  796. WCHAR pTmpBuf[MAX_PATH_LEN]; // ptr to temp buffer
  797. ULONG rc = 0L; // return code
  798. //
  799. // Set up the Unicode string structure.
  800. //
  801. ObString.Length = (USHORT)(Size * sizeof(WCHAR));
  802. ObString.MaximumLength = (USHORT)(Size * sizeof(WCHAR));
  803. ObString.Buffer = pTmpBuf;
  804. //
  805. // Get the value as a string.
  806. //
  807. if (rc = RtlIntegerToUnicodeString(Value, Base, &ObString))
  808. {
  809. return (rc);
  810. }
  811. //
  812. // Pad the string with the appropriate number of zeros.
  813. //
  814. pBufPtr = pResultBuf;
  815. for (ctr = GET_WC_COUNT(ObString.Length);
  816. ctr < Padding;
  817. ctr++, pBufPtr++, Size--)
  818. {
  819. if( Size < 1 )
  820. {
  821. return(STATUS_UNSUCCESSFUL);
  822. }
  823. *pBufPtr = NLS_CHAR_ZERO;
  824. }
  825. if(FAILED(StringCchCopyW(pBufPtr, Size, ObString.Buffer)))
  826. {
  827. return(STATUS_UNSUCCESSFUL);
  828. }
  829. return(STATUS_SUCCESS);
  830. }
  831. ////////////////////////////////////////////////////////////////////////////
  832. //
  833. // NlsConvertIntegerToHexStringW
  834. // Convert an integer value to an Unicode null-terminated string WITH
  835. // leading zeros. E.g. 0x409 with Width 5 will be converted to L"0409".
  836. // This function is faster than NlsConvertIntegerToString(), but it
  837. // only supports hex numbers.
  838. //
  839. // Parameters:
  840. // Value The number to be converted.
  841. // UpperCase If TRUE, the hex digit will be uppercase.
  842. // Str The buffer for the converted Unicode string.
  843. // Width The character count of the buffer. The value should be the total
  844. // heximal digit number plus one for null-terminiated.
  845. // E.g. if the value is from 0x0000 - 0xffff, the Width should be 5.
  846. //
  847. // Return:
  848. // TRUE if successful. FALSE if the width is not big enough to hold the converted string.
  849. //
  850. ////////////////////////////////////////////////////////////////////////////
  851. BOOL FASTCALL NlsConvertIntegerToHexStringW(UINT Value, BOOL UpperCase, PWSTR Str, UINT CharCount)
  852. {
  853. int Digit;
  854. PWSTR p;
  855. if(Str == NULL)
  856. {
  857. return (FALSE);
  858. }
  859. p = Str + CharCount - 1;
  860. *p-- = L'\0';
  861. while (p >= Str)
  862. {
  863. Digit = Value & 0xf;
  864. if (Digit < 10)
  865. {
  866. Digit = Digit + L'0';
  867. }
  868. else
  869. {
  870. Digit = Digit - 10 + (UpperCase ? L'A' : L'a');
  871. }
  872. *p-- = (WCHAR)Digit;
  873. Value >>= 4;
  874. }
  875. if (Value > 0)
  876. {
  877. //
  878. // There are still digit remaining.
  879. //
  880. return (FALSE);
  881. }
  882. return (TRUE);
  883. }
  884. ////////////////////////////////////////////////////////////////////////////
  885. //
  886. // NlsConvertStringToIntegerW
  887. //
  888. // Parameters:
  889. // Str the hex string to be converted.
  890. // Base base
  891. // CharCount
  892. // the character count of the string (excluding the terminiated-null, if any).
  893. // If the value is -1, this function assumes that
  894. // Str is a null-terminated string.
  895. // Result the pointer to the result.
  896. //
  897. // Result:
  898. // TRUE if the operation is successful. FALSE if there is non-hex
  899. // character in the string.
  900. //
  901. ////////////////////////////////////////////////////////////////////////////
  902. BOOL FASTCALL NlsConvertStringToIntegerW(PWSTR Str, UINT Base, int CharCount, UINT* Result)
  903. {
  904. int i;
  905. WCHAR Digit;
  906. WCHAR c;
  907. if (Str == NULL || Result == NULL)
  908. {
  909. return (FALSE);
  910. }
  911. *Result = 0;
  912. if (CharCount == -1)
  913. {
  914. while (c = *Str)
  915. {
  916. c = *Str;
  917. if (c >= L'0' && c <= L'9')
  918. {
  919. Digit = c - L'0';
  920. }
  921. else if(Base == 16)
  922. {
  923. if (c >= L'A' && c <= L'F')
  924. {
  925. Digit = c - L'A' + 10;
  926. }
  927. else if (c >= L'a' && c <= L'f')
  928. {
  929. Digit = c - L'a' + 10;
  930. }
  931. else
  932. {
  933. return (FALSE);
  934. }
  935. }
  936. else
  937. {
  938. return (FALSE);
  939. }
  940. if (Base == 16)
  941. {
  942. *Result = (*Result << 4) | Digit;
  943. }
  944. else
  945. {
  946. *Result = *Result*10 + Digit;
  947. }
  948. Str++;
  949. }
  950. } else
  951. {
  952. for (i=0; i< CharCount; i++) {
  953. c = *Str++;
  954. if (c >= L'0' && c <= L'9')
  955. {
  956. Digit = c - L'0';
  957. }
  958. else if(Base == 16)
  959. {
  960. if (c >= L'A' && c <= L'F')
  961. {
  962. Digit = c - L'A' + 10;
  963. }
  964. else if (c >= L'a' && c <= L'f')
  965. {
  966. Digit = c - L'a' + 10;
  967. }
  968. else
  969. {
  970. return (FALSE);
  971. }
  972. }
  973. else
  974. {
  975. return (FALSE);
  976. }
  977. if (Base == 16)
  978. {
  979. *Result = (*Result << 4) | Digit;
  980. }
  981. else
  982. {
  983. *Result = *Result*10 + Digit;
  984. }
  985. }
  986. }
  987. return (TRUE);
  988. }
  989. ////////////////////////////////////////////////////////////////////////////
  990. //
  991. // NlsStrLenW
  992. //
  993. // This routine returns the length of the given wide character string.
  994. // The length does NOT include the null terminator.
  995. //
  996. // NOTE: This routine is here to avoid any dependencies on other DLLs
  997. // during initialization.
  998. //
  999. // 05-31-91 JulieB Created.
  1000. ////////////////////////////////////////////////////////////////////////////
  1001. int FASTCALL NlsStrLenW(
  1002. LPCWSTR pwsz)
  1003. {
  1004. LPCWSTR pwszStart = pwsz; // ptr to beginning of string
  1005. loop:
  1006. if (*pwsz) pwsz++; else goto done;
  1007. if (*pwsz) pwsz++; else goto done;
  1008. if (*pwsz) pwsz++; else goto done;
  1009. if (*pwsz) pwsz++; else goto done;
  1010. if (*pwsz) pwsz++; else goto done;
  1011. if (*pwsz) pwsz++; else goto done;
  1012. if (*pwsz) pwsz++; else goto done;
  1013. if (*pwsz) pwsz++; else goto done;
  1014. if (*pwsz) pwsz++; else goto done;
  1015. if (*pwsz) pwsz++; else goto done;
  1016. if (*pwsz) pwsz++; else goto done;
  1017. if (*pwsz) pwsz++; else goto done;
  1018. if (*pwsz) pwsz++; else goto done;
  1019. if (*pwsz) pwsz++; else goto done;
  1020. if (*pwsz) pwsz++; else goto done;
  1021. if (*pwsz) pwsz++; else goto done;
  1022. goto loop;
  1023. done:
  1024. return ((int)(pwsz - pwszStart));
  1025. }
  1026. ////////////////////////////////////////////////////////////////////////////
  1027. //
  1028. // NlsStrEqualW
  1029. //
  1030. // This routine compares two strings to see if they are exactly identical.
  1031. // It returns 1 if they are identical, 0 if they are different.
  1032. //
  1033. // NOTE: This routine is here to avoid any dependencies on other DLLs
  1034. // during initialization.
  1035. //
  1036. // 05-31-91 JulieB Created.
  1037. ////////////////////////////////////////////////////////////////////////////
  1038. int FASTCALL NlsStrEqualW(
  1039. LPCWSTR pwszFirst,
  1040. LPCWSTR pwszSecond)
  1041. {
  1042. loop:
  1043. if (*pwszFirst != *pwszSecond) goto error;
  1044. if (!*pwszFirst) return (1);
  1045. pwszFirst++;
  1046. pwszSecond++;
  1047. if (*pwszFirst != *pwszSecond) goto error;
  1048. if (!*pwszFirst) return (1);
  1049. pwszFirst++;
  1050. pwszSecond++;
  1051. if (*pwszFirst != *pwszSecond) goto error;
  1052. if (!*pwszFirst) return (1);
  1053. pwszFirst++;
  1054. pwszSecond++;
  1055. if (*pwszFirst != *pwszSecond) goto error;
  1056. if (!*pwszFirst) return (1);
  1057. pwszFirst++;
  1058. pwszSecond++;
  1059. if (*pwszFirst != *pwszSecond) goto error;
  1060. if (!*pwszFirst) return (1);
  1061. pwszFirst++;
  1062. pwszSecond++;
  1063. if (*pwszFirst != *pwszSecond) goto error;
  1064. if (!*pwszFirst) return (1);
  1065. pwszFirst++;
  1066. pwszSecond++;
  1067. if (*pwszFirst != *pwszSecond) goto error;
  1068. if (!*pwszFirst) return (1);
  1069. pwszFirst++;
  1070. pwszSecond++;
  1071. if (*pwszFirst != *pwszSecond) goto error;
  1072. if (!*pwszFirst) return (1);
  1073. pwszFirst++;
  1074. pwszSecond++;
  1075. goto loop;
  1076. error:
  1077. //
  1078. // Return error.
  1079. //
  1080. return (0);
  1081. }
  1082. ////////////////////////////////////////////////////////////////////////////
  1083. //
  1084. // NlsStrNEqualW
  1085. //
  1086. // This routine compares two strings to see if they are exactly identical
  1087. // for the count of characters given.
  1088. // It returns 1 if they are identical, 0 if they are different.
  1089. //
  1090. // NOTE: This routine is here to avoid any dependencies on other DLLs
  1091. // during initialization.
  1092. //
  1093. // 05-31-91 JulieB Created.
  1094. ////////////////////////////////////////////////////////////////////////////
  1095. int FASTCALL NlsStrNEqualW(
  1096. LPCWSTR pwszFirst,
  1097. LPCWSTR pwszSecond,
  1098. int Count)
  1099. {
  1100. loop:
  1101. if (Count == 0) return (1);
  1102. if (*pwszFirst != *pwszSecond) goto error;
  1103. if (!*pwszFirst) return (1);
  1104. pwszFirst++;
  1105. pwszSecond++;
  1106. Count--;
  1107. if (Count == 0) return (1);
  1108. if (*pwszFirst != *pwszSecond) goto error;
  1109. if (!*pwszFirst) return (1);
  1110. pwszFirst++;
  1111. pwszSecond++;
  1112. Count--;
  1113. if (Count == 0) return (1);
  1114. if (*pwszFirst != *pwszSecond) goto error;
  1115. if (!*pwszFirst) return (1);
  1116. pwszFirst++;
  1117. pwszSecond++;
  1118. Count--;
  1119. if (Count == 0) return (1);
  1120. if (*pwszFirst != *pwszSecond) goto error;
  1121. if (!*pwszFirst) return (1);
  1122. pwszFirst++;
  1123. pwszSecond++;
  1124. Count--;
  1125. if (Count == 0) return (1);
  1126. if (*pwszFirst != *pwszSecond) goto error;
  1127. if (!*pwszFirst) return (1);
  1128. pwszFirst++;
  1129. pwszSecond++;
  1130. Count--;
  1131. if (Count == 0) return (1);
  1132. if (*pwszFirst != *pwszSecond) goto error;
  1133. if (!*pwszFirst) return (1);
  1134. pwszFirst++;
  1135. pwszSecond++;
  1136. Count--;
  1137. if (Count == 0) return (1);
  1138. if (*pwszFirst != *pwszSecond) goto error;
  1139. if (!*pwszFirst) return (1);
  1140. pwszFirst++;
  1141. pwszSecond++;
  1142. Count--;
  1143. if (Count == 0) return (1);
  1144. if (*pwszFirst != *pwszSecond) goto error;
  1145. if (!*pwszFirst) return (1);
  1146. pwszFirst++;
  1147. pwszSecond++;
  1148. Count--;
  1149. goto loop;
  1150. error:
  1151. //
  1152. // Return error.
  1153. //
  1154. return (0);
  1155. }
  1156. ////////////////////////////////////////////////////////////////////////////
  1157. //
  1158. // GetDefaultSortkeySize
  1159. //
  1160. ////////////////////////////////////////////////////////////////////////////
  1161. ULONG GetDefaultSortkeySize(
  1162. PLARGE_INTEGER pSize)
  1163. {
  1164. *pSize = pTblPtrs->DefaultSortkeySize;
  1165. return (STATUS_SUCCESS);
  1166. }
  1167. ////////////////////////////////////////////////////////////////////////////
  1168. //
  1169. // GetLinguistLangSize
  1170. //
  1171. ////////////////////////////////////////////////////////////////////////////
  1172. ULONG GetLinguistLangSize(
  1173. PLARGE_INTEGER pSize)
  1174. {
  1175. *pSize = pTblPtrs->LinguistLangSize;
  1176. return (STATUS_SUCCESS);
  1177. }
  1178. ////////////////////////////////////////////////////////////////////////////
  1179. //
  1180. // ValidateLocale
  1181. //
  1182. // Internal routine, called from server. Validates that a locale is
  1183. // present in the registry. This code comes from IsValidLocale, but
  1184. // does not check the internal data to prevent recursive calls to the
  1185. // server.
  1186. //
  1187. ////////////////////////////////////////////////////////////////////////////
  1188. BOOL ValidateLocale(
  1189. LCID Locale)
  1190. {
  1191. PKEY_VALUE_FULL_INFORMATION pKeyValueFull;
  1192. BYTE pStatic1[MAX_KEY_VALUE_FULLINFO];
  1193. BYTE pStatic2[MAX_KEY_VALUE_FULLINFO];
  1194. WCHAR pTmpBuf[MAX_PATH]; // temp buffer
  1195. UNICODE_STRING ObUnicodeStr; // registry data value string
  1196. DWORD Data; // registry data value
  1197. LPWSTR pData; // ptr to registry data
  1198. BOOL bResult = FALSE; // result value
  1199. //
  1200. // Invalid Locale Check.
  1201. //
  1202. if (IS_INVALID_LOCALE(Locale))
  1203. {
  1204. return (FALSE);
  1205. }
  1206. //
  1207. // Open the Locale, the Alternate Sorts, and the Language Groups
  1208. // registry keys.
  1209. //
  1210. OPEN_LOCALE_KEY(FALSE);
  1211. OPEN_ALT_SORTS_KEY(FALSE);
  1212. OPEN_LANG_GROUPS_KEY(FALSE);
  1213. //
  1214. // Convert locale value to Unicode string.
  1215. //
  1216. if (NlsConvertIntegerToString(Locale, 16, 8, pTmpBuf, MAX_PATH))
  1217. {
  1218. return (FALSE);
  1219. }
  1220. //
  1221. // Query the registry for the value.
  1222. //
  1223. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic1;
  1224. if (((QueryRegValue( hLocaleKey,
  1225. pTmpBuf,
  1226. &pKeyValueFull,
  1227. MAX_KEY_VALUE_FULLINFO,
  1228. NULL ) == NO_ERROR) ||
  1229. (QueryRegValue( hAltSortsKey,
  1230. pTmpBuf,
  1231. &pKeyValueFull,
  1232. MAX_KEY_VALUE_FULLINFO,
  1233. NULL ) == NO_ERROR)) &&
  1234. (pKeyValueFull->DataLength > 2))
  1235. {
  1236. RtlInitUnicodeString(&ObUnicodeStr, GET_VALUE_DATA_PTR(pKeyValueFull));
  1237. if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 16, &Data) == NO_ERROR) &&
  1238. (Data != 0))
  1239. {
  1240. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic2;
  1241. if ((QueryRegValue( hLangGroupsKey,
  1242. ObUnicodeStr.Buffer,
  1243. &pKeyValueFull,
  1244. MAX_KEY_VALUE_FULLINFO,
  1245. NULL ) == NO_ERROR) &&
  1246. (pKeyValueFull->DataLength > 2))
  1247. {
  1248. pData = GET_VALUE_DATA_PTR(pKeyValueFull);
  1249. if ((pData[0] == L'1') && (pData[1] == 0))
  1250. {
  1251. bResult = TRUE;
  1252. }
  1253. }
  1254. }
  1255. }
  1256. //
  1257. // Return the result.
  1258. //
  1259. return (TRUE);
  1260. }
  1261. ////////////////////////////////////////////////////////////////////////////
  1262. //
  1263. // ValidateLCType
  1264. //
  1265. // This routine is called from the server (and also from locale.c) in
  1266. // order to get a Registry key name and a field pointer in the NlsInfo
  1267. // structure given an LCType.
  1268. //
  1269. ////////////////////////////////////////////////////////////////////////////
  1270. BOOL ValidateLCType(
  1271. PNLS_USER_INFO pInfo,
  1272. LCTYPE LCType,
  1273. LPWSTR *ppwReg,
  1274. LPWSTR *ppwCache)
  1275. {
  1276. switch (LCType)
  1277. {
  1278. case ( LOCALE_IFIRSTWEEKOFYEAR ) :
  1279. {
  1280. *ppwReg = NLS_VALUE_IFIRSTWEEKOFYEAR;
  1281. *ppwCache = pInfo->iFirstWeek;
  1282. break;
  1283. }
  1284. case ( LOCALE_IFIRSTDAYOFWEEK ) :
  1285. {
  1286. *ppwReg = NLS_VALUE_IFIRSTDAYOFWEEK;
  1287. *ppwCache = pInfo->iFirstDay;
  1288. break;
  1289. }
  1290. case ( LOCALE_ICALENDARTYPE ) :
  1291. {
  1292. *ppwReg = NLS_VALUE_ICALENDARTYPE;
  1293. *ppwCache = pInfo->iCalType;
  1294. break;
  1295. }
  1296. case ( LOCALE_SLONGDATE ) :
  1297. {
  1298. *ppwReg = NLS_VALUE_SLONGDATE;
  1299. *ppwCache = pInfo->sLongDate;
  1300. break;
  1301. }
  1302. case ( LOCALE_SYEARMONTH ) :
  1303. {
  1304. *ppwReg = NLS_VALUE_SYEARMONTH;
  1305. *ppwCache = pInfo->sYearMonth;
  1306. break;
  1307. }
  1308. case ( LOCALE_S1159 ) :
  1309. {
  1310. *ppwReg = NLS_VALUE_S1159;
  1311. *ppwCache = pInfo->s1159;
  1312. break;
  1313. }
  1314. case ( LOCALE_SNEGATIVESIGN ) :
  1315. {
  1316. *ppwReg = NLS_VALUE_SNEGATIVESIGN;
  1317. *ppwCache = pInfo->sNegSign;
  1318. break;
  1319. }
  1320. case ( LOCALE_SPOSITIVESIGN ) :
  1321. {
  1322. *ppwReg = NLS_VALUE_SPOSITIVESIGN;
  1323. *ppwCache = pInfo->sPosSign;
  1324. break;
  1325. }
  1326. case ( LOCALE_INEGCURR ) :
  1327. {
  1328. *ppwReg = NLS_VALUE_INEGCURR;
  1329. *ppwCache = pInfo->iNegCurr;
  1330. break;
  1331. }
  1332. case ( LOCALE_ICURRENCY ) :
  1333. {
  1334. *ppwReg = NLS_VALUE_ICURRENCY;
  1335. *ppwCache = pInfo->iCurrency;
  1336. break;
  1337. }
  1338. case ( LOCALE_ICURRDIGITS ) :
  1339. {
  1340. *ppwReg = NLS_VALUE_ICURRDIGITS;
  1341. *ppwCache = pInfo->iCurrDigits;
  1342. break;
  1343. }
  1344. case ( LOCALE_SMONGROUPING ) :
  1345. {
  1346. *ppwReg = NLS_VALUE_SMONGROUPING;
  1347. *ppwCache = pInfo->sMonGrouping;
  1348. break;
  1349. }
  1350. case ( LOCALE_SMONTHOUSANDSEP ) :
  1351. {
  1352. *ppwReg = NLS_VALUE_SMONTHOUSANDSEP;
  1353. *ppwCache = pInfo->sMonThouSep;
  1354. break;
  1355. }
  1356. case ( LOCALE_SMONDECIMALSEP ) :
  1357. {
  1358. *ppwReg = NLS_VALUE_SMONDECIMALSEP;
  1359. *ppwCache = pInfo->sMonDecSep;
  1360. break;
  1361. }
  1362. case ( LOCALE_SCURRENCY ) :
  1363. {
  1364. *ppwReg = NLS_VALUE_SCURRENCY;
  1365. *ppwCache = pInfo->sCurrency;
  1366. break;
  1367. }
  1368. case ( LOCALE_IDIGITSUBSTITUTION ) :
  1369. {
  1370. *ppwReg = NLS_VALUE_IDIGITSUBST;
  1371. *ppwCache = pInfo->iDigitSubstitution;
  1372. break;
  1373. }
  1374. case ( LOCALE_SNATIVEDIGITS ) :
  1375. {
  1376. *ppwReg = NLS_VALUE_SNATIVEDIGITS;
  1377. *ppwCache = pInfo->sNativeDigits;
  1378. break;
  1379. }
  1380. case ( LOCALE_INEGNUMBER ) :
  1381. {
  1382. *ppwReg = NLS_VALUE_INEGNUMBER;
  1383. *ppwCache = pInfo->iNegNumber;
  1384. break;
  1385. }
  1386. case ( LOCALE_ILZERO ) :
  1387. {
  1388. *ppwReg = NLS_VALUE_ILZERO;
  1389. *ppwCache = pInfo->iLZero;
  1390. break;
  1391. }
  1392. case ( LOCALE_IDIGITS ) :
  1393. {
  1394. *ppwReg = NLS_VALUE_IDIGITS;
  1395. *ppwCache = pInfo->iDigits;
  1396. break;
  1397. }
  1398. case ( LOCALE_SGROUPING ) :
  1399. {
  1400. *ppwReg = NLS_VALUE_SGROUPING;
  1401. *ppwCache = pInfo->sGrouping;
  1402. break;
  1403. }
  1404. case ( LOCALE_STHOUSAND ) :
  1405. {
  1406. *ppwReg = NLS_VALUE_STHOUSAND;
  1407. *ppwCache = pInfo->sThousand;
  1408. break;
  1409. }
  1410. case ( LOCALE_SDECIMAL ) :
  1411. {
  1412. *ppwReg = NLS_VALUE_SDECIMAL;
  1413. *ppwCache = pInfo->sDecimal;
  1414. break;
  1415. }
  1416. case ( LOCALE_IPAPERSIZE ) :
  1417. {
  1418. *ppwReg = NLS_VALUE_IPAPERSIZE;
  1419. *ppwCache = pInfo->iPaperSize;
  1420. break;
  1421. }
  1422. case ( LOCALE_IMEASURE ) :
  1423. {
  1424. *ppwReg = NLS_VALUE_IMEASURE;
  1425. *ppwCache = pInfo->iMeasure;
  1426. break;
  1427. }
  1428. case ( LOCALE_SLIST ) :
  1429. {
  1430. *ppwReg = NLS_VALUE_SLIST;
  1431. *ppwCache = pInfo->sList;
  1432. break;
  1433. }
  1434. case ( LOCALE_S2359 ) :
  1435. {
  1436. *ppwReg = NLS_VALUE_S2359;
  1437. *ppwCache = pInfo->s2359;
  1438. break;
  1439. }
  1440. default :
  1441. {
  1442. return (FALSE);
  1443. }
  1444. }
  1445. return (TRUE);
  1446. }
  1447. ////////////////////////////////////////////////////////////////////////////
  1448. //
  1449. // GetStringTableEntry
  1450. //
  1451. // Returns the localized version of the strings for the given resource
  1452. // id. It gets the information from the resource file in the language that
  1453. // the current user is using.
  1454. //
  1455. // The string table contains a series of strings in the following order:
  1456. // Language Name
  1457. // Country Name
  1458. // Language Group Name
  1459. // Code Page Name (decimal values converted to hex values)
  1460. // Region (Geo) Friendly Name (decimal values converted to hex values)
  1461. // Region (Geo) Official Name (decimal values converted to hex values)
  1462. // Sorting Names (in order starting with 0, separated by $)
  1463. //
  1464. // Each string is separated by $. The final string is terminated with
  1465. // a null.
  1466. //
  1467. // The sorting names are in order of the sort ids, starting with 0.
  1468. //
  1469. // For example,
  1470. // "Language$Country$LangGrp$CodePage$Geo1$Geo2$Sort0$Sort1" or
  1471. // "Language$Country" or
  1472. // "$$LangGrp$CodePage" or
  1473. // "$$$CodePage" or
  1474. // "$$$$Geo1$Geo2"
  1475. //
  1476. // 11-17-00 JulieB Created.
  1477. ////////////////////////////////////////////////////////////////////////////
  1478. int GetStringTableEntry(
  1479. UINT ResourceID,
  1480. LANGID UILangId,
  1481. LPWSTR pBuffer,
  1482. int cchBuffer,
  1483. int WhichString)
  1484. {
  1485. HANDLE hFindRes; // handle from find resource
  1486. HANDLE hLoadRes; // handle from load resource
  1487. LPWSTR pSearch, pSearchEnd; // ptrs to search for correct string
  1488. LPWSTR pString; // ptr to final string
  1489. int cchCount = 0; // count of characters
  1490. //
  1491. // Make sure the buffer is ok.
  1492. //
  1493. if ((pBuffer == NULL) || (cchBuffer == 0))
  1494. {
  1495. return (0);
  1496. }
  1497. //
  1498. // Make sure we're not hitting the GEO ID that is out of bounds.
  1499. //
  1500. // !!! NOTE !!! This is needed because the East Timor Geo Id
  1501. // is out of bounds and wraps to 0x60e7.
  1502. //
  1503. if (ResourceID == 0x60e7)
  1504. {
  1505. return (0);
  1506. }
  1507. //
  1508. // Set the UI Language Id.
  1509. //
  1510. if (UILangId == 0)
  1511. {
  1512. UILangId = GetUserDefaultUILanguage();
  1513. }
  1514. //
  1515. // String Tables are broken up into 16 string segments. Find the
  1516. // resource containing the string we want.
  1517. //
  1518. if ((!(hFindRes = FindResourceExW( hModule,
  1519. RT_STRING,
  1520. (LPWSTR)UlongToPtr((ULONG)(((USHORT)ResourceID >> 4) + 1)),
  1521. (WORD)UILangId ))))
  1522. {
  1523. //
  1524. // Could not find resource. Try NEUTRAL language id.
  1525. //
  1526. if ((!(hFindRes = FindResourceExW( hModule,
  1527. RT_STRING,
  1528. (LPWSTR)UlongToPtr((ULONG)(((USHORT)ResourceID >> 4) + 1)),
  1529. (WORD)0 ))))
  1530. {
  1531. //
  1532. // Could not find resource. Return 0.
  1533. //
  1534. return (0);
  1535. }
  1536. }
  1537. //
  1538. // Load the resource.
  1539. //
  1540. if (hLoadRes = LoadResource(hModule, hFindRes))
  1541. {
  1542. //
  1543. // Lock the resource. Store the found pointer in the given
  1544. // pointer.
  1545. //
  1546. if (pSearch = (LPWSTR)LockResource(hLoadRes))
  1547. {
  1548. //
  1549. // Move past the other strings in this segment.
  1550. // (16 strings in a segment -> & 0x0F)
  1551. //
  1552. ResourceID &= 0x0F;
  1553. //
  1554. // Find the correct string in this segment.
  1555. //
  1556. while (TRUE)
  1557. {
  1558. cchCount = *((WORD *)pSearch++);
  1559. if (ResourceID-- == 0)
  1560. {
  1561. break;
  1562. }
  1563. pSearch += cchCount;
  1564. }
  1565. //
  1566. // Mark the end of the resource string since it is not
  1567. // NULL terminated.
  1568. //
  1569. pSearchEnd = pSearch + cchCount;
  1570. //
  1571. // Get to the appropriate string.
  1572. //
  1573. while ((WhichString > 0) && (pSearch < pSearchEnd))
  1574. {
  1575. do
  1576. {
  1577. if (*pSearch == RC_STRING_SEPARATOR)
  1578. {
  1579. pSearch++;
  1580. break;
  1581. }
  1582. pSearch++;
  1583. } while (pSearch < pSearchEnd);
  1584. WhichString--;
  1585. }
  1586. //
  1587. // Count the number of characters for this string.
  1588. //
  1589. pString = pSearch;
  1590. cchCount = 0;
  1591. while ((pSearch < pSearchEnd) && (*pSearch != RC_STRING_SEPARATOR))
  1592. {
  1593. pSearch++;
  1594. cchCount++;
  1595. }
  1596. //
  1597. // See if there is anything to copy.
  1598. //
  1599. if (cchCount > 0)
  1600. {
  1601. //
  1602. // Don't copy more than the max allowed.
  1603. //
  1604. if (cchCount >= cchBuffer)
  1605. {
  1606. cchCount = cchBuffer - 1;
  1607. }
  1608. //
  1609. // Copy the string into the buffer and NULL terminate it.
  1610. //
  1611. CopyMemory(pBuffer, pString, cchCount * sizeof(WCHAR));
  1612. pBuffer[cchCount] = 0;
  1613. //
  1614. // Return the number of characters in the string, not
  1615. // including the NULL terminator.
  1616. //
  1617. return (cchCount);
  1618. }
  1619. }
  1620. }
  1621. //
  1622. // Return failure.
  1623. //
  1624. return (0);
  1625. }
  1626. ////////////////////////////////////////////////////////////////////////////
  1627. //
  1628. // NlsIsDll
  1629. //
  1630. // Check if file extension is DLL
  1631. //
  1632. ////////////////////////////////////////////////////////////////////////////
  1633. #define DLL_SUFFIX_LENGTH 4 // (.XXX)
  1634. BOOL FASTCALL NlsIsDll(
  1635. LPCWSTR pFileName
  1636. )
  1637. {
  1638. BOOL bIsDll = FALSE;
  1639. if (pFileName)
  1640. {
  1641. size_t iLen = 0;
  1642. if(SUCCEEDED(StringCchLengthW(pFileName, MAX_PATH, &iLen)))
  1643. {
  1644. //
  1645. // Check DLL extension, save the trouble of calling lstricmpW
  1646. //
  1647. // REVIEW: lstricmpW would not be an appropriate function to
  1648. // call here anyway, since user locale collation
  1649. // semantics != file system collation semantics.
  1650. //
  1651. if (iLen > DLL_SUFFIX_LENGTH)
  1652. {
  1653. pFileName += iLen - DLL_SUFFIX_LENGTH;
  1654. //
  1655. // File names are lower case in setup, so optimize for that
  1656. // by putting them first.
  1657. //
  1658. if ((pFileName[0] == L'.') &&
  1659. (pFileName[1] == L'd' || pFileName[1] == L'D') &&
  1660. (pFileName[2] == L'l' || pFileName[2] == L'L') &&
  1661. (pFileName[3] == L'l' || pFileName[3] == L'L'))
  1662. {
  1663. bIsDll = TRUE;
  1664. }
  1665. }
  1666. }
  1667. }
  1668. return bIsDll;
  1669. }
  1670. ////////////////////////////////////////////////////////////////////////////
  1671. //
  1672. // IsCodePointDefined
  1673. //
  1674. // Check if the code point is defined.
  1675. //
  1676. ////////////////////////////////////////////////////////////////////////////
  1677. BOOL FASTCALL IsSortingCodePointDefined(
  1678. LPNLSVERSIONINFO lpVersionInformation,
  1679. LPCWSTR lpString,
  1680. INT cchStr
  1681. )
  1682. {
  1683. PNLSDEFINED pDefinedCodePoints = NULL;
  1684. LPCWSTR pStringEnd;
  1685. //
  1686. // Make sure the appropriate tables are available. If not,
  1687. // return an error.
  1688. //
  1689. if ((pTblPtrs->pDefinedVersion == NULL) ||
  1690. (pTblPtrs->pSortingTableFileBase == NULL) ||
  1691. (pTblPtrs->pDefaultSortkey == NULL))
  1692. {
  1693. KdPrint(("NLSAPI: Appropriate Tables (Defined, Base and/or Default) Not Loaded.\n"));
  1694. SetLastError(ERROR_FILE_NOT_FOUND);
  1695. return (FALSE);
  1696. }
  1697. //
  1698. // Get the version.
  1699. //
  1700. if (lpVersionInformation != NULL)
  1701. {
  1702. UINT idx;
  1703. //
  1704. // Buffer size check.
  1705. //
  1706. if (lpVersionInformation->dwNLSVersionInfoSize != sizeof(NLSVERSIONINFO))
  1707. {
  1708. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  1709. return (FALSE);
  1710. }
  1711. if ((lpVersionInformation->dwDefinedVersion == 0L) ||
  1712. (lpVersionInformation->dwDefinedVersion == (pTblPtrs->pDefinedVersion)[0].Version))
  1713. {
  1714. // Use the current version.
  1715. // Do nothing here. We let pDefinedCodePoints to be NULL, so that current table is used.
  1716. }
  1717. else
  1718. {
  1719. if (lpVersionInformation->dwDefinedVersion < pTblPtrs->NumDefinedVersion) {
  1720. //
  1721. // Not the default version, get the the requested version.
  1722. //
  1723. pDefinedCodePoints = (PNLSDEFINED)(pTblPtrs->pSortingTableFileBase + (pTblPtrs->pDefinedVersion)[lpVersionInformation->dwDefinedVersion].dwOffset);
  1724. }
  1725. //
  1726. // Check if the version requested is valid.
  1727. //
  1728. if (pDefinedCodePoints == NULL)
  1729. {
  1730. SetLastError(ERROR_INVALID_PARAMETER);
  1731. return (FALSE);
  1732. }
  1733. }
  1734. }
  1735. pStringEnd = lpString + cchStr;
  1736. //
  1737. // Check if we deal with the current version.
  1738. //
  1739. if (pDefinedCodePoints == NULL)
  1740. {
  1741. //
  1742. // Use the default table.
  1743. //
  1744. // For each code point verify is they exist
  1745. // in the table.
  1746. //
  1747. while (lpString < pStringEnd)
  1748. {
  1749. //
  1750. // Check is the fist script member is defined for this codepoint.
  1751. //
  1752. if ((pTblPtrs->pDefaultSortkey)[*lpString].UW.SM_AW.Script == UNSORTABLE)
  1753. {
  1754. //
  1755. // Check for the NULL case and formatting characters case. Not
  1756. // defined but valid.
  1757. //
  1758. if ((*lpString == L'\x0000') ||
  1759. (*lpString == L'\x0640') ||
  1760. ((*lpString >= L'\x180B') && (*lpString <= L'\x180E')) ||
  1761. ((*lpString >= L'\x200C') && (*lpString <= L'\x200F')) ||
  1762. ((*lpString >= L'\x202A') && (*lpString <= L'\x202E')) ||
  1763. ((*lpString >= L'\x206A') && (*lpString <= L'\x206F')) ||
  1764. (*lpString == L'\xFEFF') ||
  1765. (*lpString == L'\xFFF9') ||
  1766. ((*lpString >= L'\xFFFA') && (*lpString <= L'\xFFFD')))
  1767. {
  1768. lpString++;
  1769. continue;
  1770. }
  1771. else
  1772. {
  1773. return (FALSE);
  1774. }
  1775. }
  1776. //
  1777. // Eliminate Private Use characters. They are defined but cannot be considered
  1778. // valid.
  1779. //
  1780. if ((*lpString >= L'\xE000') && (*lpString <= L'\xF8FF'))
  1781. {
  1782. return (FALSE);
  1783. }
  1784. //
  1785. // Eliminate invalid surogates pairs or single surrogates.
  1786. //
  1787. if ((*lpString >= L'\xDC00') && (*lpString <= L'\xDFFF')) // Leading low surrogate
  1788. {
  1789. return (FALSE);
  1790. }
  1791. else if ((*lpString >= L'\xD800') && (*lpString <= L'\xDBFF')) // Leading high surrogate
  1792. {
  1793. if ( ((lpString + 1) < pStringEnd) && // Surrogates not the last character
  1794. (*(lpString + 1) >= L'\xDC00') && (*(lpString + 1) <= L'\xDFFF')) // Low surrogate
  1795. {
  1796. lpString++; // Valid surrogates pair, High followed by a low surrogate. Skip the pair!
  1797. }
  1798. else
  1799. {
  1800. return (FALSE);
  1801. }
  1802. }
  1803. lpString++;
  1804. }
  1805. }
  1806. else
  1807. {
  1808. WORD wIndex;
  1809. BYTE wMod32Val;
  1810. while (lpString < pStringEnd)
  1811. {
  1812. //
  1813. // Compute the modulo 32 of the code point value.
  1814. //
  1815. wMod32Val = (BYTE)(*lpString & 0x0000001f); // 0x1fff => 5 bits
  1816. //
  1817. // Compute the DWORD index that contain the desired code point.
  1818. //
  1819. wIndex = (WORD)(*lpString >> 5);
  1820. //
  1821. // Get the DWORD aligned entry that contain the desired code point.
  1822. //
  1823. // Note: We need to get a DWORD aligned value to make sure that we
  1824. // that we don't access memory outside the table especially at the
  1825. // end of the table.
  1826. //
  1827. //
  1828. // Shift the value to retrieve information about code point at the
  1829. // position 0.
  1830. //
  1831. //
  1832. // Check is the code point is defined or not.
  1833. //
  1834. if ((pDefinedCodePoints[wIndex] >> wMod32Val) == 0)
  1835. {
  1836. // NOTENOTE YSLin: In NLSTrans, make sure that we mark U+0000 as 1, instead of 0.
  1837. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+E000-U+F8FF as 0.
  1838. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+070F as 1.
  1839. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+0640 as 1.
  1840. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+180B-U+180E as 1.
  1841. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+200C-U+200F as 1.
  1842. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+202A-U+202E as 1.
  1843. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+206A-U+206F as 1.
  1844. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+FEFF as 1.
  1845. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+FFF9 as 1.
  1846. // NOTENOTE lguindon: In NLSTrans, make sure that we mark U+FFFA-U+FFFD as 1.
  1847. return (FALSE);
  1848. }
  1849. //
  1850. // Eliminate invalid surogates pairs or single surrogates.
  1851. //
  1852. if ((*lpString >= L'\xDC00') && (*lpString <= L'\xDFFF')) // Leading low surrogate
  1853. {
  1854. return (FALSE);
  1855. }
  1856. else if ((*lpString >= L'\xD800') && (*lpString <= L'\xDBFF')) // Leading high surrogate
  1857. {
  1858. if ( ((lpString + 1) < pStringEnd) && // Surrogates not the last character
  1859. (*(lpString + 1) >= L'\xDC00') && (*(lpString + 1) <= L'\xDFFF')) // Low surrogate
  1860. {
  1861. lpString++; // Valid surrogates pair, High followed by a low surrogate. Skip the pair!
  1862. }
  1863. else
  1864. {
  1865. return (FALSE);
  1866. }
  1867. }
  1868. lpString++;
  1869. }
  1870. }
  1871. return (TRUE);
  1872. }