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.

6276 lines
194 KiB

  1. /*++
  2. Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
  3. Module Name:
  4. locale.c
  5. Abstract:
  6. This file contains functions that return information about a
  7. language group, a UI language, a locale, or a calendar.
  8. APIs found in this file:
  9. IsValidLanguageGroup
  10. IsValidLocale
  11. IsValidUILanguage
  12. ConvertDefaultLocale
  13. GetThreadLocale
  14. SetThreadLocale
  15. SetThreadUILanguage
  16. GetSystemDefaultUILanguage
  17. GetUserDefaultUILanguage
  18. GetSystemDefaultLangID
  19. GetUserDefaultLangID
  20. GetSystemDefaultLCID
  21. GetUserDefaultLCID
  22. VerLanguageNameW
  23. VerLanguageNameA
  24. GetLocaleInfoW
  25. SetLocaleInfoW
  26. GetCalendarInfoW
  27. SetCalendarInfoW
  28. Revision History:
  29. 05-31-91 JulieB Created.
  30. --*/
  31. //
  32. // Include Files.
  33. //
  34. #include "nls.h"
  35. #include "nlssafe.h"
  36. //
  37. // Allow this file to build without warnings when the DUnicode switch
  38. // is turned off.
  39. //
  40. #undef MAKEINTRESOURCE
  41. #define MAKEINTRESOURCE MAKEINTRESOURCEW
  42. ////////////////////////////////////////////////////////////////////////////
  43. //
  44. // NLS_STRING_TO_INTEGER
  45. //
  46. // Converts a string to an integer value.
  47. //
  48. // DEFINED AS A MACRO.
  49. //
  50. // 10-19-93 JulieB Created.
  51. ////////////////////////////////////////////////////////////////////////////
  52. #define NLS_STRING_TO_INTEGER( CalData, \
  53. pCalData ) \
  54. { \
  55. UNICODE_STRING ObUnicodeStrCalData; /* value string */ \
  56. \
  57. \
  58. /* \
  59. * No need to check return value since the calendar number \
  60. * will be validated after this anyway. \
  61. */ \
  62. RtlInitUnicodeString(&ObUnicodeStrCalData, pCalData); \
  63. RtlUnicodeStringToInteger(&ObUnicodeStrCalData, 10, &CalData); \
  64. }
  65. //
  66. // Global Variables.
  67. //
  68. LCID gProcessLocale;
  69. //
  70. // Forward Declarations.
  71. //
  72. BOOL
  73. SetUserInfo(
  74. LCTYPE LCType,
  75. LPWSTR pData,
  76. ULONG DataLength);
  77. BOOL SetCurrentUserRegValue(
  78. LCTYPE LCType,
  79. LPWSTR pData,
  80. ULONG DataLength);
  81. BOOL
  82. SetMultipleUserInfo(
  83. DWORD dwFlags,
  84. int cchData,
  85. LPCWSTR pPicture,
  86. LPCWSTR pSeparator,
  87. LPCWSTR pOrder,
  88. LPCWSTR pTLZero,
  89. LPCWSTR pTimeMarkPosn);
  90. BOOL
  91. SetTwoDigitYearInfo(
  92. CALID Calendar,
  93. LPCWSTR pYearInfo,
  94. int cchData);
  95. void
  96. GetInstallLanguageFromRegistry();
  97. //-------------------------------------------------------------------------//
  98. // PRIVATE API ROUTINES //
  99. //-------------------------------------------------------------------------//
  100. ////////////////////////////////////////////////////////////////////////////
  101. //
  102. // NlsResetProcessLocale
  103. //
  104. ////////////////////////////////////////////////////////////////////////////
  105. void NlsResetProcessLocale(void)
  106. {
  107. //
  108. // If the thread isn't impersonating, then re-read the process locale
  109. // from the current user's registry.
  110. //
  111. if (NtCurrentTeb()->IsImpersonating == 0L)
  112. {
  113. NlsFlushProcessCache(LOCALE_SLOCALE);
  114. NlsGetUserLocale(&gProcessLocale);
  115. }
  116. return;
  117. }
  118. //-------------------------------------------------------------------------//
  119. // API ROUTINES //
  120. //-------------------------------------------------------------------------//
  121. ////////////////////////////////////////////////////////////////////////////
  122. //
  123. // IsValidLanguageGroup
  124. //
  125. // Determines whether or not a language group is installed in the system
  126. // if the LGRPID_INSTALLED flag is set, or whether or not a language group
  127. // is supported in the system if the LGRPID_SUPPORTED flag is set.
  128. //
  129. // 03-10-98 JulieB Created.
  130. ////////////////////////////////////////////////////////////////////////////
  131. BOOL WINAPI IsValidLanguageGroup(
  132. LGRPID LanguageGroup,
  133. DWORD dwFlags)
  134. {
  135. PKEY_VALUE_FULL_INFORMATION pKeyValueFull;
  136. BYTE pStatic[MAX_KEY_VALUE_FULLINFO];
  137. WCHAR pTmpBuf[MAX_PATH]; // temp buffer
  138. UNICODE_STRING ObUnicodeStr; // registry data value string
  139. LPWSTR pData; // ptr to registry data
  140. //
  141. // Invalid Flags Check:
  142. // - flags other than valid ones
  143. // - more than one of either supported or installed
  144. //
  145. if ((dwFlags & IVLG_INVALID_FLAG) ||
  146. (MORE_THAN_ONE(dwFlags, IVLG_SINGLE_FLAG)))
  147. {
  148. return (FALSE);
  149. }
  150. //
  151. // Open the Language Groups registry key.
  152. //
  153. OPEN_LANG_GROUPS_KEY(FALSE);
  154. //
  155. // Convert language group value to Unicode string.
  156. //
  157. if (NlsConvertIntegerToString(LanguageGroup, 16, 1, pTmpBuf, MAX_PATH))
  158. {
  159. return (FALSE);
  160. }
  161. //
  162. // Query the registry for the value.
  163. //
  164. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
  165. if ((QueryRegValue( hLangGroupsKey,
  166. pTmpBuf,
  167. &pKeyValueFull,
  168. MAX_KEY_VALUE_FULLINFO,
  169. NULL ) != NO_ERROR))
  170. {
  171. return (FALSE);
  172. }
  173. //
  174. // Language Group is SUPPORTED. If the INSTALLED flag is NOT set, then
  175. // return success.
  176. //
  177. if (!(dwFlags & LGRPID_INSTALLED))
  178. {
  179. return (TRUE);
  180. }
  181. //
  182. // Need to find out if it's installed.
  183. //
  184. if (pKeyValueFull->DataLength > 2)
  185. {
  186. pData = GET_VALUE_DATA_PTR(pKeyValueFull);
  187. if ((pData[0] == L'1') && (pData[1] == 0))
  188. {
  189. return (TRUE);
  190. }
  191. }
  192. //
  193. // Return result.
  194. //
  195. return (FALSE);
  196. }
  197. ////////////////////////////////////////////////////////////////////////////
  198. //
  199. // IsValidLocale
  200. //
  201. // Determines whether or not a locale is installed in the system if the
  202. // LCID_INSTALLED flag is set, or whether or not a locale is supported in
  203. // the system if the LCID_SUPPORTED flag is set.
  204. //
  205. // 07-26-93 JulieB Created.
  206. ////////////////////////////////////////////////////////////////////////////
  207. BOOL WINAPI IsValidLocale(
  208. LCID Locale,
  209. DWORD dwFlags)
  210. {
  211. PKEY_VALUE_FULL_INFORMATION pKeyValueFull;
  212. BYTE pStatic1[MAX_KEY_VALUE_FULLINFO];
  213. BYTE pStatic2[MAX_KEY_VALUE_FULLINFO];
  214. WCHAR pTmpBuf[MAX_PATH]; // temp buffer
  215. UNICODE_STRING ObUnicodeStr; // registry data value string
  216. DWORD Data; // registry data value
  217. LPWSTR pData; // ptr to registry data
  218. BOOL bResult = FALSE; // result value
  219. //
  220. // Invalid Flags Check:
  221. // - flags other than valid ones
  222. // - more than one of either supported or installed
  223. //
  224. if ((dwFlags & IVL_INVALID_FLAG) ||
  225. (MORE_THAN_ONE(dwFlags, IVL_SINGLE_FLAG)))
  226. {
  227. //
  228. // The ME release of NT 4 did a really bad thing and allowed 0x39
  229. // to be passed in as a valid flag value for Arabic and Hebrew.
  230. // As a result, we need to allow this flag combination for
  231. // the Arabic and Hebrew locales.
  232. //
  233. if ((dwFlags == 0x39) &&
  234. ((Locale == MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT)) ||
  235. (Locale == MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT))))
  236. {
  237. dwFlags = LCID_INSTALLED;
  238. }
  239. else
  240. {
  241. return (FALSE);
  242. }
  243. }
  244. //
  245. // Invalid Locale Check.
  246. //
  247. if (IS_INVALID_LOCALE(Locale))
  248. {
  249. return (FALSE);
  250. }
  251. //
  252. // See if the LOCALE information is in the system for the
  253. // given locale.
  254. //
  255. if (GetLocHashNode(Locale) == NULL)
  256. {
  257. //
  258. // Return failure.
  259. //
  260. return (FALSE);
  261. }
  262. //
  263. // Locale is SUPPORTED. If the INSTALLED flag is NOT set, then
  264. // return success.
  265. //
  266. if (!(dwFlags & LCID_INSTALLED))
  267. {
  268. return (TRUE);
  269. }
  270. //
  271. // Open the Locale, the Alternate Sorts, and the Language Groups
  272. // registry keys.
  273. //
  274. OPEN_LOCALE_KEY(FALSE);
  275. OPEN_ALT_SORTS_KEY(FALSE);
  276. OPEN_LANG_GROUPS_KEY(FALSE);
  277. //
  278. // Convert locale value to Unicode string.
  279. //
  280. if (NlsConvertIntegerToString(Locale, 16, 8, pTmpBuf, MAX_PATH))
  281. {
  282. return (FALSE);
  283. }
  284. //
  285. // Query the registry for the value.
  286. //
  287. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic1;
  288. if (((QueryRegValue( hLocaleKey,
  289. pTmpBuf,
  290. &pKeyValueFull,
  291. MAX_KEY_VALUE_FULLINFO,
  292. NULL ) == NO_ERROR) ||
  293. (QueryRegValue( hAltSortsKey,
  294. pTmpBuf,
  295. &pKeyValueFull,
  296. MAX_KEY_VALUE_FULLINFO,
  297. NULL ) == NO_ERROR)) &&
  298. (pKeyValueFull->DataLength > 2))
  299. {
  300. RtlInitUnicodeString(&ObUnicodeStr, GET_VALUE_DATA_PTR(pKeyValueFull));
  301. if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 16, &Data) == NO_ERROR) &&
  302. (Data != 0))
  303. {
  304. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic2;
  305. if ((QueryRegValue( hLangGroupsKey,
  306. ObUnicodeStr.Buffer,
  307. &pKeyValueFull,
  308. MAX_KEY_VALUE_FULLINFO,
  309. NULL ) == NO_ERROR) &&
  310. (pKeyValueFull->DataLength > 2))
  311. {
  312. pData = GET_VALUE_DATA_PTR(pKeyValueFull);
  313. if ((pData[0] == L'1') && (pData[1] == 0))
  314. {
  315. bResult = TRUE;
  316. }
  317. }
  318. }
  319. }
  320. //
  321. // Return result.
  322. //
  323. return (bResult);
  324. }
  325. ////////////////////////////////////////////////////////////////////////////
  326. //
  327. // IsValidUILanguage
  328. //
  329. // Determines whether or not the specified UI language is installed in the system.
  330. //
  331. //
  332. // 12-03-00 YSLin Created.
  333. ////////////////////////////////////////////////////////////////////////////
  334. BOOL WINAPI IsValidUILanguage(LANGID UILangID)
  335. {
  336. NTSTATUS Status;
  337. OBJECT_ATTRIBUTES ObjectAttributes;
  338. UNICODE_STRING KeyPath, KeyValueName;
  339. HANDLE Key;
  340. WCHAR UILangIDStr[5];
  341. WCHAR KeyValueBuffer[ 128 ];
  342. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  343. ULONG ResultLength;
  344. ULONG Value = 0, Digit, i;
  345. WCHAR c;
  346. BOOL Result = FALSE;
  347. RtlInitUnicodeString(&KeyPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\MUILanguages");
  348. if (!NlsConvertIntegerToHexStringW(UILangID, FALSE, UILangIDStr, sizeof(UILangIDStr)/sizeof(WCHAR)))
  349. {
  350. return (FALSE);
  351. }
  352. RtlInitUnicodeString(&KeyValueName, UILangIDStr);
  353. InitializeObjectAttributes (&ObjectAttributes,
  354. &KeyPath,
  355. OBJ_CASE_INSENSITIVE,
  356. NULL,
  357. NULL);
  358. if (NT_SUCCESS(NtOpenKey (&Key, KEY_READ, &ObjectAttributes)))
  359. {
  360. KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
  361. Status = NtQueryValueKey(Key,
  362. &KeyValueName,
  363. KeyValuePartialInformation,
  364. KeyValueInformation,
  365. sizeof( KeyValueBuffer ),
  366. &ResultLength
  367. );
  368. if (NT_SUCCESS(Status))
  369. {
  370. if (KeyValueInformation->Type == REG_SZ && *((PWSTR)(KeyValueInformation->Data)) == L'1')
  371. {
  372. Result = TRUE;
  373. }
  374. }
  375. NtClose(Key);
  376. }
  377. return (Result);
  378. }
  379. ////////////////////////////////////////////////////////////////////////////
  380. //
  381. // ConvertDefaultLocale
  382. //
  383. // Converts any of the special case locale values to an actual locale id.
  384. // If none of the special case locales was given, the given locale id
  385. // is returned.
  386. //
  387. // 09-01-93 JulieB Created.
  388. ////////////////////////////////////////////////////////////////////////////
  389. LCID WINAPI ConvertDefaultLocale(
  390. LCID Locale)
  391. {
  392. //
  393. // Check for the special locale values.
  394. //
  395. CHECK_SPECIAL_LOCALES(Locale, FALSE);
  396. //
  397. // Return the locale id.
  398. //
  399. return (Locale);
  400. }
  401. ////////////////////////////////////////////////////////////////////////////
  402. //
  403. // GetThreadLocale
  404. //
  405. // Returns the locale id for the current thread.
  406. //
  407. // 03-11-93 JulieB Moved from base\client.
  408. ////////////////////////////////////////////////////////////////////////////
  409. LCID WINAPI GetThreadLocale()
  410. {
  411. //
  412. // Return the locale id stored in the TEB.
  413. //
  414. return ((LCID)(NtCurrentTeb()->CurrentLocale));
  415. }
  416. ////////////////////////////////////////////////////////////////////////////
  417. //
  418. // SetThreadLocale
  419. //
  420. // Resets the locale id for the current thread. Any locale-dependent
  421. // functions will reflect the new locale. If the locale passed in is
  422. // not a valid locale id, then FALSE is returned.
  423. //
  424. // 03-11-93 JulieB Moved from base\client; Added Locale Validation.
  425. ////////////////////////////////////////////////////////////////////////////
  426. BOOL WINAPI SetThreadLocale(
  427. LCID Locale)
  428. {
  429. PLOC_HASH pHashN; // ptr to hash node
  430. //
  431. // Validate locale id.
  432. //
  433. VALIDATE_LANGUAGE(Locale, pHashN, 0, FALSE);
  434. if (pHashN == NULL)
  435. {
  436. SetLastError(ERROR_INVALID_PARAMETER);
  437. return (FALSE);
  438. }
  439. //
  440. // Set the locale id in the TEB.
  441. //
  442. NtCurrentTeb()->CurrentLocale = (ULONG)Locale;
  443. //
  444. // Return success.
  445. //
  446. return (TRUE);
  447. }
  448. ////////////////////////////////////////////////////////////////////////////
  449. //
  450. // SetThreadUILanguage
  451. //
  452. // This routine sets the thread UI language based on the console codepage.
  453. //
  454. // 9-29-00 WeiWu Created.
  455. ////////////////////////////////////////////////////////////////////////////
  456. LANGID WINAPI SetThreadUILanguage(
  457. WORD wReserved)
  458. {
  459. //
  460. // Cache system locale and CP info
  461. //
  462. static LCID s_lidSystem = 0;
  463. static UINT s_uiSysCp = 0;
  464. static UINT s_uiSysOEMCp = 0;
  465. UINT uiUserUICp;
  466. UINT uiUserUIOEMCp;
  467. WCHAR szData[16];
  468. LANGID lidUserUI = GetUserDefaultUILanguage();
  469. LCID lcidThreadOld = GetThreadLocale();
  470. //
  471. // Set default thread locale to EN-US
  472. //
  473. // This allow us to fall back to English UI to avoid trashed characters
  474. // when console doesn't meet the criteria of rendering native UI.
  475. //
  476. LCID lcidThread = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
  477. UINT uiConsoleCp = GetConsoleOutputCP();
  478. //
  479. // Make sure nobody uses it yet
  480. //
  481. ASSERT(wReserved == 0);
  482. //
  483. // Get cached system locale and CP info.
  484. //
  485. if (!s_uiSysCp)
  486. {
  487. LCID lcidSystem = GetSystemDefaultLCID();
  488. if (lcidSystem)
  489. {
  490. //
  491. // Get ANSI CP
  492. //
  493. GetLocaleInfoW(lcidSystem, LOCALE_IDEFAULTANSICODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
  494. NlsConvertStringToIntegerW(szData, 10, -1, &s_uiSysCp);
  495. //
  496. // Get OEM CP
  497. //
  498. GetLocaleInfoW(lcidSystem, LOCALE_IDEFAULTCODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
  499. NlsConvertStringToIntegerW(szData, 10, -1, &s_uiSysOEMCp);
  500. //
  501. // Cache system primary langauge
  502. //
  503. s_lidSystem = PRIMARYLANGID(LANGIDFROMLCID(lcidSystem));
  504. }
  505. }
  506. //
  507. // Don't cache user UI language and CP info, UI language can be changed without system reboot.
  508. //
  509. if (lidUserUI)
  510. {
  511. GetLocaleInfoW(MAKELCID(lidUserUI,SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
  512. NlsConvertStringToIntegerW(szData, 10, -1, &uiUserUICp);
  513. GetLocaleInfoW(MAKELCID(lidUserUI,SORT_DEFAULT), LOCALE_IDEFAULTCODEPAGE, szData, sizeof(szData)/sizeof(WCHAR));
  514. NlsConvertStringToIntegerW(szData, 10, -1, &uiUserUIOEMCp);
  515. }
  516. //
  517. // Complex scripts cannot be rendered in the console, so we
  518. // force the English (US) resource.
  519. //
  520. if (uiConsoleCp)
  521. {
  522. if (s_lidSystem != LANG_ARABIC &&
  523. s_lidSystem != LANG_HEBREW &&
  524. s_lidSystem != LANG_VIETNAMESE &&
  525. s_lidSystem != LANG_THAI)
  526. {
  527. //
  528. // Use UI language for console only when console CP, system CP and UI language CP match.
  529. //
  530. if ((uiConsoleCp == s_uiSysCp || uiConsoleCp == s_uiSysOEMCp) &&
  531. (uiConsoleCp == uiUserUICp || uiConsoleCp == uiUserUIOEMCp))
  532. {
  533. lcidThread = MAKELCID(lidUserUI, SORT_DEFAULT);
  534. }
  535. }
  536. }
  537. else
  538. {
  539. //
  540. // No console window, keep the original thread locale
  541. //
  542. lcidThread = lcidThreadOld;
  543. }
  544. //
  545. // Set the thread locale if it's different from the currently set
  546. // thread locale.
  547. //
  548. if ((lcidThread != lcidThreadOld) && (!SetThreadLocale(lcidThread)))
  549. {
  550. lcidThread = lcidThreadOld;
  551. }
  552. //
  553. // Return the thread locale that was set.
  554. //
  555. return (LANGIDFROMLCID(lcidThread));
  556. }
  557. ////////////////////////////////////////////////////////////////////////////
  558. //
  559. // GetSystemDefaultUILanguage
  560. //
  561. // Returns the language of the original install.
  562. //
  563. // 03-10-98 JulieB Created.
  564. ////////////////////////////////////////////////////////////////////////////
  565. LANGID WINAPI GetSystemDefaultUILanguage()
  566. {
  567. //
  568. // Get the original install language and return it.
  569. //
  570. if (gSystemInstallLang == 0)
  571. {
  572. if (NtQueryInstallUILanguage(&gSystemInstallLang) != STATUS_SUCCESS)
  573. {
  574. gSystemInstallLang = 0;
  575. return (NLS_DEFAULT_UILANG);
  576. }
  577. }
  578. return (gSystemInstallLang);
  579. }
  580. ////////////////////////////////////////////////////////////////////////////
  581. //
  582. // GetUserDefaultUILanguage
  583. //
  584. // Returns the current User's UI language selection. If the UI language
  585. // is not available, then the chosen default UI language is used
  586. // (NLS_DEFAULT_UILANG).
  587. //
  588. // 03-10-98 JulieB Created.
  589. ////////////////////////////////////////////////////////////////////////////
  590. LANGID WINAPI GetUserDefaultUILanguage()
  591. {
  592. LANGID DefaultUILang;
  593. LANGID SystemUILang;
  594. //
  595. // Note that the default UI language is coming from HKCU. However,
  596. // in the roaming profile situation, the default UI language for the
  597. // user may be not installed in the roamming machine, therefore we will
  598. // need to check if the DefaultUILang is a valid UI language installed
  599. // in the machine (the check is based in HKLM).
  600. //
  601. if (NtQueryDefaultUILanguage(&DefaultUILang) != STATUS_SUCCESS)
  602. {
  603. if ((SystemUILang = GetSystemDefaultUILanguage()) == 0)
  604. {
  605. return (NLS_DEFAULT_UILANG);
  606. }
  607. return (SystemUILang);
  608. }
  609. return (DefaultUILang);
  610. }
  611. ////////////////////////////////////////////////////////////////////////////
  612. //
  613. // GetSystemDefaultLangID
  614. //
  615. // Returns the default language for the system. If the registry value is
  616. // not readable, then the chosen default language is used
  617. // (NLS_DEFAULT_LANGID).
  618. //
  619. // 05-31-91 JulieB Created.
  620. ////////////////////////////////////////////////////////////////////////////
  621. LANGID WINAPI GetSystemDefaultLangID()
  622. {
  623. //
  624. // Get the language id from the locale id stored in the cache
  625. // and return it.
  626. //
  627. return (LANGIDFROMLCID(gSystemLocale));
  628. }
  629. ////////////////////////////////////////////////////////////////////////////
  630. //
  631. // GetUserDefaultLangID
  632. //
  633. // Returns the default language for the current user. If the current user's
  634. // language is not set, then the system default language id is returned.
  635. //
  636. // 05-31-91 JulieB Created.
  637. ////////////////////////////////////////////////////////////////////////////
  638. LANGID WINAPI GetUserDefaultLangID()
  639. {
  640. //
  641. // Get the language id from the locale id stored in the cache
  642. // and return it.
  643. //
  644. return (LANGIDFROMLCID(GetUserDefaultLCID()));
  645. }
  646. ////////////////////////////////////////////////////////////////////////////
  647. //
  648. // GetSystemDefaultLCID
  649. //
  650. // Returns the default locale for the system.
  651. //
  652. // 05-31-91 JulieB Created.
  653. ////////////////////////////////////////////////////////////////////////////
  654. LCID WINAPI GetSystemDefaultLCID()
  655. {
  656. //
  657. // Return the locale id stored in the cache.
  658. //
  659. return (gSystemLocale);
  660. }
  661. ////////////////////////////////////////////////////////////////////////////
  662. //
  663. // GetUserDefaultLCID
  664. //
  665. // Returns the default locale for the current user. If current user's locale
  666. // is not set, then the system default locale id is returned.
  667. //
  668. // 05-31-91 JulieB Created.
  669. ////////////////////////////////////////////////////////////////////////////
  670. LCID WINAPI GetUserDefaultLCID()
  671. {
  672. LCID Lcid = NtCurrentTeb()->ImpersonationLocale;
  673. switch (Lcid)
  674. {
  675. case ( -1 ) :
  676. {
  677. //
  678. // Thread is being impersonated.
  679. //
  680. if (NT_SUCCESS( NlsGetUserLocale(&Lcid) ))
  681. {
  682. NtCurrentTeb()->ImpersonationLocale = Lcid;
  683. }
  684. else
  685. {
  686. //
  687. // If we can't get it from the registry, then let's use the
  688. // system locale since it won't be resolved by calling
  689. // GetUserDefaultLCID() again.
  690. //
  691. Lcid = NtCurrentTeb()->ImpersonationLocale = gSystemLocale;
  692. }
  693. break;
  694. }
  695. case ( 0 ) :
  696. {
  697. //
  698. // Thread hasn't been impersonated.
  699. // If we are running in the interactive logged on user, then
  700. // use the one cached in CSRSS if the cache is valid. Otherwise,
  701. // use the process cached locale.
  702. //
  703. if (gInteractiveLogonUserProcess == (BOOL) -1)
  704. {
  705. NlsIsInteractiveUserProcess();
  706. }
  707. if ((gInteractiveLogonUserProcess == FALSE) ||
  708. ((Lcid = pNlsUserInfo->UserLocaleId) == 0))
  709. {
  710. if (!gProcessLocale)
  711. {
  712. if (!NT_SUCCESS (NlsGetUserLocale(&gProcessLocale)) )
  713. {
  714. gProcessLocale = gSystemLocale;
  715. }
  716. }
  717. Lcid = gProcessLocale;
  718. }
  719. break;
  720. }
  721. }
  722. return (Lcid);
  723. }
  724. ////////////////////////////////////////////////////////////////////////////
  725. //
  726. // VerLanguageNameW
  727. //
  728. // Returns the language name of the given language id in the language of
  729. // the current user.
  730. //
  731. // 05-31-91 JulieB Moved and Rewrote from Version Library.
  732. ////////////////////////////////////////////////////////////////////////////
  733. DWORD WINAPI VerLanguageNameW(
  734. DWORD wLang,
  735. LPWSTR szLang,
  736. DWORD wSize)
  737. {
  738. DWORD Length = 0; // length of string
  739. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  740. //
  741. // Make sure we have a buffer.
  742. //
  743. if ((wSize == 0) || (szLang == NULL))
  744. {
  745. return (0);
  746. }
  747. //
  748. // Try to get the localized language name for the given ID.
  749. //
  750. pTemp[0] = 0;
  751. if (!(Length = GetStringTableEntry( wLang,
  752. 0,
  753. pTemp,
  754. MAX_REG_VAL_SIZE,
  755. RC_LANGUAGE_NAME )))
  756. {
  757. //
  758. // Can't get the name of the language id passed in, so get
  759. // the "language neutral" name.
  760. //
  761. Length = GetStringTableEntry( LANG_NEUTRAL,
  762. 0,
  763. pTemp,
  764. MAX_REG_VAL_SIZE,
  765. RC_LANGUAGE_NAME );
  766. }
  767. //
  768. // If the length is too big for the buffer, then reset the length
  769. // to the size of the given buffer.
  770. //
  771. if (Length >= wSize)
  772. {
  773. Length = wSize - 1;
  774. }
  775. //
  776. // Copy the string to the buffer and zero terminate it.
  777. //
  778. if (Length > 0)
  779. {
  780. wcsncpy(szLang, pTemp, Length);
  781. szLang[Length] = 0;
  782. }
  783. //
  784. // Return the number of characters in the string, NOT including
  785. // the null termination.
  786. //
  787. return (Length);
  788. }
  789. ////////////////////////////////////////////////////////////////////////////
  790. //
  791. // VerLanguageNameA
  792. //
  793. // Returns the language name of the given language id in the language of
  794. // the current user.
  795. //
  796. // 05-31-91 JulieB Moved from Version Library.
  797. ////////////////////////////////////////////////////////////////////////////
  798. DWORD WINAPI VerLanguageNameA(
  799. DWORD wLang,
  800. LPSTR szLang,
  801. DWORD wSize)
  802. {
  803. UNICODE_STRING Language; // unicode string buffer
  804. ANSI_STRING AnsiString; // ansi string buffer
  805. DWORD Status; // return status
  806. //
  807. // Make sure we have a buffer.
  808. //
  809. if ((wSize == 0) || (szLang == NULL))
  810. {
  811. return (0);
  812. }
  813. //
  814. // Allocate Unicode string structure and set the fields with the
  815. // given parameters.
  816. //
  817. Language.Buffer = RtlAllocateHeap( RtlProcessHeap(),
  818. 0,
  819. sizeof(WCHAR) * wSize );
  820. Language.MaximumLength = (USHORT)(wSize * sizeof(WCHAR));
  821. //
  822. // Make sure the allocation succeeded.
  823. //
  824. if (Language.Buffer == NULL)
  825. {
  826. return (FALSE);
  827. }
  828. //
  829. // Get the language name (in Unicode).
  830. //
  831. Status = VerLanguageNameW( wLang,
  832. Language.Buffer,
  833. wSize );
  834. Language.Length = (USHORT)(Status * sizeof(WCHAR));
  835. //
  836. // Convert unicode string to ansi.
  837. //
  838. AnsiString.Buffer = szLang;
  839. AnsiString.Length = AnsiString.MaximumLength = (USHORT)wSize;
  840. RtlUnicodeStringToAnsiString(&AnsiString, &Language, FALSE);
  841. Status = AnsiString.Length;
  842. RtlFreeUnicodeString(&Language);
  843. //
  844. // Return the value returned from VerLanguageNameW.
  845. //
  846. return (Status);
  847. }
  848. ////////////////////////////////////////////////////////////////////////////
  849. //
  850. // GetLocaleInfoW
  851. //
  852. // Returns one of the various pieces of information about a particular
  853. // locale by querying the configuration registry. This call also indicates
  854. // how much memory is necessary to contain the desired information.
  855. //
  856. // 05-31-91 JulieB Created.
  857. ////////////////////////////////////////////////////////////////////////////
  858. int WINAPI GetLocaleInfoW(
  859. LCID Locale,
  860. LCTYPE LCType,
  861. LPWSTR lpLCData,
  862. int cchData)
  863. {
  864. PLOC_HASH pHashN; // ptr to LOC hash node
  865. int Length = 0; // length of info string
  866. LPWSTR pString; // ptr to the info string
  867. LPWORD pStart; // ptr to starting point
  868. BOOL UserOverride = TRUE; // use user override
  869. BOOL ReturnNum = FALSE; // return number instead of string
  870. LPWSTR pTmp; // tmp ptr to info string
  871. int Repeat; // # repetitions of same letter
  872. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  873. UNICODE_STRING ObUnicodeStr; // value string
  874. int Base = 0; // base for str to int conversion
  875. static LANGID lidSystem = 0; // system default UI language
  876. //
  877. // Invalid Parameter Check:
  878. // - validate LCID
  879. // - count is negative
  880. // - NULL data pointer AND count is not zero
  881. //
  882. // NOTE: invalid type is checked in the switch statement below.
  883. //
  884. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  885. if ( (pHashN == NULL) ||
  886. (cchData < 0) ||
  887. ((lpLCData == NULL) && (cchData != 0)) )
  888. {
  889. SetLastError(ERROR_INVALID_PARAMETER);
  890. return (0);
  891. }
  892. //
  893. // Set the base value to add to in order to get the variable
  894. // length strings.
  895. //
  896. pStart = (LPWORD)(pHashN->pLocaleHdr);
  897. //
  898. // Check for NO USER OVERRIDE flag and remove the USE CP ACP flag.
  899. //
  900. if (LCType & LOCALE_NOUSEROVERRIDE)
  901. {
  902. //
  903. // Flag is set, so set the boolean value and remove the flag
  904. // from the LCType parameter (for switch statement).
  905. //
  906. UserOverride = FALSE;
  907. }
  908. if (LCType & LOCALE_RETURN_NUMBER)
  909. {
  910. //
  911. // Flag is set, so set the boolean value and remove the flag
  912. // from the LCType parameter (for switch statement).
  913. //
  914. ReturnNum = TRUE;
  915. }
  916. LCType = NLS_GET_LCTYPE_VALUE(LCType);
  917. //
  918. // Initialize temp buffer.
  919. //
  920. pTemp[0] = 0;
  921. //
  922. // Return the appropriate information for the given LCTYPE.
  923. // If user information exists for the given LCTYPE, then
  924. // the user default is returned instead of the system default.
  925. //
  926. switch (LCType)
  927. {
  928. case ( LOCALE_ILANGUAGE ) :
  929. {
  930. Base = 16;
  931. pString = pHashN->pLocaleFixed->szILanguage;
  932. break;
  933. }
  934. case ( LOCALE_SLANGUAGE ) :
  935. {
  936. if (!lidSystem)
  937. {
  938. lidSystem = GetSystemDefaultUILanguage();
  939. }
  940. //
  941. // Get the information from the RC file.
  942. //
  943. // Use system installed language resource if we're not under MUI.
  944. // Otherwise, let resource loader load the default language resource.
  945. //
  946. Length = GetStringTableEntry( LANGIDFROMLCID(Locale),
  947. GetUserDefaultUILanguage() == lidSystem? lidSystem : 0,
  948. pTemp,
  949. MAX_REG_VAL_SIZE,
  950. RC_LANGUAGE_NAME );
  951. if (Length == 0)
  952. {
  953. SetLastError(ERROR_INVALID_PARAMETER);
  954. return (0);
  955. }
  956. pString = pTemp;
  957. break;
  958. }
  959. case ( LOCALE_SENGLANGUAGE ) :
  960. {
  961. pString = pStart + pHashN->pLocaleHdr->SEngLanguage;
  962. break;
  963. }
  964. case ( LOCALE_SABBREVLANGNAME ) :
  965. {
  966. if (UserOverride &&
  967. GetUserInfo( Locale,
  968. LCType,
  969. FIELD_OFFSET(NLS_USER_INFO, sAbbrevLangName),
  970. NLS_VALUE_SLANGUAGE,
  971. pTemp,
  972. ARRAYSIZE(pTemp),
  973. TRUE ))
  974. {
  975. pString = pTemp;
  976. }
  977. else
  978. {
  979. pString = pStart + pHashN->pLocaleHdr->SAbbrevLang;
  980. }
  981. break;
  982. }
  983. case ( LOCALE_SISO639LANGNAME ) :
  984. {
  985. pString = pStart + pHashN->pLocaleHdr->SAbbrevLangISO;
  986. break;
  987. }
  988. case ( LOCALE_SNATIVELANGNAME ) :
  989. {
  990. pString = pStart + pHashN->pLocaleHdr->SNativeLang;
  991. break;
  992. }
  993. case ( LOCALE_ICOUNTRY ) :
  994. {
  995. Base = 10;
  996. if (UserOverride &&
  997. GetUserInfo( Locale,
  998. LCType,
  999. FIELD_OFFSET(NLS_USER_INFO, iCountry),
  1000. NLS_VALUE_ICOUNTRY,
  1001. pTemp,
  1002. ARRAYSIZE(pTemp),
  1003. TRUE ))
  1004. {
  1005. pString = pTemp;
  1006. }
  1007. else
  1008. {
  1009. pString = pHashN->pLocaleFixed->szICountry;
  1010. }
  1011. break;
  1012. }
  1013. case ( LOCALE_SCOUNTRY ) :
  1014. {
  1015. if (UserOverride &&
  1016. GetUserInfo( Locale,
  1017. LCType,
  1018. FIELD_OFFSET(NLS_USER_INFO, sCountry),
  1019. NLS_VALUE_SCOUNTRY,
  1020. pTemp,
  1021. ARRAYSIZE(pTemp),
  1022. TRUE ))
  1023. {
  1024. pString = pTemp;
  1025. }
  1026. else
  1027. {
  1028. //
  1029. // Get the information from the RC file.
  1030. //
  1031. Length = GetStringTableEntry( LANGIDFROMLCID(Locale),
  1032. 0,
  1033. pTemp,
  1034. MAX_REG_VAL_SIZE,
  1035. RC_COUNTRY_NAME );
  1036. if (Length == 0)
  1037. {
  1038. SetLastError(ERROR_INVALID_PARAMETER);
  1039. return (0);
  1040. }
  1041. pString = pTemp;
  1042. break;
  1043. }
  1044. break;
  1045. }
  1046. case ( LOCALE_SENGCOUNTRY ) :
  1047. {
  1048. pString = pStart + pHashN->pLocaleHdr->SEngCountry;
  1049. break;
  1050. }
  1051. case ( LOCALE_SABBREVCTRYNAME ) :
  1052. {
  1053. pString = pStart + pHashN->pLocaleHdr->SAbbrevCtry;
  1054. break;
  1055. }
  1056. case ( LOCALE_SISO3166CTRYNAME ) :
  1057. {
  1058. pString = pStart + pHashN->pLocaleHdr->SAbbrevCtryISO;
  1059. break;
  1060. }
  1061. case ( LOCALE_SNATIVECTRYNAME ) :
  1062. {
  1063. pString = pStart + pHashN->pLocaleHdr->SNativeCtry;
  1064. break;
  1065. }
  1066. case ( LOCALE_IGEOID ) :
  1067. {
  1068. Base = 10;
  1069. pString = pHashN->pLocaleFixed->szIGeoID;
  1070. break;
  1071. }
  1072. case ( LOCALE_SSORTNAME ) :
  1073. {
  1074. //
  1075. // Get the information from the RC file.
  1076. //
  1077. Length = GetStringTableEntry( LANGIDFROMLCID(Locale),
  1078. 0,
  1079. pTemp,
  1080. MAX_REG_VAL_SIZE,
  1081. RC_SORT_NAMES + SORTIDFROMLCID(Locale) );
  1082. if (Length == 0)
  1083. {
  1084. //
  1085. // If the sort name doesn't exist for the given locale id,
  1086. // then try to get the Default name. This is stored in the
  1087. // 0x0000 entry.
  1088. //
  1089. Length = GetStringTableEntry( 0x0000,
  1090. 0,
  1091. pTemp,
  1092. MAX_REG_VAL_SIZE,
  1093. RC_SORT_NAMES + SORTIDFROMLCID(Locale) );
  1094. if (Length == 0)
  1095. {
  1096. SetLastError(ERROR_INVALID_PARAMETER);
  1097. return (0);
  1098. }
  1099. }
  1100. pString = pTemp;
  1101. break;
  1102. }
  1103. case ( LOCALE_IDEFAULTLANGUAGE ) :
  1104. {
  1105. Base = 16;
  1106. pString = pHashN->pLocaleFixed->szIDefaultLang;
  1107. break;
  1108. }
  1109. case ( LOCALE_IDEFAULTCOUNTRY ) :
  1110. {
  1111. Base = 10;
  1112. pString = pHashN->pLocaleFixed->szIDefaultCtry;
  1113. break;
  1114. }
  1115. case ( LOCALE_IDEFAULTANSICODEPAGE ) :
  1116. {
  1117. if (ReturnNum)
  1118. {
  1119. if (cchData < 2)
  1120. {
  1121. if (cchData == 0)
  1122. {
  1123. //
  1124. // DWORD is needed for this option (2 WORDS),
  1125. // so return 2.
  1126. //
  1127. return (2);
  1128. }
  1129. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  1130. return (0);
  1131. }
  1132. //
  1133. // Copy the value to lpLCData and return 2
  1134. // (2 WORDS = 1 DWORD).
  1135. //
  1136. *((LPDWORD)lpLCData) = (DWORD)(pHashN->pLocaleFixed->DefaultACP);
  1137. return (2);
  1138. }
  1139. pString = pHashN->pLocaleFixed->szIDefaultACP;
  1140. break;
  1141. }
  1142. case ( LOCALE_IDEFAULTCODEPAGE ) :
  1143. {
  1144. Base = 10;
  1145. pString = pHashN->pLocaleFixed->szIDefaultOCP;
  1146. break;
  1147. }
  1148. case ( LOCALE_IDEFAULTMACCODEPAGE ) :
  1149. {
  1150. Base = 10;
  1151. pString = pHashN->pLocaleFixed->szIDefaultMACCP;
  1152. break;
  1153. }
  1154. case ( LOCALE_IDEFAULTEBCDICCODEPAGE ) :
  1155. {
  1156. Base = 10;
  1157. pString = pHashN->pLocaleFixed->szIDefaultEBCDICCP;
  1158. break;
  1159. }
  1160. case ( LOCALE_SLIST ) :
  1161. {
  1162. if (UserOverride &&
  1163. GetUserInfo( Locale,
  1164. LCType,
  1165. FIELD_OFFSET(NLS_USER_INFO, sList),
  1166. NLS_VALUE_SLIST,
  1167. pTemp,
  1168. ARRAYSIZE(pTemp),
  1169. FALSE ))
  1170. {
  1171. pString = pTemp;
  1172. }
  1173. else
  1174. {
  1175. pString = pStart + pHashN->pLocaleHdr->SList;
  1176. }
  1177. break;
  1178. }
  1179. case ( LOCALE_IMEASURE ) :
  1180. {
  1181. Base = 10;
  1182. if (UserOverride &&
  1183. GetUserInfo( Locale,
  1184. LCType,
  1185. FIELD_OFFSET(NLS_USER_INFO, iMeasure),
  1186. NLS_VALUE_IMEASURE,
  1187. pTemp,
  1188. ARRAYSIZE(pTemp),
  1189. TRUE ))
  1190. {
  1191. pString = pTemp;
  1192. }
  1193. else
  1194. {
  1195. pString = pHashN->pLocaleFixed->szIMeasure;
  1196. }
  1197. break;
  1198. }
  1199. case ( LOCALE_IPAPERSIZE ) :
  1200. {
  1201. Base = 10;
  1202. if (UserOverride &&
  1203. GetUserInfo( Locale,
  1204. LCType,
  1205. FIELD_OFFSET(NLS_USER_INFO, iPaperSize),
  1206. NLS_VALUE_IPAPERSIZE,
  1207. pTemp,
  1208. ARRAYSIZE(pTemp),
  1209. TRUE ))
  1210. {
  1211. pString = pTemp;
  1212. }
  1213. else
  1214. {
  1215. pString = pHashN->pLocaleFixed->szIPaperSize;
  1216. }
  1217. break;
  1218. }
  1219. case ( LOCALE_SDECIMAL ) :
  1220. {
  1221. if (UserOverride &&
  1222. GetUserInfo( Locale,
  1223. LCType,
  1224. FIELD_OFFSET(NLS_USER_INFO, sDecimal),
  1225. NLS_VALUE_SDECIMAL,
  1226. pTemp,
  1227. ARRAYSIZE(pTemp),
  1228. FALSE ))
  1229. {
  1230. pString = pTemp;
  1231. }
  1232. else
  1233. {
  1234. pString = pStart + pHashN->pLocaleHdr->SDecimal;
  1235. }
  1236. break;
  1237. }
  1238. case ( LOCALE_STHOUSAND ) :
  1239. {
  1240. if (UserOverride &&
  1241. GetUserInfo( Locale,
  1242. LCType,
  1243. FIELD_OFFSET(NLS_USER_INFO, sThousand),
  1244. NLS_VALUE_STHOUSAND,
  1245. pTemp,
  1246. ARRAYSIZE(pTemp),
  1247. FALSE ))
  1248. {
  1249. pString = pTemp;
  1250. }
  1251. else
  1252. {
  1253. pString = pStart + pHashN->pLocaleHdr->SThousand;
  1254. }
  1255. break;
  1256. }
  1257. case ( LOCALE_SGROUPING ) :
  1258. {
  1259. if (UserOverride &&
  1260. GetUserInfo( Locale,
  1261. LCType,
  1262. FIELD_OFFSET(NLS_USER_INFO, sGrouping),
  1263. NLS_VALUE_SGROUPING,
  1264. pTemp,
  1265. ARRAYSIZE(pTemp),
  1266. TRUE ))
  1267. {
  1268. pString = pTemp;
  1269. }
  1270. else
  1271. {
  1272. pString = pStart + pHashN->pLocaleHdr->SGrouping;
  1273. }
  1274. break;
  1275. }
  1276. case ( LOCALE_IDIGITS ) :
  1277. {
  1278. Base = 10;
  1279. if (UserOverride &&
  1280. GetUserInfo( Locale,
  1281. LCType,
  1282. FIELD_OFFSET(NLS_USER_INFO, iDigits),
  1283. NLS_VALUE_IDIGITS,
  1284. pTemp,
  1285. ARRAYSIZE(pTemp),
  1286. TRUE ))
  1287. {
  1288. pString = pTemp;
  1289. }
  1290. else
  1291. {
  1292. pString = pHashN->pLocaleFixed->szIDigits;
  1293. }
  1294. break;
  1295. }
  1296. case ( LOCALE_ILZERO ) :
  1297. {
  1298. Base = 10;
  1299. if (UserOverride &&
  1300. GetUserInfo( Locale,
  1301. LCType,
  1302. FIELD_OFFSET(NLS_USER_INFO, iLZero),
  1303. NLS_VALUE_ILZERO,
  1304. pTemp,
  1305. ARRAYSIZE(pTemp),
  1306. TRUE ))
  1307. {
  1308. pString = pTemp;
  1309. }
  1310. else
  1311. {
  1312. pString = pHashN->pLocaleFixed->szILZero;
  1313. }
  1314. break;
  1315. }
  1316. case ( LOCALE_INEGNUMBER ) :
  1317. {
  1318. Base = 10;
  1319. if (UserOverride &&
  1320. GetUserInfo( Locale,
  1321. LCType,
  1322. FIELD_OFFSET(NLS_USER_INFO, iNegNumber),
  1323. NLS_VALUE_INEGNUMBER,
  1324. pTemp,
  1325. ARRAYSIZE(pTemp),
  1326. TRUE ))
  1327. {
  1328. pString = pTemp;
  1329. }
  1330. else
  1331. {
  1332. pString = pHashN->pLocaleFixed->szINegNumber;
  1333. }
  1334. break;
  1335. }
  1336. case ( LOCALE_SNATIVEDIGITS ) :
  1337. {
  1338. if (UserOverride &&
  1339. GetUserInfo( Locale,
  1340. LCType,
  1341. FIELD_OFFSET(NLS_USER_INFO, sNativeDigits),
  1342. NLS_VALUE_SNATIVEDIGITS,
  1343. pTemp,
  1344. ARRAYSIZE(pTemp),
  1345. FALSE ))
  1346. {
  1347. pString = pTemp;
  1348. }
  1349. else
  1350. {
  1351. pString = pStart + pHashN->pLocaleHdr->SNativeDigits;
  1352. }
  1353. break;
  1354. }
  1355. case ( LOCALE_IDIGITSUBSTITUTION ) :
  1356. {
  1357. Base = 10;
  1358. if (UserOverride &&
  1359. GetUserInfo( Locale,
  1360. LCType,
  1361. FIELD_OFFSET(NLS_USER_INFO, iDigitSubstitution),
  1362. NLS_VALUE_IDIGITSUBST,
  1363. pTemp,
  1364. ARRAYSIZE(pTemp),
  1365. TRUE ))
  1366. {
  1367. pString = pTemp;
  1368. }
  1369. else
  1370. {
  1371. pString = pHashN->pLocaleFixed->szIDigitSubstitution;
  1372. }
  1373. break;
  1374. }
  1375. case ( LOCALE_SCURRENCY ) :
  1376. {
  1377. if (UserOverride &&
  1378. GetUserInfo( Locale,
  1379. LCType,
  1380. FIELD_OFFSET(NLS_USER_INFO, sCurrency),
  1381. NLS_VALUE_SCURRENCY,
  1382. pTemp,
  1383. ARRAYSIZE(pTemp),
  1384. FALSE ))
  1385. {
  1386. pString = pTemp;
  1387. }
  1388. else
  1389. {
  1390. pString = pStart + pHashN->pLocaleHdr->SCurrency;
  1391. }
  1392. break;
  1393. }
  1394. case ( LOCALE_SINTLSYMBOL ) :
  1395. {
  1396. pString = pStart + pHashN->pLocaleHdr->SIntlSymbol;
  1397. break;
  1398. }
  1399. case ( LOCALE_SENGCURRNAME ) :
  1400. {
  1401. pString = pStart + pHashN->pLocaleHdr->SEngCurrName;
  1402. break;
  1403. }
  1404. case ( LOCALE_SNATIVECURRNAME ) :
  1405. {
  1406. pString = pStart + pHashN->pLocaleHdr->SNativeCurrName;
  1407. break;
  1408. }
  1409. case ( LOCALE_SMONDECIMALSEP ) :
  1410. {
  1411. if (UserOverride &&
  1412. GetUserInfo( Locale,
  1413. LCType,
  1414. FIELD_OFFSET(NLS_USER_INFO, sMonDecSep),
  1415. NLS_VALUE_SMONDECIMALSEP,
  1416. pTemp,
  1417. ARRAYSIZE(pTemp),
  1418. FALSE ))
  1419. {
  1420. pString = pTemp;
  1421. }
  1422. else
  1423. {
  1424. pString = pStart + pHashN->pLocaleHdr->SMonDecSep;
  1425. }
  1426. break;
  1427. }
  1428. case ( LOCALE_SMONTHOUSANDSEP ) :
  1429. {
  1430. if (UserOverride &&
  1431. GetUserInfo( Locale,
  1432. LCType,
  1433. FIELD_OFFSET(NLS_USER_INFO, sMonThouSep),
  1434. NLS_VALUE_SMONTHOUSANDSEP,
  1435. pTemp,
  1436. ARRAYSIZE(pTemp),
  1437. FALSE ))
  1438. {
  1439. pString = pTemp;
  1440. }
  1441. else
  1442. {
  1443. pString = pStart + pHashN->pLocaleHdr->SMonThousSep;
  1444. }
  1445. break;
  1446. }
  1447. case ( LOCALE_SMONGROUPING ) :
  1448. {
  1449. if (UserOverride &&
  1450. GetUserInfo( Locale,
  1451. LCType,
  1452. FIELD_OFFSET(NLS_USER_INFO, sMonGrouping),
  1453. NLS_VALUE_SMONGROUPING,
  1454. pTemp,
  1455. ARRAYSIZE(pTemp),
  1456. TRUE ))
  1457. {
  1458. pString = pTemp;
  1459. }
  1460. else
  1461. {
  1462. pString = pStart + pHashN->pLocaleHdr->SMonGrouping;
  1463. }
  1464. break;
  1465. }
  1466. case ( LOCALE_ICURRDIGITS ) :
  1467. {
  1468. Base = 10;
  1469. if (UserOverride &&
  1470. GetUserInfo( Locale,
  1471. LCType,
  1472. FIELD_OFFSET(NLS_USER_INFO, iCurrDigits),
  1473. NLS_VALUE_ICURRDIGITS,
  1474. pTemp,
  1475. ARRAYSIZE(pTemp),
  1476. TRUE ))
  1477. {
  1478. pString = pTemp;
  1479. }
  1480. else
  1481. {
  1482. pString = pHashN->pLocaleFixed->szICurrDigits;
  1483. }
  1484. break;
  1485. }
  1486. case ( LOCALE_IINTLCURRDIGITS ) :
  1487. {
  1488. Base = 10;
  1489. pString = pHashN->pLocaleFixed->szIIntlCurrDigits;
  1490. break;
  1491. }
  1492. case ( LOCALE_ICURRENCY ) :
  1493. {
  1494. Base = 10;
  1495. if (UserOverride &&
  1496. GetUserInfo( Locale,
  1497. LCType,
  1498. FIELD_OFFSET(NLS_USER_INFO, iCurrency),
  1499. NLS_VALUE_ICURRENCY,
  1500. pTemp,
  1501. ARRAYSIZE(pTemp),
  1502. TRUE ))
  1503. {
  1504. pString = pTemp;
  1505. }
  1506. else
  1507. {
  1508. pString = pHashN->pLocaleFixed->szICurrency;
  1509. }
  1510. break;
  1511. }
  1512. case ( LOCALE_INEGCURR ) :
  1513. {
  1514. Base = 10;
  1515. if (UserOverride &&
  1516. GetUserInfo( Locale,
  1517. LCType,
  1518. FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
  1519. NLS_VALUE_INEGCURR,
  1520. pTemp,
  1521. ARRAYSIZE(pTemp),
  1522. TRUE ))
  1523. {
  1524. pString = pTemp;
  1525. }
  1526. else
  1527. {
  1528. pString = pHashN->pLocaleFixed->szINegCurr;
  1529. }
  1530. break;
  1531. }
  1532. case ( LOCALE_SPOSITIVESIGN ) :
  1533. {
  1534. if (UserOverride &&
  1535. GetUserInfo( Locale,
  1536. LCType,
  1537. FIELD_OFFSET(NLS_USER_INFO, sPosSign),
  1538. NLS_VALUE_SPOSITIVESIGN,
  1539. pTemp,
  1540. ARRAYSIZE(pTemp),
  1541. FALSE ))
  1542. {
  1543. pString = pTemp;
  1544. }
  1545. else
  1546. {
  1547. pString = pStart + pHashN->pLocaleHdr->SPositiveSign;
  1548. }
  1549. break;
  1550. }
  1551. case ( LOCALE_SNEGATIVESIGN ) :
  1552. {
  1553. if (UserOverride &&
  1554. GetUserInfo( Locale,
  1555. LCType,
  1556. FIELD_OFFSET(NLS_USER_INFO, sNegSign),
  1557. NLS_VALUE_SNEGATIVESIGN,
  1558. pTemp,
  1559. ARRAYSIZE(pTemp),
  1560. FALSE ))
  1561. {
  1562. pString = pTemp;
  1563. }
  1564. else
  1565. {
  1566. pString = pStart + pHashN->pLocaleHdr->SNegativeSign;
  1567. }
  1568. break;
  1569. }
  1570. case ( LOCALE_IPOSSIGNPOSN ) :
  1571. {
  1572. //
  1573. // Since there is no positive sign in any of the ICURRENCY
  1574. // options, use the INEGCURR options instead. All known
  1575. // locales would use the positive sign in the same position
  1576. // as the negative sign.
  1577. //
  1578. // NOTE: For the 2 options that use parenthesis, put the
  1579. // positive sign at the beginning of the string
  1580. // (where the opening parenthesis is).
  1581. //
  1582. // 1 => 4, 5, 8, 15
  1583. // 2 => 3, 11
  1584. // 3 => 0, 1, 6, 9, 13, 14
  1585. // 4 => 2, 7, 10, 12
  1586. //
  1587. Base = 10;
  1588. if (UserOverride &&
  1589. GetUserInfo( Locale,
  1590. LCType,
  1591. FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
  1592. NLS_VALUE_INEGCURR,
  1593. pTemp,
  1594. ARRAYSIZE(pTemp),
  1595. TRUE ))
  1596. {
  1597. pString = pTemp;
  1598. //
  1599. // Set the appropriate value in pString.
  1600. //
  1601. switch (*pString)
  1602. {
  1603. case ( L'4' ) :
  1604. case ( L'5' ) :
  1605. case ( L'8' ) :
  1606. {
  1607. *pString = L'1';
  1608. *(pString + 1) = 0;
  1609. break;
  1610. }
  1611. case ( L'3' ) :
  1612. {
  1613. *pString = L'2';
  1614. *(pString + 1) = 0;
  1615. break;
  1616. }
  1617. case ( L'0' ) :
  1618. case ( L'6' ) :
  1619. case ( L'9' ) :
  1620. {
  1621. *pString = L'3';
  1622. *(pString + 1) = 0;
  1623. break;
  1624. }
  1625. case ( L'2' ) :
  1626. case ( L'7' ) :
  1627. {
  1628. *pString = L'4';
  1629. *(pString + 1) = 0;
  1630. break;
  1631. }
  1632. case ( L'1' ) :
  1633. {
  1634. switch (*(pString + 1))
  1635. {
  1636. case ( 0 ) :
  1637. case ( L'3' ) :
  1638. case ( L'4' ) :
  1639. default :
  1640. {
  1641. *pString = L'3';
  1642. *(pString + 1) = 0;
  1643. break;
  1644. }
  1645. case ( L'0' ) :
  1646. case ( L'2' ) :
  1647. {
  1648. *pString = L'4';
  1649. *(pString + 1) = 0;
  1650. break;
  1651. }
  1652. case ( L'1' ) :
  1653. {
  1654. *pString = L'2';
  1655. *(pString + 1) = 0;
  1656. break;
  1657. }
  1658. case ( L'5' ) :
  1659. {
  1660. *pString = L'1';
  1661. *(pString + 1) = 0;
  1662. break;
  1663. }
  1664. }
  1665. break;
  1666. }
  1667. default :
  1668. {
  1669. pString = pHashN->pLocaleFixed->szIPosSignPosn;
  1670. break;
  1671. }
  1672. }
  1673. }
  1674. else
  1675. {
  1676. pString = pHashN->pLocaleFixed->szIPosSignPosn;
  1677. }
  1678. break;
  1679. }
  1680. case ( LOCALE_INEGSIGNPOSN ) :
  1681. {
  1682. //
  1683. // Use the INEGCURR value from the user portion of the
  1684. // registry, if it exists.
  1685. //
  1686. // 0 => 0, 4, 14, 15
  1687. // 1 => 5, 8
  1688. // 2 => 3, 11
  1689. // 3 => 1, 6, 9, 13
  1690. // 4 => 2, 7, 10, 12
  1691. //
  1692. Base = 10;
  1693. if (UserOverride &&
  1694. GetUserInfo( Locale,
  1695. LCType,
  1696. FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
  1697. NLS_VALUE_INEGCURR,
  1698. pTemp,
  1699. ARRAYSIZE(pTemp),
  1700. TRUE ))
  1701. {
  1702. pString = pTemp;
  1703. //
  1704. // Set the appropriate value in pString.
  1705. //
  1706. switch (*pString)
  1707. {
  1708. case ( L'0' ) :
  1709. case ( L'4' ) :
  1710. {
  1711. *pString = L'0';
  1712. *(pString + 1) = 0;
  1713. break;
  1714. }
  1715. case ( L'5' ) :
  1716. case ( L'8' ) :
  1717. {
  1718. *pString = L'1';
  1719. *(pString + 1) = 0;
  1720. break;
  1721. }
  1722. case ( L'3' ) :
  1723. {
  1724. *pString = L'2';
  1725. *(pString + 1) = 0;
  1726. break;
  1727. }
  1728. case ( L'6' ) :
  1729. case ( L'9' ) :
  1730. {
  1731. *pString = L'3';
  1732. *(pString + 1) = 0;
  1733. break;
  1734. }
  1735. case ( L'2' ) :
  1736. case ( L'7' ) :
  1737. {
  1738. *pString = L'4';
  1739. *(pString + 1) = 0;
  1740. break;
  1741. }
  1742. case ( L'1' ) :
  1743. {
  1744. switch (*(pString + 1))
  1745. {
  1746. case ( 0 ) :
  1747. case ( L'3' ) :
  1748. default :
  1749. {
  1750. *pString = L'3';
  1751. *(pString + 1) = 0;
  1752. break;
  1753. }
  1754. case ( L'0' ) :
  1755. case ( L'2' ) :
  1756. {
  1757. *pString = L'4';
  1758. *(pString + 1) = 0;
  1759. break;
  1760. }
  1761. case ( L'1' ) :
  1762. {
  1763. *pString = L'2';
  1764. *(pString + 1) = 0;
  1765. break;
  1766. }
  1767. case ( L'4' ) :
  1768. case ( L'5' ) :
  1769. {
  1770. *pString = L'0';
  1771. *(pString + 1) = 0;
  1772. break;
  1773. }
  1774. }
  1775. break;
  1776. }
  1777. default :
  1778. {
  1779. pString = pHashN->pLocaleFixed->szINegSignPosn;
  1780. break;
  1781. }
  1782. }
  1783. }
  1784. else
  1785. {
  1786. pString = pHashN->pLocaleFixed->szINegSignPosn;
  1787. }
  1788. break;
  1789. }
  1790. case ( LOCALE_IPOSSYMPRECEDES ) :
  1791. {
  1792. //
  1793. // Use the ICURRENCY value from the user portion of the
  1794. // registry, if it exists.
  1795. //
  1796. // 0 => 1, 3
  1797. // 1 => 0, 2
  1798. //
  1799. Base = 10;
  1800. if (UserOverride &&
  1801. GetUserInfo( Locale,
  1802. LCType,
  1803. FIELD_OFFSET(NLS_USER_INFO, iCurrency),
  1804. NLS_VALUE_ICURRENCY,
  1805. pTemp,
  1806. ARRAYSIZE(pTemp),
  1807. TRUE ))
  1808. {
  1809. pString = pTemp;
  1810. //
  1811. // Set the appropriate value in pString.
  1812. //
  1813. switch (*pString)
  1814. {
  1815. case ( L'1' ) :
  1816. case ( L'3' ) :
  1817. {
  1818. *pString = L'0';
  1819. *(pString + 1) = 0;
  1820. break;
  1821. }
  1822. case ( L'0' ) :
  1823. case ( L'2' ) :
  1824. {
  1825. *pString = L'1';
  1826. *(pString + 1) = 0;
  1827. break;
  1828. }
  1829. default :
  1830. {
  1831. pString = pHashN->pLocaleFixed->szIPosSymPrecedes;
  1832. break;
  1833. }
  1834. }
  1835. }
  1836. else
  1837. {
  1838. pString = pHashN->pLocaleFixed->szIPosSymPrecedes;
  1839. }
  1840. break;
  1841. }
  1842. case ( LOCALE_IPOSSEPBYSPACE ) :
  1843. {
  1844. //
  1845. // Use the ICURRENCY value from the user portion of the
  1846. // registry, if it exists.
  1847. //
  1848. // 0 => 0, 1
  1849. // 1 => 2, 3
  1850. //
  1851. Base = 10;
  1852. if (UserOverride &&
  1853. GetUserInfo( Locale,
  1854. LCType,
  1855. FIELD_OFFSET(NLS_USER_INFO, iCurrency),
  1856. NLS_VALUE_ICURRENCY,
  1857. pTemp,
  1858. ARRAYSIZE(pTemp),
  1859. TRUE ))
  1860. {
  1861. pString = pTemp;
  1862. //
  1863. // Set the appropriate value in pString.
  1864. //
  1865. switch (*pString)
  1866. {
  1867. case ( L'0' ) :
  1868. case ( L'1' ) :
  1869. {
  1870. *pString = L'0';
  1871. *(pString + 1) = 0;
  1872. break;
  1873. }
  1874. case ( L'2' ) :
  1875. case ( L'3' ) :
  1876. {
  1877. *pString = L'1';
  1878. *(pString + 1) = 0;
  1879. break;
  1880. }
  1881. default :
  1882. {
  1883. pString = pHashN->pLocaleFixed->szIPosSepBySpace;
  1884. break;
  1885. }
  1886. }
  1887. }
  1888. else
  1889. {
  1890. pString = pHashN->pLocaleFixed->szIPosSepBySpace;
  1891. }
  1892. break;
  1893. }
  1894. case ( LOCALE_INEGSYMPRECEDES ) :
  1895. {
  1896. //
  1897. // Use the INEGCURR value from the user portion of the
  1898. // registry, if it exists.
  1899. //
  1900. // 0 => 4, 5, 6, 7, 8, 10, 13, 15
  1901. // 1 => 0, 1, 2, 3, 9, 11, 12, 14
  1902. //
  1903. Base = 10;
  1904. if (UserOverride &&
  1905. GetUserInfo( Locale,
  1906. LCType,
  1907. FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
  1908. NLS_VALUE_INEGCURR,
  1909. pTemp,
  1910. ARRAYSIZE(pTemp),
  1911. TRUE ))
  1912. {
  1913. pString = pTemp;
  1914. //
  1915. // Set the appropriate value in pString.
  1916. //
  1917. switch (*pString)
  1918. {
  1919. case ( L'4' ) :
  1920. case ( L'5' ) :
  1921. case ( L'6' ) :
  1922. case ( L'7' ) :
  1923. case ( L'8' ) :
  1924. {
  1925. *pString = L'0';
  1926. *(pString + 1) = 0;
  1927. break;
  1928. }
  1929. case ( L'0' ) :
  1930. case ( L'2' ) :
  1931. case ( L'3' ) :
  1932. case ( L'9' ) :
  1933. {
  1934. *pString = L'1';
  1935. *(pString + 1) = 0;
  1936. break;
  1937. }
  1938. case ( L'1' ) :
  1939. {
  1940. if ((*(pString + 1) == L'0') ||
  1941. (*(pString + 1) == L'3') ||
  1942. (*(pString + 1) == L'5'))
  1943. {
  1944. *pString = L'0';
  1945. *(pString + 1) = 0;
  1946. }
  1947. else
  1948. {
  1949. *pString = L'1';
  1950. *(pString + 1) = 0;
  1951. }
  1952. break;
  1953. }
  1954. default :
  1955. {
  1956. pString = pHashN->pLocaleFixed->szINegSymPrecedes;
  1957. break;
  1958. }
  1959. }
  1960. }
  1961. else
  1962. {
  1963. pString = pHashN->pLocaleFixed->szINegSymPrecedes;
  1964. }
  1965. break;
  1966. }
  1967. case ( LOCALE_INEGSEPBYSPACE ) :
  1968. {
  1969. //
  1970. // Use the INEGCURR value from the user portion of the
  1971. // registry, if it exists.
  1972. //
  1973. // 0 => 0, 1, 2, 3, 4, 5, 6, 7
  1974. // 1 => 8, 9, 10, 11, 12, 13, 14, 15
  1975. //
  1976. Base = 10;
  1977. if (UserOverride &&
  1978. GetUserInfo( Locale,
  1979. LCType,
  1980. FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
  1981. NLS_VALUE_INEGCURR,
  1982. pTemp,
  1983. ARRAYSIZE(pTemp),
  1984. TRUE ))
  1985. {
  1986. pString = pTemp;
  1987. //
  1988. // Set the appropriate value in pString.
  1989. //
  1990. switch (*pString)
  1991. {
  1992. case ( L'0' ) :
  1993. case ( L'2' ) :
  1994. case ( L'3' ) :
  1995. case ( L'4' ) :
  1996. case ( L'5' ) :
  1997. case ( L'6' ) :
  1998. case ( L'7' ) :
  1999. {
  2000. *pString = L'0';
  2001. *(pString + 1) = 0;
  2002. break;
  2003. }
  2004. case ( L'8' ) :
  2005. case ( L'9' ) :
  2006. {
  2007. *pString = L'1';
  2008. *(pString + 1) = 0;
  2009. break;
  2010. }
  2011. case ( L'1' ) :
  2012. {
  2013. if (*(pString + 1) == 0)
  2014. {
  2015. *pString = L'0';
  2016. *(pString + 1) = 0;
  2017. }
  2018. else
  2019. {
  2020. *pString = L'1';
  2021. *(pString + 1) = 0;
  2022. }
  2023. break;
  2024. }
  2025. default :
  2026. {
  2027. pString = pHashN->pLocaleFixed->szINegSepBySpace;
  2028. break;
  2029. }
  2030. }
  2031. }
  2032. else
  2033. {
  2034. pString = pHashN->pLocaleFixed->szINegSepBySpace;
  2035. }
  2036. break;
  2037. }
  2038. case ( LOCALE_STIMEFORMAT ) :
  2039. {
  2040. if (UserOverride &&
  2041. GetUserInfo( Locale,
  2042. LCType,
  2043. FIELD_OFFSET(NLS_USER_INFO, sTimeFormat),
  2044. NLS_VALUE_STIMEFORMAT,
  2045. pTemp,
  2046. ARRAYSIZE(pTemp),
  2047. TRUE ))
  2048. {
  2049. pString = pTemp;
  2050. }
  2051. else
  2052. {
  2053. pString = pStart + pHashN->pLocaleHdr->STimeFormat;
  2054. }
  2055. break;
  2056. }
  2057. case ( LOCALE_STIME ) :
  2058. {
  2059. if (UserOverride &&
  2060. GetUserInfo( Locale,
  2061. LCType,
  2062. FIELD_OFFSET(NLS_USER_INFO, sTime),
  2063. NLS_VALUE_STIME,
  2064. pTemp,
  2065. ARRAYSIZE(pTemp),
  2066. TRUE ))
  2067. {
  2068. pString = pTemp;
  2069. }
  2070. else
  2071. {
  2072. pString = pStart + pHashN->pLocaleHdr->STime;
  2073. }
  2074. break;
  2075. }
  2076. case ( LOCALE_ITIME ) :
  2077. {
  2078. Base = 10;
  2079. if (UserOverride &&
  2080. GetUserInfo( Locale,
  2081. LCType,
  2082. FIELD_OFFSET(NLS_USER_INFO, iTime),
  2083. NLS_VALUE_ITIME,
  2084. pTemp,
  2085. ARRAYSIZE(pTemp),
  2086. TRUE ))
  2087. {
  2088. pString = pTemp;
  2089. }
  2090. else
  2091. {
  2092. pString = pHashN->pLocaleFixed->szITime;
  2093. }
  2094. break;
  2095. }
  2096. case ( LOCALE_ITLZERO ) :
  2097. {
  2098. Base = 10;
  2099. if (UserOverride &&
  2100. GetUserInfo( Locale,
  2101. LCType,
  2102. FIELD_OFFSET(NLS_USER_INFO, iTLZero),
  2103. NLS_VALUE_ITLZERO,
  2104. pTemp,
  2105. ARRAYSIZE(pTemp),
  2106. TRUE ))
  2107. {
  2108. pString = pTemp;
  2109. }
  2110. else
  2111. {
  2112. pString = pHashN->pLocaleFixed->szITLZero;
  2113. }
  2114. break;
  2115. }
  2116. case ( LOCALE_ITIMEMARKPOSN ) :
  2117. {
  2118. Base = 10;
  2119. if (UserOverride &&
  2120. GetUserInfo( Locale,
  2121. LCType,
  2122. FIELD_OFFSET(NLS_USER_INFO, iTimeMarkPosn),
  2123. NLS_VALUE_ITIMEMARKPOSN,
  2124. pTemp,
  2125. ARRAYSIZE(pTemp),
  2126. TRUE ))
  2127. {
  2128. pString = pTemp;
  2129. }
  2130. else
  2131. {
  2132. pString = pHashN->pLocaleFixed->szITimeMarkPosn;
  2133. }
  2134. break;
  2135. }
  2136. case ( LOCALE_S1159 ) :
  2137. {
  2138. if (UserOverride &&
  2139. GetUserInfo( Locale,
  2140. LCType,
  2141. FIELD_OFFSET(NLS_USER_INFO, s1159),
  2142. NLS_VALUE_S1159,
  2143. pTemp,
  2144. ARRAYSIZE(pTemp),
  2145. FALSE ))
  2146. {
  2147. pString = pTemp;
  2148. }
  2149. else
  2150. {
  2151. pString = pStart + pHashN->pLocaleHdr->S1159;
  2152. }
  2153. break;
  2154. }
  2155. case ( LOCALE_S2359 ) :
  2156. {
  2157. if (UserOverride &&
  2158. GetUserInfo( Locale,
  2159. LCType,
  2160. FIELD_OFFSET(NLS_USER_INFO, s2359),
  2161. NLS_VALUE_S2359,
  2162. pTemp,
  2163. ARRAYSIZE(pTemp),
  2164. FALSE ))
  2165. {
  2166. pString = pTemp;
  2167. }
  2168. else
  2169. {
  2170. pString = pStart + pHashN->pLocaleHdr->S2359;
  2171. }
  2172. break;
  2173. }
  2174. case ( LOCALE_SSHORTDATE ) :
  2175. {
  2176. if (UserOverride &&
  2177. GetUserInfo( Locale,
  2178. LCType,
  2179. FIELD_OFFSET(NLS_USER_INFO, sShortDate),
  2180. NLS_VALUE_SSHORTDATE,
  2181. pTemp,
  2182. ARRAYSIZE(pTemp),
  2183. TRUE ))
  2184. {
  2185. pString = pTemp;
  2186. }
  2187. else
  2188. {
  2189. pString = pStart + pHashN->pLocaleHdr->SShortDate;
  2190. }
  2191. break;
  2192. }
  2193. case ( LOCALE_SDATE ) :
  2194. {
  2195. if (UserOverride &&
  2196. GetUserInfo( Locale,
  2197. LCType,
  2198. FIELD_OFFSET(NLS_USER_INFO, sDate),
  2199. NLS_VALUE_SDATE,
  2200. pTemp,
  2201. ARRAYSIZE(pTemp),
  2202. TRUE ))
  2203. {
  2204. pString = pTemp;
  2205. }
  2206. else
  2207. {
  2208. pString = pStart + pHashN->pLocaleHdr->SDate;
  2209. }
  2210. break;
  2211. }
  2212. case ( LOCALE_IDATE ) :
  2213. {
  2214. Base = 10;
  2215. if (UserOverride &&
  2216. GetUserInfo( Locale,
  2217. LCType,
  2218. FIELD_OFFSET(NLS_USER_INFO, iDate),
  2219. NLS_VALUE_IDATE,
  2220. pTemp,
  2221. ARRAYSIZE(pTemp),
  2222. TRUE ))
  2223. {
  2224. pString = pTemp;
  2225. }
  2226. else
  2227. {
  2228. pString = pHashN->pLocaleFixed->szIDate;
  2229. }
  2230. break;
  2231. }
  2232. case ( LOCALE_ICENTURY ) :
  2233. {
  2234. //
  2235. // Use the short date picture to get this information.
  2236. //
  2237. Base = 10;
  2238. if (UserOverride &&
  2239. GetUserInfo( Locale,
  2240. LCType,
  2241. FIELD_OFFSET(NLS_USER_INFO, sShortDate),
  2242. NLS_VALUE_SSHORTDATE,
  2243. pTemp,
  2244. ARRAYSIZE(pTemp),
  2245. TRUE ))
  2246. {
  2247. pString = pTemp;
  2248. //
  2249. // Find out how many y's in string.
  2250. // No need to ignore quotes in short date.
  2251. //
  2252. pTmp = pString;
  2253. while ((*pTmp) &&
  2254. (*pTmp != L'y'))
  2255. {
  2256. pTmp++;
  2257. }
  2258. //
  2259. // Set the appropriate value in pString.
  2260. //
  2261. if (*pTmp == L'y')
  2262. {
  2263. //
  2264. // Get the number of 'y' repetitions in the format string.
  2265. //
  2266. pTmp++;
  2267. for (Repeat = 0; (*pTmp == L'y'); Repeat++, pTmp++)
  2268. ;
  2269. switch (Repeat)
  2270. {
  2271. case ( 0 ) :
  2272. case ( 1 ) :
  2273. {
  2274. //
  2275. // Two-digit century with leading zero.
  2276. //
  2277. *pString = L'0';
  2278. *(pString + 1) = 0;
  2279. break;
  2280. }
  2281. case ( 2 ) :
  2282. case ( 3 ) :
  2283. default :
  2284. {
  2285. //
  2286. // Full century.
  2287. //
  2288. *pString = L'1';
  2289. *(pString + 1) = 0;
  2290. break;
  2291. }
  2292. }
  2293. break;
  2294. }
  2295. }
  2296. //
  2297. // Use the system default value.
  2298. //
  2299. pString = pHashN->pLocaleFixed->szICentury;
  2300. break;
  2301. }
  2302. case ( LOCALE_IDAYLZERO ) :
  2303. {
  2304. //
  2305. // Use the short date picture to get this information.
  2306. //
  2307. Base = 10;
  2308. if (UserOverride &&
  2309. GetUserInfo( Locale,
  2310. LCType,
  2311. FIELD_OFFSET(NLS_USER_INFO, sShortDate),
  2312. NLS_VALUE_SSHORTDATE,
  2313. pTemp,
  2314. ARRAYSIZE(pTemp),
  2315. TRUE ))
  2316. {
  2317. pString = pTemp;
  2318. //
  2319. // Find out how many d's in string.
  2320. // No need to ignore quotes in short date.
  2321. //
  2322. pTmp = pString;
  2323. while ((*pTmp) &&
  2324. (*pTmp != L'd'))
  2325. {
  2326. pTmp++;
  2327. }
  2328. //
  2329. // Set the appropriate value in pString.
  2330. //
  2331. if (*pTmp == L'd')
  2332. {
  2333. //
  2334. // Get the number of 'd' repetitions in the format string.
  2335. //
  2336. pTmp++;
  2337. for (Repeat = 0; (*pTmp == L'd'); Repeat++, pTmp++)
  2338. ;
  2339. switch (Repeat)
  2340. {
  2341. case ( 0 ) :
  2342. {
  2343. //
  2344. // No leading zero.
  2345. //
  2346. *pString = L'0';
  2347. *(pString + 1) = 0;
  2348. break;
  2349. }
  2350. case ( 1 ) :
  2351. default :
  2352. {
  2353. //
  2354. // Use leading zero.
  2355. //
  2356. *pString = L'1';
  2357. *(pString + 1) = 0;
  2358. break;
  2359. }
  2360. }
  2361. break;
  2362. }
  2363. }
  2364. //
  2365. // Use the system default value.
  2366. //
  2367. pString = pHashN->pLocaleFixed->szIDayLZero;
  2368. break;
  2369. }
  2370. case ( LOCALE_IMONLZERO ) :
  2371. {
  2372. //
  2373. // Use the short date picture to get this information.
  2374. //
  2375. Base = 10;
  2376. if (UserOverride &&
  2377. GetUserInfo( Locale,
  2378. LCType,
  2379. FIELD_OFFSET(NLS_USER_INFO, sShortDate),
  2380. NLS_VALUE_SSHORTDATE,
  2381. pTemp,
  2382. ARRAYSIZE(pTemp),
  2383. TRUE ))
  2384. {
  2385. pString = pTemp;
  2386. //
  2387. // Find out how many M's in string.
  2388. // No need to ignore quotes in short date.
  2389. //
  2390. pTmp = pString;
  2391. while ((*pTmp) &&
  2392. (*pTmp != L'M'))
  2393. {
  2394. pTmp++;
  2395. }
  2396. //
  2397. // Set the appropriate value in pString.
  2398. //
  2399. if (*pTmp == L'M')
  2400. {
  2401. //
  2402. // Get the number of 'M' repetitions in the format string.
  2403. //
  2404. pTmp++;
  2405. for (Repeat = 0; (*pTmp == L'M'); Repeat++, pTmp++)
  2406. ;
  2407. switch (Repeat)
  2408. {
  2409. case ( 0 ) :
  2410. {
  2411. //
  2412. // No leading zero.
  2413. //
  2414. *pString = L'0';
  2415. *(pString + 1) = 0;
  2416. break;
  2417. }
  2418. case ( 1 ) :
  2419. default :
  2420. {
  2421. //
  2422. // Use leading zero.
  2423. //
  2424. *pString = L'1';
  2425. *(pString + 1) = 0;
  2426. break;
  2427. }
  2428. }
  2429. break;
  2430. }
  2431. }
  2432. //
  2433. // Use the system default value.
  2434. //
  2435. pString = pHashN->pLocaleFixed->szIMonLZero;
  2436. break;
  2437. }
  2438. case ( LOCALE_SYEARMONTH ) :
  2439. {
  2440. if (UserOverride &&
  2441. GetUserInfo( Locale,
  2442. LCType,
  2443. FIELD_OFFSET(NLS_USER_INFO, sYearMonth),
  2444. NLS_VALUE_SYEARMONTH,
  2445. pTemp,
  2446. ARRAYSIZE(pTemp),
  2447. TRUE ))
  2448. {
  2449. pString = pTemp;
  2450. }
  2451. else
  2452. {
  2453. pString = pStart + pHashN->pLocaleHdr->SYearMonth;
  2454. }
  2455. break;
  2456. }
  2457. case ( LOCALE_SLONGDATE ) :
  2458. {
  2459. if (UserOverride &&
  2460. GetUserInfo( Locale,
  2461. LCType,
  2462. FIELD_OFFSET(NLS_USER_INFO, sLongDate),
  2463. NLS_VALUE_SLONGDATE,
  2464. pTemp,
  2465. ARRAYSIZE(pTemp),
  2466. TRUE ))
  2467. {
  2468. pString = pTemp;
  2469. }
  2470. else
  2471. {
  2472. pString = pStart + pHashN->pLocaleHdr->SLongDate;
  2473. }
  2474. break;
  2475. }
  2476. case ( LOCALE_ILDATE ) :
  2477. {
  2478. //
  2479. // Use the long date picture to get this information.
  2480. //
  2481. Base = 10;
  2482. if (UserOverride &&
  2483. GetUserInfo( Locale,
  2484. LCType,
  2485. FIELD_OFFSET(NLS_USER_INFO, sLongDate),
  2486. NLS_VALUE_SLONGDATE,
  2487. pTemp,
  2488. ARRAYSIZE(pTemp),
  2489. TRUE ))
  2490. {
  2491. pString = pTemp;
  2492. //
  2493. // Find out if d, M, or y is first, but ignore quotes.
  2494. // Also, if "ddd" or "dddd" is found, then skip it. Only
  2495. // want "d" or "dd".
  2496. //
  2497. pTmp = pString;
  2498. while (pTmp = wcspbrk(pTmp, L"dMy'"))
  2499. {
  2500. //
  2501. // Check special cases.
  2502. //
  2503. if (*pTmp == L'd')
  2504. {
  2505. //
  2506. // Check for d's. Ignore more than 2 d's.
  2507. //
  2508. for (Repeat = 0; (*pTmp == L'd'); Repeat++, pTmp++)
  2509. ;
  2510. if (Repeat < 3)
  2511. {
  2512. //
  2513. // Break out of while loop. Found "d" or "dd".
  2514. //
  2515. pTmp--;
  2516. break;
  2517. }
  2518. }
  2519. else if (*pTmp == NLS_CHAR_QUOTE)
  2520. {
  2521. //
  2522. // Ignore quotes.
  2523. //
  2524. pTmp++;
  2525. while ((*pTmp) && (*pTmp != NLS_CHAR_QUOTE))
  2526. {
  2527. pTmp++;
  2528. }
  2529. pTmp++;
  2530. }
  2531. else
  2532. {
  2533. //
  2534. // Found one of the values, so break out of
  2535. // while loop.
  2536. //
  2537. break;
  2538. }
  2539. }
  2540. //
  2541. // Set the appropriate value in pString.
  2542. //
  2543. if (pTmp)
  2544. {
  2545. switch (*pTmp)
  2546. {
  2547. case ( L'd' ) :
  2548. {
  2549. *pString = L'1';
  2550. break;
  2551. }
  2552. case ( L'M' ) :
  2553. {
  2554. *pString = L'0';
  2555. break;
  2556. }
  2557. case ( L'y' ) :
  2558. {
  2559. *pString = L'2';
  2560. break;
  2561. }
  2562. }
  2563. //
  2564. // Null terminate the string.
  2565. //
  2566. *(pString + 1) = 0;
  2567. break;
  2568. }
  2569. }
  2570. //
  2571. // Use the default value.
  2572. //
  2573. pString = pHashN->pLocaleFixed->szILDate;
  2574. break;
  2575. }
  2576. case ( LOCALE_ICALENDARTYPE ) :
  2577. {
  2578. Base = 10;
  2579. if (UserOverride &&
  2580. GetUserInfo( Locale,
  2581. LCType,
  2582. FIELD_OFFSET(NLS_USER_INFO, iCalType),
  2583. NLS_VALUE_ICALENDARTYPE,
  2584. pTemp,
  2585. ARRAYSIZE(pTemp),
  2586. TRUE ))
  2587. {
  2588. pString = pTemp;
  2589. }
  2590. else
  2591. {
  2592. pString = pHashN->pLocaleFixed->szICalendarType;
  2593. }
  2594. break;
  2595. }
  2596. case ( LOCALE_IOPTIONALCALENDAR ) :
  2597. {
  2598. Base = 10;
  2599. pString = pStart + pHashN->pLocaleHdr->IOptionalCal;
  2600. pString = ((POPT_CAL)pString)->pCalStr;
  2601. break;
  2602. }
  2603. case ( LOCALE_IFIRSTDAYOFWEEK ) :
  2604. {
  2605. Base = 10;
  2606. if (UserOverride &&
  2607. GetUserInfo( Locale,
  2608. LCType,
  2609. FIELD_OFFSET(NLS_USER_INFO, iFirstDay),
  2610. NLS_VALUE_IFIRSTDAYOFWEEK,
  2611. pTemp,
  2612. ARRAYSIZE(pTemp),
  2613. TRUE ))
  2614. {
  2615. pString = pTemp;
  2616. }
  2617. else
  2618. {
  2619. pString = pHashN->pLocaleFixed->szIFirstDayOfWk;
  2620. }
  2621. break;
  2622. }
  2623. case ( LOCALE_IFIRSTWEEKOFYEAR ) :
  2624. {
  2625. Base = 10;
  2626. if (UserOverride &&
  2627. GetUserInfo( Locale,
  2628. LCType,
  2629. FIELD_OFFSET(NLS_USER_INFO, iFirstWeek),
  2630. NLS_VALUE_IFIRSTWEEKOFYEAR,
  2631. pTemp,
  2632. ARRAYSIZE(pTemp),
  2633. TRUE ))
  2634. {
  2635. pString = pTemp;
  2636. }
  2637. else
  2638. {
  2639. pString = pHashN->pLocaleFixed->szIFirstWkOfYr;
  2640. }
  2641. break;
  2642. }
  2643. case ( LOCALE_SDAYNAME1 ) :
  2644. {
  2645. pString = pStart + pHashN->pLocaleHdr->SDayName1;
  2646. break;
  2647. }
  2648. case ( LOCALE_SDAYNAME2 ) :
  2649. {
  2650. pString = pStart + pHashN->pLocaleHdr->SDayName2;
  2651. break;
  2652. }
  2653. case ( LOCALE_SDAYNAME3 ) :
  2654. {
  2655. pString = pStart + pHashN->pLocaleHdr->SDayName3;
  2656. break;
  2657. }
  2658. case ( LOCALE_SDAYNAME4 ) :
  2659. {
  2660. pString = pStart + pHashN->pLocaleHdr->SDayName4;
  2661. break;
  2662. }
  2663. case ( LOCALE_SDAYNAME5 ) :
  2664. {
  2665. pString = pStart + pHashN->pLocaleHdr->SDayName5;
  2666. break;
  2667. }
  2668. case ( LOCALE_SDAYNAME6 ) :
  2669. {
  2670. pString = pStart + pHashN->pLocaleHdr->SDayName6;
  2671. break;
  2672. }
  2673. case ( LOCALE_SDAYNAME7 ) :
  2674. {
  2675. pString = pStart + pHashN->pLocaleHdr->SDayName7;
  2676. break;
  2677. }
  2678. case ( LOCALE_SABBREVDAYNAME1 ) :
  2679. {
  2680. pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName1;
  2681. break;
  2682. }
  2683. case ( LOCALE_SABBREVDAYNAME2 ) :
  2684. {
  2685. pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName2;
  2686. break;
  2687. }
  2688. case ( LOCALE_SABBREVDAYNAME3 ) :
  2689. {
  2690. pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName3;
  2691. break;
  2692. }
  2693. case ( LOCALE_SABBREVDAYNAME4 ) :
  2694. {
  2695. pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName4;
  2696. break;
  2697. }
  2698. case ( LOCALE_SABBREVDAYNAME5 ) :
  2699. {
  2700. pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName5;
  2701. break;
  2702. }
  2703. case ( LOCALE_SABBREVDAYNAME6 ) :
  2704. {
  2705. pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName6;
  2706. break;
  2707. }
  2708. case ( LOCALE_SABBREVDAYNAME7 ) :
  2709. {
  2710. pString = pStart + pHashN->pLocaleHdr->SAbbrevDayName7;
  2711. break;
  2712. }
  2713. case ( LOCALE_SMONTHNAME1 ) :
  2714. {
  2715. pString = pStart + pHashN->pLocaleHdr->SMonthName1;
  2716. break;
  2717. }
  2718. case ( LOCALE_SMONTHNAME2 ) :
  2719. {
  2720. pString = pStart + pHashN->pLocaleHdr->SMonthName2;
  2721. break;
  2722. }
  2723. case ( LOCALE_SMONTHNAME3 ) :
  2724. {
  2725. pString = pStart + pHashN->pLocaleHdr->SMonthName3;
  2726. break;
  2727. }
  2728. case ( LOCALE_SMONTHNAME4 ) :
  2729. {
  2730. pString = pStart + pHashN->pLocaleHdr->SMonthName4;
  2731. break;
  2732. }
  2733. case ( LOCALE_SMONTHNAME5 ) :
  2734. {
  2735. pString = pStart + pHashN->pLocaleHdr->SMonthName5;
  2736. break;
  2737. }
  2738. case ( LOCALE_SMONTHNAME6 ) :
  2739. {
  2740. pString = pStart + pHashN->pLocaleHdr->SMonthName6;
  2741. break;
  2742. }
  2743. case ( LOCALE_SMONTHNAME7 ) :
  2744. {
  2745. pString = pStart + pHashN->pLocaleHdr->SMonthName7;
  2746. break;
  2747. }
  2748. case ( LOCALE_SMONTHNAME8 ) :
  2749. {
  2750. pString = pStart + pHashN->pLocaleHdr->SMonthName8;
  2751. break;
  2752. }
  2753. case ( LOCALE_SMONTHNAME9 ) :
  2754. {
  2755. pString = pStart + pHashN->pLocaleHdr->SMonthName9;
  2756. break;
  2757. }
  2758. case ( LOCALE_SMONTHNAME10 ) :
  2759. {
  2760. pString = pStart + pHashN->pLocaleHdr->SMonthName10;
  2761. break;
  2762. }
  2763. case ( LOCALE_SMONTHNAME11 ) :
  2764. {
  2765. pString = pStart + pHashN->pLocaleHdr->SMonthName11;
  2766. break;
  2767. }
  2768. case ( LOCALE_SMONTHNAME12 ) :
  2769. {
  2770. pString = pStart + pHashN->pLocaleHdr->SMonthName12;
  2771. break;
  2772. }
  2773. case ( LOCALE_SMONTHNAME13 ) :
  2774. {
  2775. pString = pStart + pHashN->pLocaleHdr->SMonthName13;
  2776. break;
  2777. }
  2778. case ( LOCALE_SABBREVMONTHNAME1 ) :
  2779. {
  2780. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName1;
  2781. break;
  2782. }
  2783. case ( LOCALE_SABBREVMONTHNAME2 ) :
  2784. {
  2785. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName2;
  2786. break;
  2787. }
  2788. case ( LOCALE_SABBREVMONTHNAME3 ) :
  2789. {
  2790. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName3;
  2791. break;
  2792. }
  2793. case ( LOCALE_SABBREVMONTHNAME4 ) :
  2794. {
  2795. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName4;
  2796. break;
  2797. }
  2798. case ( LOCALE_SABBREVMONTHNAME5 ) :
  2799. {
  2800. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName5;
  2801. break;
  2802. }
  2803. case ( LOCALE_SABBREVMONTHNAME6 ) :
  2804. {
  2805. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName6;
  2806. break;
  2807. }
  2808. case ( LOCALE_SABBREVMONTHNAME7 ) :
  2809. {
  2810. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName7;
  2811. break;
  2812. }
  2813. case ( LOCALE_SABBREVMONTHNAME8 ) :
  2814. {
  2815. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName8;
  2816. break;
  2817. }
  2818. case ( LOCALE_SABBREVMONTHNAME9 ) :
  2819. {
  2820. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName9;
  2821. break;
  2822. }
  2823. case ( LOCALE_SABBREVMONTHNAME10 ) :
  2824. {
  2825. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName10;
  2826. break;
  2827. }
  2828. case ( LOCALE_SABBREVMONTHNAME11 ) :
  2829. {
  2830. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName11;
  2831. break;
  2832. }
  2833. case ( LOCALE_SABBREVMONTHNAME12 ) :
  2834. {
  2835. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName12;
  2836. break;
  2837. }
  2838. case ( LOCALE_SABBREVMONTHNAME13 ) :
  2839. {
  2840. pString = pStart + pHashN->pLocaleHdr->SAbbrevMonthName13;
  2841. break;
  2842. }
  2843. case ( LOCALE_FONTSIGNATURE ) :
  2844. {
  2845. //
  2846. // Check cchData for size of given buffer.
  2847. //
  2848. if (cchData == 0)
  2849. {
  2850. return (MAX_FONTSIGNATURE);
  2851. }
  2852. else if (cchData < MAX_FONTSIGNATURE)
  2853. {
  2854. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  2855. return (0);
  2856. }
  2857. //
  2858. // This string does NOT get zero terminated.
  2859. //
  2860. pString = pHashN->pLocaleFixed->szFontSignature;
  2861. //
  2862. // Copy the string to lpLCData and return the number of
  2863. // characters copied.
  2864. //
  2865. RtlMoveMemory(lpLCData, pString, MAX_FONTSIGNATURE * sizeof(WCHAR));
  2866. return (MAX_FONTSIGNATURE);
  2867. break;
  2868. }
  2869. default :
  2870. {
  2871. SetLastError(ERROR_INVALID_FLAGS);
  2872. return (0);
  2873. }
  2874. }
  2875. //
  2876. // See if the caller wants the value in the form of a number instead
  2877. // of a string.
  2878. //
  2879. if (ReturnNum)
  2880. {
  2881. //
  2882. // Make sure the flags are valid and there is enough buffer
  2883. // space.
  2884. //
  2885. if (Base == 0)
  2886. {
  2887. SetLastError(ERROR_INVALID_FLAGS);
  2888. return (0);
  2889. }
  2890. if (cchData < 2)
  2891. {
  2892. if (cchData == 0)
  2893. {
  2894. //
  2895. // DWORD is needed for this option (2 WORDS), so return 2.
  2896. //
  2897. return (2);
  2898. }
  2899. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  2900. return (0);
  2901. }
  2902. //
  2903. // Convert the string to an int and return 2 (1 DWORD = 2 WORDS).
  2904. //
  2905. RtlInitUnicodeString(&ObUnicodeStr, pString);
  2906. if (RtlUnicodeStringToInteger(&ObUnicodeStr, Base, (LPDWORD)lpLCData))
  2907. {
  2908. SetLastError(ERROR_INVALID_FLAGS);
  2909. return (0);
  2910. }
  2911. return (2);
  2912. }
  2913. //
  2914. // Get the length (in characters) of the string to copy.
  2915. //
  2916. if (Length == 0)
  2917. {
  2918. Length = NlsStrLenW(pString);
  2919. }
  2920. //
  2921. // Add one for the null termination. All strings should be null
  2922. // terminated.
  2923. //
  2924. Length++;
  2925. //
  2926. // Check cchData for size of given buffer.
  2927. //
  2928. if (cchData == 0)
  2929. {
  2930. //
  2931. // If cchData is 0, then we can't use lpLCData. In this
  2932. // case, we simply want to return the length (in characters) of
  2933. // the string to be copied.
  2934. //
  2935. return (Length);
  2936. }
  2937. else if (cchData < Length)
  2938. {
  2939. //
  2940. // The buffer is too small for the string, so return an error
  2941. // and zero bytes written.
  2942. //
  2943. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  2944. return (0);
  2945. }
  2946. //
  2947. // Copy the string to lpLCData and null terminate it.
  2948. // Return the number of characters copied.
  2949. //
  2950. wcsncpy(lpLCData, pString, Length - 1);
  2951. lpLCData[Length - 1] = 0;
  2952. return (Length);
  2953. }
  2954. ////////////////////////////////////////////////////////////////////////////
  2955. //
  2956. // SetLocaleInfoW
  2957. //
  2958. // Sets one of the various pieces of information about a particular
  2959. // locale by making an entry in the user's portion of the configuration
  2960. // registry. This will only affect the user override portion of the locale
  2961. // settings. The system defaults will never be reset.
  2962. //
  2963. // 07-14-93 JulieB Created.
  2964. ////////////////////////////////////////////////////////////////////////////
  2965. BOOL WINAPI SetLocaleInfoW(
  2966. LCID Locale,
  2967. LCTYPE LCType,
  2968. LPCWSTR lpLCData)
  2969. {
  2970. PLOC_HASH pHashN; // ptr to LOC hash node
  2971. int cchData; // length of lpLCData
  2972. LPWSTR pString; // ptr to info string to change
  2973. LPWSTR pPos; // ptr to position in info string
  2974. LPWSTR pPos2; // ptr to position in info string
  2975. LPWSTR pSep; // ptr to separator string
  2976. WCHAR pTemp[MAX_PATH_LEN]; // ptr to temp storage buffer
  2977. WCHAR pOutput[MAX_REG_VAL_SIZE]; // ptr to output for GetInfo call
  2978. WCHAR pOutput2[MAX_REG_VAL_SIZE]; // ptr to output for GetInfo call
  2979. UINT Order; // date or time order value
  2980. UINT TLZero; // time leading zero value
  2981. UINT TimeMarkPosn; // time mark position value
  2982. WCHAR pFind[3]; // ptr to chars to find
  2983. int SepLen; // length of separator string
  2984. UNICODE_STRING ObUnicodeStr; // value string
  2985. int Value; // value
  2986. //
  2987. // Invalid Parameter Check:
  2988. // - validate LCID
  2989. // - NULL data pointer
  2990. //
  2991. // NOTE: invalid type is checked in the switch statement below.
  2992. //
  2993. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  2994. if ((pHashN == NULL) || (lpLCData == NULL))
  2995. {
  2996. SetLastError(ERROR_INVALID_PARAMETER);
  2997. return (FALSE);
  2998. }
  2999. //
  3000. // Get the length of the buffer.
  3001. //
  3002. cchData = NlsStrLenW(lpLCData) + 1;
  3003. //
  3004. // Initialize temp buffer.
  3005. //
  3006. pTemp[0] = 0;
  3007. //
  3008. // Set the appropriate user information for the given LCTYPE.
  3009. //
  3010. LCType &= (~LOCALE_USE_CP_ACP);
  3011. switch (LCType)
  3012. {
  3013. case ( LOCALE_SLIST ) :
  3014. {
  3015. //
  3016. // Validate the new value. It should be no longer than
  3017. // MAX_SLIST wide characters in length.
  3018. //
  3019. if (cchData > MAX_SLIST)
  3020. {
  3021. SetLastError(ERROR_INVALID_PARAMETER);
  3022. return (FALSE);
  3023. }
  3024. //
  3025. // Set the registry with the new SLIST string.
  3026. //
  3027. return (SetUserInfo( LOCALE_SLIST,
  3028. (LPWSTR)lpLCData,
  3029. cchData ));
  3030. break;
  3031. }
  3032. case ( LOCALE_IMEASURE ) :
  3033. {
  3034. //
  3035. // Validate the new value. It should be no longer than
  3036. // MAX_IMEASURE wide characters in length.
  3037. // It should be between 0 and MAX_VALUE_IMEASURE.
  3038. //
  3039. // NOTE: The string may not be NULL, so it must be at least
  3040. // 2 chars long (includes null).
  3041. //
  3042. // Optimized - since MAX_IMEASURE is 2.
  3043. //
  3044. if ((cchData != MAX_IMEASURE) ||
  3045. (*lpLCData < NLS_CHAR_ZERO) ||
  3046. (*lpLCData > MAX_CHAR_IMEASURE))
  3047. {
  3048. SetLastError(ERROR_INVALID_PARAMETER);
  3049. return (FALSE);
  3050. }
  3051. //
  3052. // Set the registry with the new IMEASURE string.
  3053. //
  3054. return (SetUserInfo( LOCALE_IMEASURE,
  3055. (LPWSTR)lpLCData,
  3056. cchData ));
  3057. break;
  3058. }
  3059. case ( LOCALE_IPAPERSIZE ) :
  3060. {
  3061. //
  3062. // Validate the new value.
  3063. // It should be between DMPAPER_LETTER and DMPAPER_LAST.
  3064. //
  3065. // NOTE: The string may not be NULL, so it must be at least
  3066. // 2 chars long (includes null).
  3067. //
  3068. RtlInitUnicodeString(&ObUnicodeStr, lpLCData);
  3069. if ((cchData < 2) ||
  3070. (RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
  3071. (Value < DMPAPER_LETTER) || (Value > DMPAPER_LAST))
  3072. {
  3073. SetLastError(ERROR_INVALID_PARAMETER);
  3074. return (FALSE);
  3075. }
  3076. //
  3077. // Set the registry with the new IPAPERSIZE string.
  3078. //
  3079. return (SetUserInfo( LOCALE_IPAPERSIZE,
  3080. (LPWSTR)lpLCData,
  3081. cchData ));
  3082. break;
  3083. }
  3084. case ( LOCALE_SDECIMAL ) :
  3085. {
  3086. //
  3087. // Validate the new value. It should be no longer than
  3088. // MAX_SDECIMAL wide characters in length and should not
  3089. // contain any integer values (L'0' thru L'9').
  3090. //
  3091. if (!IsValidSeparatorString( lpLCData,
  3092. MAX_SDECIMAL,
  3093. FALSE ))
  3094. {
  3095. SetLastError(ERROR_INVALID_PARAMETER);
  3096. return (FALSE);
  3097. }
  3098. //
  3099. // Set the registry with the new SDECIMAL string.
  3100. //
  3101. return (SetUserInfo( LOCALE_SDECIMAL,
  3102. (LPWSTR)lpLCData,
  3103. cchData ));
  3104. break;
  3105. }
  3106. case ( LOCALE_STHOUSAND ) :
  3107. {
  3108. //
  3109. // Validate the new value. It should be no longer than
  3110. // MAX_STHOUSAND wide characters in length and should not
  3111. // contain any integer values (L'0' thru L'9').
  3112. //
  3113. if (!IsValidSeparatorString( lpLCData,
  3114. MAX_STHOUSAND,
  3115. FALSE ))
  3116. {
  3117. SetLastError(ERROR_INVALID_PARAMETER);
  3118. return (FALSE);
  3119. }
  3120. //
  3121. // Set the registry with the new STHOUSAND string.
  3122. //
  3123. return (SetUserInfo( LOCALE_STHOUSAND,
  3124. (LPWSTR)lpLCData,
  3125. cchData ));
  3126. break;
  3127. }
  3128. case ( LOCALE_SGROUPING ) :
  3129. {
  3130. //
  3131. // Validate the new value. It should be no longer than
  3132. // MAX_SGROUPING wide characters in length and should
  3133. // contain alternating integer and semicolon values.
  3134. // (eg. 3;2;0 or 3;0 or 0)
  3135. //
  3136. // NOTE: The string may not be NULL, so it must be at least
  3137. // 2 chars long (includes null).
  3138. //
  3139. if (!IsValidGroupingString( lpLCData,
  3140. MAX_SGROUPING,
  3141. TRUE ))
  3142. {
  3143. SetLastError(ERROR_INVALID_PARAMETER);
  3144. return (FALSE);
  3145. }
  3146. //
  3147. // Set the registry with the new SGROUPING string.
  3148. //
  3149. return (SetUserInfo( LOCALE_SGROUPING,
  3150. (LPWSTR)lpLCData,
  3151. cchData ));
  3152. break;
  3153. }
  3154. case ( LOCALE_IDIGITS ) :
  3155. {
  3156. //
  3157. // Validate the new value. It should be no longer than
  3158. // MAX_IDIGITS wide characters in length.
  3159. // The value should be between 0 and MAX_VALUE_IDIGITS.
  3160. //
  3161. // NOTE: The string may not be NULL, so it must be at least
  3162. // 2 chars long (includes null).
  3163. //
  3164. // Optimized - since MAX_IDIGITS is 2.
  3165. //
  3166. if ((cchData != MAX_IDIGITS) ||
  3167. (*lpLCData < NLS_CHAR_ZERO) ||
  3168. (*lpLCData > MAX_CHAR_IDIGITS))
  3169. {
  3170. SetLastError(ERROR_INVALID_PARAMETER);
  3171. return (FALSE);
  3172. }
  3173. //
  3174. // Set the registry with the new IDIGITS string.
  3175. //
  3176. return (SetUserInfo( LOCALE_IDIGITS,
  3177. (LPWSTR)lpLCData,
  3178. cchData ));
  3179. break;
  3180. }
  3181. case ( LOCALE_ILZERO ) :
  3182. {
  3183. //
  3184. // Validate the new value. It should be no longer than
  3185. // MAX_ILZERO wide characters in length.
  3186. // The value should be between 0 and MAX_VALUE_ILZERO.
  3187. //
  3188. // NOTE: The string may not be NULL, so it must be at least
  3189. // 2 chars long (includes null).
  3190. //
  3191. // Optimized - since MAX_ILZERO is 2.
  3192. //
  3193. if ((cchData != MAX_ILZERO) ||
  3194. (*lpLCData < NLS_CHAR_ZERO) ||
  3195. (*lpLCData > MAX_CHAR_ILZERO))
  3196. {
  3197. SetLastError(ERROR_INVALID_PARAMETER);
  3198. return (FALSE);
  3199. }
  3200. //
  3201. // Set the registry with the new ILZERO string.
  3202. //
  3203. return (SetUserInfo( LOCALE_ILZERO,
  3204. (LPWSTR)lpLCData,
  3205. cchData ));
  3206. break;
  3207. }
  3208. case ( LOCALE_INEGNUMBER ) :
  3209. {
  3210. //
  3211. // Validate the new value. It should be no longer than
  3212. // MAX_INEGNUMBER wide characters in length.
  3213. // The value should be between 0 and MAX_VALUE_INEGNUMBER.
  3214. //
  3215. // NOTE: The string may not be NULL, so it must be at least
  3216. // 2 chars long (includes null).
  3217. //
  3218. // Optimized - since MAX_INEGNUMBER is 2.
  3219. //
  3220. if ((cchData != MAX_INEGNUMBER) ||
  3221. (*lpLCData < NLS_CHAR_ZERO) ||
  3222. (*lpLCData > MAX_CHAR_INEGNUMBER))
  3223. {
  3224. SetLastError(ERROR_INVALID_PARAMETER);
  3225. return (FALSE);
  3226. }
  3227. //
  3228. // Set the registry with the new INEGNUMBER string.
  3229. //
  3230. return (SetUserInfo( LOCALE_INEGNUMBER,
  3231. (LPWSTR)lpLCData,
  3232. cchData ));
  3233. break;
  3234. }
  3235. case ( LOCALE_SNATIVEDIGITS ) :
  3236. {
  3237. //
  3238. // Validate the new value. It should be exactly
  3239. // MAX_SNATIVEDIGITS wide characters in length.
  3240. //
  3241. if (cchData != MAX_SNATIVEDIGITS)
  3242. {
  3243. SetLastError(ERROR_INVALID_PARAMETER);
  3244. return (FALSE);
  3245. }
  3246. //
  3247. // Set the registry with the new SNATIVEDIGITS string.
  3248. //
  3249. return (SetUserInfo( LOCALE_SNATIVEDIGITS,
  3250. (LPWSTR)lpLCData,
  3251. cchData ));
  3252. break;
  3253. }
  3254. case ( LOCALE_IDIGITSUBSTITUTION ) :
  3255. {
  3256. //
  3257. // Validate the new value. It should be no longer than
  3258. // MAX_IDIGITSUBST wide characters in length.
  3259. // The value should be between 0 and MAX_VALUE_IDIGITSUBST.
  3260. //
  3261. // NOTE: The string may not be NULL, so it must be at least
  3262. // 2 chars long (includes null).
  3263. //
  3264. // Optimized - since MAX_IDIGITSUBST is 2.
  3265. //
  3266. if ((cchData != MAX_IDIGITSUBST) ||
  3267. (*lpLCData < NLS_CHAR_ZERO) ||
  3268. (*lpLCData > MAX_CHAR_IDIGITSUBST))
  3269. {
  3270. SetLastError(ERROR_INVALID_PARAMETER);
  3271. return (FALSE);
  3272. }
  3273. //
  3274. // Set the registry with the new IDIGITSUBST string.
  3275. //
  3276. return (SetUserInfo( LOCALE_IDIGITSUBSTITUTION,
  3277. (LPWSTR)lpLCData,
  3278. cchData ));
  3279. break;
  3280. }
  3281. case ( LOCALE_SCURRENCY ) :
  3282. {
  3283. //
  3284. // Validate the new value. It should be no longer than
  3285. // MAX_SCURRENCY wide characters in length and should not
  3286. // contain any integer values (L'0' thru L'9').
  3287. //
  3288. if (!IsValidSeparatorString( lpLCData,
  3289. MAX_SCURRENCY,
  3290. FALSE ))
  3291. {
  3292. SetLastError(ERROR_INVALID_PARAMETER);
  3293. return (FALSE);
  3294. }
  3295. //
  3296. // Set the registry with the new SCURRENCY string.
  3297. //
  3298. return (SetUserInfo( LOCALE_SCURRENCY,
  3299. (LPWSTR)lpLCData,
  3300. cchData ));
  3301. break;
  3302. }
  3303. case ( LOCALE_SMONDECIMALSEP ) :
  3304. {
  3305. //
  3306. // Validate the new value. It should be no longer than
  3307. // MAX_SMONDECSEP wide characters in length and should not
  3308. // contain any integer values (L'0' thru L'9').
  3309. //
  3310. if (!IsValidSeparatorString( lpLCData,
  3311. MAX_SMONDECSEP,
  3312. FALSE ))
  3313. {
  3314. SetLastError(ERROR_INVALID_PARAMETER);
  3315. return (FALSE);
  3316. }
  3317. //
  3318. // Set the registry with the new SMONDECIMALSEP string.
  3319. //
  3320. return (SetUserInfo( LOCALE_SMONDECIMALSEP,
  3321. (LPWSTR)lpLCData,
  3322. cchData ));
  3323. break;
  3324. }
  3325. case ( LOCALE_SMONTHOUSANDSEP ) :
  3326. {
  3327. //
  3328. // Validate the new value. It should be no longer than
  3329. // MAX_SMONTHOUSEP wide characters in length and should not
  3330. // contain any integer values (L'0' thru L'9').
  3331. //
  3332. if (!IsValidSeparatorString( lpLCData,
  3333. MAX_SMONTHOUSEP,
  3334. FALSE ))
  3335. {
  3336. SetLastError(ERROR_INVALID_PARAMETER);
  3337. return (FALSE);
  3338. }
  3339. //
  3340. // Set the registry with the new SMONTHOUSANDSEP string.
  3341. //
  3342. return (SetUserInfo( LOCALE_SMONTHOUSANDSEP,
  3343. (LPWSTR)lpLCData,
  3344. cchData ));
  3345. break;
  3346. }
  3347. case ( LOCALE_SMONGROUPING ) :
  3348. {
  3349. //
  3350. // Validate the new value. It should be no longer than
  3351. // MAX_SMONGROUPING wide characters in length and should
  3352. // contain alternating integer and semicolon values.
  3353. // (eg. 3;2;0 or 3;0 or 0)
  3354. //
  3355. // NOTE: The string may not be NULL, so it must be at least
  3356. // 2 chars long (includes null).
  3357. //
  3358. if (!IsValidGroupingString( lpLCData,
  3359. MAX_SMONGROUPING,
  3360. TRUE ))
  3361. {
  3362. SetLastError(ERROR_INVALID_PARAMETER);
  3363. return (FALSE);
  3364. }
  3365. //
  3366. // Set the registry with the new SMONGROUPING string.
  3367. //
  3368. return (SetUserInfo( LOCALE_SMONGROUPING,
  3369. (LPWSTR)lpLCData,
  3370. cchData ));
  3371. break;
  3372. }
  3373. case ( LOCALE_ICURRDIGITS ) :
  3374. {
  3375. //
  3376. // Validate the new value.
  3377. // The value should be between 0 and MAX_VALUE_ICURRDIGITS.
  3378. //
  3379. // NOTE: The string may not be NULL, so it must be at least
  3380. // 2 chars long (includes null).
  3381. //
  3382. RtlInitUnicodeString(&ObUnicodeStr, lpLCData);
  3383. if ((cchData < 2) ||
  3384. (RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
  3385. (Value < 0) || (Value > MAX_VALUE_ICURRDIGITS) ||
  3386. ((Value == 0) &&
  3387. ((*lpLCData != NLS_CHAR_ZERO) || (cchData != 2))))
  3388. {
  3389. SetLastError(ERROR_INVALID_PARAMETER);
  3390. return (FALSE);
  3391. }
  3392. //
  3393. // Set the registry with the new ICURRDIGITS string.
  3394. //
  3395. return (SetUserInfo( LOCALE_ICURRDIGITS,
  3396. (LPWSTR)lpLCData,
  3397. cchData ));
  3398. break;
  3399. }
  3400. case ( LOCALE_ICURRENCY ) :
  3401. {
  3402. //
  3403. // Validate the new value. It should be no longer than
  3404. // MAX_ICURRENCY wide characters in length.
  3405. // The value should be between 0 and MAX_VALUE_ICURRENCY.
  3406. //
  3407. // NOTE: The string may not be NULL, so it must be at least
  3408. // 2 chars long (includes null).
  3409. //
  3410. // Optimized - since MAX_ICURRENCY is 2.
  3411. //
  3412. if ((cchData != MAX_ICURRENCY) ||
  3413. (*lpLCData < NLS_CHAR_ZERO) ||
  3414. (*lpLCData > MAX_CHAR_ICURRENCY))
  3415. {
  3416. SetLastError(ERROR_INVALID_PARAMETER);
  3417. return (FALSE);
  3418. }
  3419. //
  3420. // Set the registry with the new ICURRENCY string.
  3421. //
  3422. return (SetUserInfo( LOCALE_ICURRENCY,
  3423. (LPWSTR)lpLCData,
  3424. cchData ));
  3425. break;
  3426. }
  3427. case ( LOCALE_INEGCURR ) :
  3428. {
  3429. //
  3430. // Validate the new value.
  3431. // The value should be between 0 and MAX_VALUE_INEGCURR.
  3432. //
  3433. // NOTE: The string may not be NULL, so it must be at least
  3434. // 2 chars long (includes null).
  3435. //
  3436. RtlInitUnicodeString(&ObUnicodeStr, lpLCData);
  3437. if ((cchData < 2) ||
  3438. (RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
  3439. (Value < 0) || (Value > MAX_VALUE_INEGCURR) ||
  3440. ((Value == 0) &&
  3441. ((*lpLCData != NLS_CHAR_ZERO) || (cchData != 2))))
  3442. {
  3443. SetLastError(ERROR_INVALID_PARAMETER);
  3444. return (FALSE);
  3445. }
  3446. //
  3447. // Set the registry with the new INEGCURR string.
  3448. //
  3449. return (SetUserInfo( LOCALE_INEGCURR,
  3450. (LPWSTR)lpLCData,
  3451. cchData ));
  3452. break;
  3453. }
  3454. case ( LOCALE_SPOSITIVESIGN ) :
  3455. {
  3456. //
  3457. // Validate the new value. It should be no longer than
  3458. // MAX_SPOSSIGN wide characters in length and should not
  3459. // contain any integer values (L'0' thru L'9').
  3460. //
  3461. if (!IsValidSeparatorString( lpLCData,
  3462. MAX_SPOSSIGN,
  3463. FALSE ))
  3464. {
  3465. SetLastError(ERROR_INVALID_PARAMETER);
  3466. return (FALSE);
  3467. }
  3468. //
  3469. // Set the registry with the new SPOSITIVESIGN string.
  3470. //
  3471. return (SetUserInfo( LOCALE_SPOSITIVESIGN,
  3472. (LPWSTR)lpLCData,
  3473. cchData ));
  3474. break;
  3475. }
  3476. case ( LOCALE_SNEGATIVESIGN ) :
  3477. {
  3478. //
  3479. // Validate the new value. It should be no longer than
  3480. // MAX_SNEGSIGN wide characters in length and should not
  3481. // contain any integer values (L'0' thru L'9').
  3482. //
  3483. if (!IsValidSeparatorString( lpLCData,
  3484. MAX_SNEGSIGN,
  3485. FALSE ))
  3486. {
  3487. SetLastError(ERROR_INVALID_PARAMETER);
  3488. return (FALSE);
  3489. }
  3490. //
  3491. // Set the registry with the new SNEGATIVESIGN string.
  3492. //
  3493. return (SetUserInfo( LOCALE_SNEGATIVESIGN,
  3494. (LPWSTR)lpLCData,
  3495. cchData ));
  3496. break;
  3497. }
  3498. case ( LOCALE_STIMEFORMAT ) :
  3499. {
  3500. BOOL bInsideQuotedString = FALSE;
  3501. //
  3502. // Validate the new value. It should be no longer than
  3503. // MAX_STIMEFORMAT wide characters in length.
  3504. //
  3505. // NOTE: The string may not be NULL, so it must be at least
  3506. // 2 chars long (includes null). This is checked below
  3507. // in the check for whether or not there is an hour
  3508. // delimeter.
  3509. //
  3510. if (cchData > MAX_STIMEFORMAT)
  3511. {
  3512. SetLastError(ERROR_INVALID_PARAMETER);
  3513. return (FALSE);
  3514. }
  3515. //
  3516. // NOTE: Must link the STIME, ITIME, ITLZERO, and
  3517. // ITIMEMARKPOSN values in the registry.
  3518. //
  3519. //
  3520. // Search for H or h, so that iTime and iTLZero can be
  3521. // set. If no H or h exists, return an error. Note: the
  3522. // combinations "hH" or "Hh" are invalid.
  3523. //
  3524. pPos = (LPWSTR)lpLCData;
  3525. while ((pPos = wcspbrk(pPos, L"Hh'")))
  3526. {
  3527. if (*pPos == L'\'')
  3528. {
  3529. //
  3530. // Enter or leave a quoted string.
  3531. //
  3532. bInsideQuotedString &= ~TRUE;
  3533. }
  3534. else if (*pPos == L'H')
  3535. {
  3536. //
  3537. // Found an H.
  3538. //
  3539. if (!bInsideQuotedString)
  3540. {
  3541. //
  3542. // Get the appropriate ITIME value.
  3543. //
  3544. Order = 1;
  3545. //
  3546. // Get the appropriate ITLZERO value.
  3547. //
  3548. if (*(pPos + 1) == L'H')
  3549. {
  3550. TLZero = 1;
  3551. break;
  3552. }
  3553. else if (*(pPos + 1) == L'h')
  3554. {
  3555. //
  3556. // Invalid combination.
  3557. //
  3558. pPos = NULL;
  3559. break;
  3560. }
  3561. else
  3562. {
  3563. TLZero = 0;
  3564. break;
  3565. }
  3566. }
  3567. }
  3568. else if (*pPos == L'h')
  3569. {
  3570. //
  3571. // Found an h.
  3572. //
  3573. if (!bInsideQuotedString)
  3574. {
  3575. //
  3576. // Get the appropriate ITIME value.
  3577. //
  3578. Order = 0;
  3579. //
  3580. // Get the appropriate ITLZERO value.
  3581. //
  3582. if (*(pPos + 1) == L'h')
  3583. {
  3584. TLZero = 1;
  3585. break;
  3586. }
  3587. else if (*(pPos + 1) == L'H')
  3588. {
  3589. //
  3590. // Invalid combination.
  3591. //
  3592. pPos = NULL;
  3593. break;
  3594. }
  3595. else
  3596. {
  3597. TLZero = 0;
  3598. break;
  3599. }
  3600. }
  3601. }
  3602. pPos++;
  3603. }
  3604. //
  3605. // If pPos == NULL, then one of two things happened:
  3606. // - reached the end of the string without finding "H" or "h"
  3607. // - found an invalid combination like "hH" or "Hh"
  3608. //
  3609. if (!pPos)
  3610. {
  3611. SetLastError(ERROR_INVALID_PARAMETER);
  3612. return (FALSE);
  3613. }
  3614. //
  3615. // Search for tt, so that ITIMEMARKPOSN can be
  3616. // set. If no tt exists, do not change the value.
  3617. //
  3618. bInsideQuotedString = FALSE;
  3619. pPos = (LPWSTR)lpLCData;
  3620. while ((pPos = wcspbrk(pPos, L"t'")))
  3621. {
  3622. if (*pPos == L'\'')
  3623. {
  3624. //
  3625. // Enter or leave a quoted string.
  3626. //
  3627. bInsideQuotedString &= ~TRUE;
  3628. }
  3629. else if (*(pPos + 1) == L't')
  3630. {
  3631. if (!bInsideQuotedString)
  3632. {
  3633. //
  3634. // The string "tt" is found.
  3635. //
  3636. break;
  3637. }
  3638. }
  3639. pPos++;
  3640. }
  3641. if (pPos)
  3642. {
  3643. //
  3644. // Get the appropriate ITIMEMARKPOSN value.
  3645. //
  3646. bInsideQuotedString = FALSE;
  3647. pPos2 = (LPWSTR)lpLCData;
  3648. while ((pPos2 = wcspbrk(pPos2, L"Hhmst'")))
  3649. {
  3650. if (*pPos == L'\'')
  3651. {
  3652. //
  3653. // Enter or leave a quoted string.
  3654. //
  3655. bInsideQuotedString &= ~TRUE;
  3656. }
  3657. else
  3658. {
  3659. if (!bInsideQuotedString)
  3660. {
  3661. //
  3662. // Get the appropriate ITIMEMARKPOSN value.
  3663. //
  3664. TimeMarkPosn = (pPos == pPos2) ? 1 : 0;
  3665. break;
  3666. }
  3667. }
  3668. pPos2++;
  3669. }
  3670. }
  3671. //
  3672. // Find the time separator so that STIME can be set.
  3673. //
  3674. bInsideQuotedString = FALSE;
  3675. pPos = (LPWSTR)lpLCData;
  3676. while (pPos = wcspbrk(pPos, L"Hhms'"))
  3677. {
  3678. if (*pPos == L'\'')
  3679. {
  3680. //
  3681. // Enter or leave a quoted string.
  3682. //
  3683. bInsideQuotedString &= ~TRUE;
  3684. pPos++;
  3685. }
  3686. else
  3687. {
  3688. if (!bInsideQuotedString)
  3689. {
  3690. //
  3691. // Look for the beginning of the time separator.
  3692. //
  3693. pPos++;
  3694. while ((*pPos) && (wcschr(L"Hhms", *pPos)))
  3695. {
  3696. pPos++;
  3697. }
  3698. //
  3699. // Look for the end of the time separator.
  3700. //
  3701. if (*pPos)
  3702. {
  3703. //
  3704. // Find the end of the separator string.
  3705. //
  3706. pPos2 = wcspbrk(pPos, L"Hhmst");
  3707. if (pPos2)
  3708. {
  3709. if (*pPos2 == L't')
  3710. {
  3711. //
  3712. // Found a time marker, so need to start
  3713. // over in search for separator. There
  3714. // are no separators around the time
  3715. // marker.
  3716. //
  3717. pPos = pPos2 + 1;
  3718. }
  3719. else
  3720. {
  3721. //
  3722. // Found end of separator, so break out of
  3723. // while loop.
  3724. //
  3725. break;
  3726. }
  3727. }
  3728. }
  3729. }
  3730. else
  3731. {
  3732. pPos++;
  3733. }
  3734. }
  3735. }
  3736. //
  3737. // Get the appropriate STIME string.
  3738. //
  3739. if (pPos)
  3740. {
  3741. //
  3742. // Copy to temp buffer so that it's zero terminated.
  3743. //
  3744. pString = pTemp;
  3745. while (pPos != pPos2)
  3746. {
  3747. //
  3748. // If there is a quoted string in the separator, then
  3749. // just put in a white space, since there is no meaning
  3750. // for time field separator anymore.
  3751. //
  3752. if (*pPos == L'\'')
  3753. {
  3754. pString = pTemp;
  3755. *pString++ = L' ';
  3756. break;
  3757. }
  3758. *pString = *pPos;
  3759. pPos++;
  3760. pString++;
  3761. }
  3762. *pString = 0;
  3763. }
  3764. else
  3765. {
  3766. //
  3767. // There is no time separator, so use NULL.
  3768. //
  3769. *pTemp = 0;
  3770. }
  3771. //
  3772. // Validate the new value. It should be no longer than
  3773. // MAX_STIME wide characters in length and should not
  3774. // contain any integer values (L'0' thru L'9').
  3775. //
  3776. // NOTE: The string may not be NULL, so it must be at least
  3777. // 2 chars long (includes null).
  3778. //
  3779. if (!IsValidSeparatorString( pTemp,
  3780. MAX_STIME,
  3781. TRUE ))
  3782. {
  3783. SetLastError(ERROR_INVALID_PARAMETER);
  3784. return (FALSE);
  3785. }
  3786. //
  3787. // Make sure that the time separator does NOT contain any
  3788. // of the special time picture characters - h, H, m, s, t, '.
  3789. //
  3790. if (wcspbrk(pTemp, L"Hhmst'"))
  3791. {
  3792. SetLastError(ERROR_INVALID_PARAMETER);
  3793. return (FALSE);
  3794. }
  3795. //
  3796. // Call the server to set the registry.
  3797. //
  3798. return (SetMultipleUserInfo( LCType,
  3799. cchData,
  3800. lpLCData,
  3801. pTemp,
  3802. (Order == 0) ? L"0" : L"1",
  3803. (TLZero == 0) ? L"0" : L"1",
  3804. (TimeMarkPosn == 0) ? L"0" : L"1" ));
  3805. break;
  3806. }
  3807. case ( LOCALE_STIME ) :
  3808. {
  3809. //
  3810. // NOTE: Must link the STIMEFORMAT value in the registry.
  3811. //
  3812. //
  3813. // Validate the new value. It should be no longer than
  3814. // MAX_STIME wide characters in length and should not
  3815. // contain any integer values (L'0' thru L'9').
  3816. //
  3817. // NOTE: The string may not be NULL, so it must be at least
  3818. // 2 chars long (includes null).
  3819. //
  3820. if (!IsValidSeparatorString( lpLCData,
  3821. MAX_STIME,
  3822. TRUE ))
  3823. {
  3824. SetLastError(ERROR_INVALID_PARAMETER);
  3825. return (FALSE);
  3826. }
  3827. //
  3828. // Make sure that the time separator does NOT contain any
  3829. // of the special time picture characters - h, H, m, s, t, '.
  3830. //
  3831. if (wcspbrk(lpLCData, L"Hhmst'"))
  3832. {
  3833. SetLastError(ERROR_INVALID_PARAMETER);
  3834. return (FALSE);
  3835. }
  3836. //
  3837. // Get the current setting for STIMEFORMAT.
  3838. //
  3839. if (GetUserInfo( Locale,
  3840. LOCALE_STIMEFORMAT,
  3841. FIELD_OFFSET(NLS_USER_INFO, sTimeFormat),
  3842. NLS_VALUE_STIMEFORMAT,
  3843. pOutput,
  3844. ARRAYSIZE(pOutput),
  3845. TRUE ))
  3846. {
  3847. pString = pOutput;
  3848. }
  3849. else
  3850. {
  3851. pString = (LPWORD)(pHashN->pLocaleHdr) +
  3852. pHashN->pLocaleHdr->STimeFormat;
  3853. }
  3854. //
  3855. // Get the current setting for STIME.
  3856. //
  3857. if (GetUserInfo( Locale,
  3858. LCType,
  3859. FIELD_OFFSET(NLS_USER_INFO, sTime),
  3860. NLS_VALUE_STIME,
  3861. pOutput2,
  3862. ARRAYSIZE(pOutput2),
  3863. TRUE ))
  3864. {
  3865. pSep = pOutput2;
  3866. }
  3867. else
  3868. {
  3869. pSep = (LPWORD)(pHashN->pLocaleHdr) +
  3870. pHashN->pLocaleHdr->STime;
  3871. }
  3872. //
  3873. // Get the length of the separator string.
  3874. //
  3875. SepLen = NlsStrLenW(pSep);
  3876. //
  3877. // Setup the string containing the characters to find in
  3878. // the timeformat string.
  3879. //
  3880. pFind[0] = NLS_CHAR_QUOTE;
  3881. pFind[1] = *pSep;
  3882. pFind[2] = 0;
  3883. //
  3884. // Find the time separator in the STIMEFORMAT string and
  3885. // replace it with the new time separator.
  3886. //
  3887. // The new separator may be a different length than
  3888. // the old one, so must use a static buffer for the new
  3889. // time format string.
  3890. //
  3891. pPos = pTemp;
  3892. while (pPos2 = wcspbrk(pString, pFind))
  3893. {
  3894. //
  3895. // Copy format string up to pPos2.
  3896. //
  3897. while (pString < pPos2)
  3898. {
  3899. *pPos = *pString;
  3900. pPos++;
  3901. pString++;
  3902. }
  3903. switch (*pPos2)
  3904. {
  3905. case ( NLS_CHAR_QUOTE ) :
  3906. {
  3907. //
  3908. // Copy the quote.
  3909. //
  3910. *pPos = *pString;
  3911. pPos++;
  3912. pString++;
  3913. //
  3914. // Copy what's inside the quotes.
  3915. //
  3916. while ((*pString) && (*pString != NLS_CHAR_QUOTE))
  3917. {
  3918. *pPos = *pString;
  3919. pPos++;
  3920. pString++;
  3921. }
  3922. //
  3923. // Copy the end quote.
  3924. //
  3925. *pPos = NLS_CHAR_QUOTE;
  3926. pPos++;
  3927. if (*pString)
  3928. {
  3929. pString++;
  3930. }
  3931. break;
  3932. }
  3933. default :
  3934. {
  3935. //
  3936. // Make sure it's the old separator.
  3937. //
  3938. if (NlsStrNEqualW(pString, pSep, SepLen))
  3939. {
  3940. //
  3941. // Adjust pointer to skip over old separator.
  3942. //
  3943. pString += SepLen;
  3944. //
  3945. // Copy the new separator.
  3946. //
  3947. pPos2 = (LPWSTR)lpLCData;
  3948. while (*pPos2)
  3949. {
  3950. *pPos = *pPos2;
  3951. pPos++;
  3952. pPos2++;
  3953. }
  3954. }
  3955. else
  3956. {
  3957. //
  3958. // Copy the code point and continue.
  3959. //
  3960. *pPos = *pString;
  3961. pPos++;
  3962. pString++;
  3963. }
  3964. break;
  3965. }
  3966. }
  3967. }
  3968. //
  3969. // Copy to the end of the string and null terminate it.
  3970. //
  3971. while (*pString)
  3972. {
  3973. *pPos = *pString;
  3974. pPos++;
  3975. pString++;
  3976. }
  3977. *pPos = 0;
  3978. //
  3979. // Call the server to set the registry.
  3980. //
  3981. return (SetMultipleUserInfo( LCType,
  3982. cchData,
  3983. pTemp,
  3984. lpLCData,
  3985. NULL,
  3986. NULL,
  3987. NULL ));
  3988. break;
  3989. }
  3990. case ( LOCALE_ITIME ) :
  3991. {
  3992. //
  3993. // NOTE: Must link the STIMEFORMAT value in the registry.
  3994. //
  3995. //
  3996. // Validate the new value. It should be no longer than
  3997. // MAX_ITIME wide characters in length.
  3998. // The value should be either 0 or MAX_VALUE_ITIME.
  3999. //
  4000. // NOTE: The string may not be NULL, so it must be at least
  4001. // 2 chars long (includes null).
  4002. //
  4003. // Optimized - since MAX_ITIME is 2.
  4004. //
  4005. if ((cchData != MAX_ITIME) ||
  4006. (*lpLCData < NLS_CHAR_ZERO) ||
  4007. (*lpLCData > MAX_CHAR_ITIME))
  4008. {
  4009. SetLastError(ERROR_INVALID_PARAMETER);
  4010. return (FALSE);
  4011. }
  4012. //
  4013. // Get the current setting for STIMEFORMAT.
  4014. //
  4015. if (GetUserInfo( Locale,
  4016. LOCALE_STIMEFORMAT,
  4017. FIELD_OFFSET(NLS_USER_INFO, sTimeFormat),
  4018. NLS_VALUE_STIMEFORMAT,
  4019. pOutput,
  4020. ARRAYSIZE(pOutput),
  4021. TRUE ))
  4022. {
  4023. pString = pOutput;
  4024. }
  4025. else
  4026. {
  4027. //
  4028. // Copy system default to temp buffer.
  4029. //
  4030. if(FAILED( StringCchCopyW( pTemp,
  4031. ARRAYSIZE(pTemp),
  4032. (LPWORD)(pHashN->pLocaleHdr) +
  4033. pHashN->pLocaleHdr->STimeFormat ) ))
  4034. {
  4035. //
  4036. // Failure should in theory be impossible, but if we ignore the
  4037. // return value, PREfast will complain.
  4038. //
  4039. SetLastError(ERROR_OUTOFMEMORY);
  4040. return(FALSE);
  4041. }
  4042. pString = pTemp;
  4043. }
  4044. //
  4045. // Search down the STIMEFORMAT string.
  4046. // If iTime = 0, then H -> h.
  4047. // If iTime = 1, then h -> H.
  4048. //
  4049. pPos = pString;
  4050. if (*lpLCData == NLS_CHAR_ZERO)
  4051. {
  4052. while (*pPos)
  4053. {
  4054. if (*pPos == L'H')
  4055. {
  4056. *pPos = L'h';
  4057. }
  4058. pPos++;
  4059. }
  4060. }
  4061. else
  4062. {
  4063. while (*pPos)
  4064. {
  4065. if (*pPos == L'h')
  4066. {
  4067. *pPos = L'H';
  4068. }
  4069. pPos++;
  4070. }
  4071. }
  4072. //
  4073. // Call the server to set the registry.
  4074. //
  4075. return (SetMultipleUserInfo( LCType,
  4076. cchData,
  4077. pString,
  4078. NULL,
  4079. lpLCData,
  4080. NULL,
  4081. NULL ));
  4082. break;
  4083. }
  4084. case ( LOCALE_S1159 ) :
  4085. {
  4086. //
  4087. // Validate the new value. It should be no longer than
  4088. // MAX_S1159 wide characters in length.
  4089. //
  4090. if (cchData > MAX_S1159)
  4091. {
  4092. SetLastError(ERROR_INVALID_PARAMETER);
  4093. return (FALSE);
  4094. }
  4095. //
  4096. // Set the registry with the new S1159 string.
  4097. //
  4098. return (SetUserInfo( LOCALE_S1159,
  4099. (LPWSTR)lpLCData,
  4100. cchData ));
  4101. break;
  4102. }
  4103. case ( LOCALE_S2359 ) :
  4104. {
  4105. //
  4106. // Validate the new value. It should be no longer than
  4107. // MAX_S2359 wide characters in length.
  4108. //
  4109. if (cchData > MAX_S2359)
  4110. {
  4111. SetLastError(ERROR_INVALID_PARAMETER);
  4112. return (FALSE);
  4113. }
  4114. //
  4115. // Set the registry with the new S2359 string.
  4116. //
  4117. return (SetUserInfo( LOCALE_S2359,
  4118. (LPWSTR)lpLCData,
  4119. cchData ));
  4120. break;
  4121. }
  4122. case ( LOCALE_SSHORTDATE ) :
  4123. {
  4124. //
  4125. // Validate the new value. It should be no longer than
  4126. // MAX_SSHORTDATE wide characters in length.
  4127. //
  4128. // NOTE: The string may not be NULL, so it must be at least
  4129. // 2 chars long (includes null). This is checked below
  4130. // in the check for whether or not there is a date,
  4131. // month, or year delimeter.
  4132. //
  4133. if (cchData > MAX_SSHORTDATE)
  4134. {
  4135. SetLastError(ERROR_INVALID_PARAMETER);
  4136. return (FALSE);
  4137. }
  4138. //
  4139. // NOTE: Must link the IDATE and SDATE values in the registry.
  4140. //
  4141. //
  4142. // Search for the 'd' or 'M' or 'y' sequence in the date format
  4143. // string to set the new IDATE value.
  4144. //
  4145. // If none of these symbols exist in the date format string,
  4146. // then return an error.
  4147. //
  4148. pPos = wcspbrk(lpLCData, L"dMy");
  4149. if (!pPos)
  4150. {
  4151. SetLastError(ERROR_INVALID_PARAMETER);
  4152. return (FALSE);
  4153. }
  4154. //
  4155. // Set the registry with the appropriate IDATE string.
  4156. //
  4157. switch (*pPos)
  4158. {
  4159. case ( L'M' ) :
  4160. {
  4161. Order = 0;
  4162. break;
  4163. }
  4164. case ( L'd' ) :
  4165. {
  4166. Order = 1;
  4167. break;
  4168. }
  4169. case ( L'y' ) :
  4170. {
  4171. Order = 2;
  4172. break;
  4173. }
  4174. }
  4175. //
  4176. // Set the registry with the appropriate SDATE string.
  4177. //
  4178. // The ptr "pPos" is pointing at either d, M, or y.
  4179. // Go to the next position past sequence of d, M, or y.
  4180. //
  4181. pPos++;
  4182. while ((*pPos) && (wcschr( L"dMy", *pPos )))
  4183. {
  4184. pPos++;
  4185. }
  4186. *pTemp = 0;
  4187. if (*pPos)
  4188. {
  4189. //
  4190. // Find the end of the separator string.
  4191. //
  4192. pPos2 = wcspbrk(pPos, L"dMy");
  4193. if (pPos2)
  4194. {
  4195. //
  4196. // Copy to temp buffer so that it's zero terminated.
  4197. //
  4198. pString = pTemp;
  4199. while (pPos != pPos2)
  4200. {
  4201. //
  4202. // If there is a quoted string in the separator, then
  4203. // just punch in a white space, since there is no meaning
  4204. // for short date field separator anymore.
  4205. //
  4206. if (*pPos == L'\'')
  4207. {
  4208. pString = pTemp;
  4209. *pString++ = L' ';
  4210. break;
  4211. }
  4212. *pString = *pPos;
  4213. pPos++;
  4214. pString++;
  4215. }
  4216. *pString = 0;
  4217. }
  4218. }
  4219. //
  4220. // Since the date separator (LOCALE_SDATE) is being set here, we
  4221. // should do the same validation as LOCALE_SDATE.
  4222. //
  4223. if (!IsValidSeparatorString( pTemp,
  4224. MAX_SDATE,
  4225. TRUE ))
  4226. {
  4227. SetLastError(ERROR_INVALID_PARAMETER);
  4228. return (FALSE);
  4229. }
  4230. //
  4231. // Make sure that the date separator does NOT contain any
  4232. // of the special date picture characters - d, M, y, g, '.
  4233. //
  4234. if (wcspbrk(pTemp, L"dMyg'"))
  4235. {
  4236. SetLastError(ERROR_INVALID_PARAMETER);
  4237. return (FALSE);
  4238. }
  4239. //
  4240. // Call the server to set the registry.
  4241. //
  4242. return (SetMultipleUserInfo( LCType,
  4243. cchData,
  4244. lpLCData,
  4245. pTemp,
  4246. (Order == 0) ? L"0" :
  4247. ((Order == 1) ? L"1" : L"2"),
  4248. NULL,
  4249. NULL ));
  4250. break;
  4251. }
  4252. case ( LOCALE_SDATE ) :
  4253. {
  4254. //
  4255. // NOTE: Must link the SSHORTDATE value in the registry.
  4256. //
  4257. //
  4258. // Validate the new value. It should be no longer than
  4259. // MAX_SDATE wide characters in length and should not
  4260. // contain any integer values (L'0' thru L'9').
  4261. //
  4262. // NOTE: The string may not be NULL, so it must be at least
  4263. // 2 chars long (includes null).
  4264. //
  4265. if (!IsValidSeparatorString( lpLCData,
  4266. MAX_SDATE,
  4267. TRUE ))
  4268. {
  4269. SetLastError(ERROR_INVALID_PARAMETER);
  4270. return (FALSE);
  4271. }
  4272. //
  4273. // Make sure that the date separator does NOT contain any
  4274. // of the special date picture characters - d, M, y, g, '.
  4275. //
  4276. if (wcspbrk(lpLCData, L"dMyg'"))
  4277. {
  4278. SetLastError(ERROR_INVALID_PARAMETER);
  4279. return (FALSE);
  4280. }
  4281. //
  4282. // Get the current setting for SSHORTDATE.
  4283. //
  4284. if (GetUserInfo( Locale,
  4285. LOCALE_SSHORTDATE,
  4286. FIELD_OFFSET(NLS_USER_INFO, sShortDate),
  4287. NLS_VALUE_SSHORTDATE,
  4288. pOutput,
  4289. ARRAYSIZE(pOutput),
  4290. TRUE ))
  4291. {
  4292. pString = pOutput;
  4293. }
  4294. else
  4295. {
  4296. pString = (LPWORD)(pHashN->pLocaleHdr) +
  4297. pHashN->pLocaleHdr->SShortDate;
  4298. }
  4299. //
  4300. // Get the current setting for SDATE.
  4301. //
  4302. if (GetUserInfo( Locale,
  4303. LCType,
  4304. FIELD_OFFSET(NLS_USER_INFO, sDate),
  4305. NLS_VALUE_SDATE,
  4306. pOutput2,
  4307. ARRAYSIZE(pOutput2),
  4308. TRUE ))
  4309. {
  4310. pSep = pOutput2;
  4311. }
  4312. else
  4313. {
  4314. pSep = (LPWORD)(pHashN->pLocaleHdr) +
  4315. pHashN->pLocaleHdr->SDate;
  4316. }
  4317. //
  4318. // Get the length of the separator string.
  4319. //
  4320. SepLen = NlsStrLenW(pSep);
  4321. //
  4322. // Setup the string containing the characters to find in
  4323. // the shortdate string.
  4324. //
  4325. pFind[0] = NLS_CHAR_QUOTE;
  4326. pFind[1] = *pSep;
  4327. pFind[2] = 0;
  4328. //
  4329. // Find the date separator in the SSHORTDATE string and
  4330. // replace it with the new date separator.
  4331. //
  4332. // The new separator may be a different length than
  4333. // the old one, so must use a static buffer for the new
  4334. // short date format string.
  4335. //
  4336. pPos = pTemp;
  4337. while (pPos2 = wcspbrk(pString, pFind))
  4338. {
  4339. //
  4340. // Copy format string up to pPos2.
  4341. //
  4342. while (pString < pPos2)
  4343. {
  4344. *pPos = *pString;
  4345. pPos++;
  4346. pString++;
  4347. }
  4348. switch (*pPos2)
  4349. {
  4350. case ( NLS_CHAR_QUOTE ) :
  4351. {
  4352. //
  4353. // Copy the quote.
  4354. //
  4355. *pPos = *pString;
  4356. pPos++;
  4357. pString++;
  4358. //
  4359. // Copy what's inside the quotes.
  4360. //
  4361. while ((*pString) && (*pString != NLS_CHAR_QUOTE))
  4362. {
  4363. *pPos = *pString;
  4364. pPos++;
  4365. pString++;
  4366. }
  4367. //
  4368. // Copy the end quote.
  4369. //
  4370. *pPos = NLS_CHAR_QUOTE;
  4371. pPos++;
  4372. if (*pString)
  4373. {
  4374. pString++;
  4375. }
  4376. break;
  4377. }
  4378. default :
  4379. {
  4380. //
  4381. // Make sure it's the old separator.
  4382. //
  4383. if (NlsStrNEqualW(pString, pSep, SepLen))
  4384. {
  4385. //
  4386. // Adjust pointer to skip over old separator.
  4387. //
  4388. pString += SepLen;
  4389. //
  4390. // Copy the new separator.
  4391. //
  4392. pPos2 = (LPWSTR)lpLCData;
  4393. while (*pPos2)
  4394. {
  4395. *pPos = *pPos2;
  4396. pPos++;
  4397. pPos2++;
  4398. }
  4399. }
  4400. else
  4401. {
  4402. //
  4403. // Copy the code point and continue.
  4404. //
  4405. *pPos = *pString;
  4406. pPos++;
  4407. pString++;
  4408. }
  4409. break;
  4410. }
  4411. }
  4412. }
  4413. //
  4414. // Copy to the end of the string and null terminate it.
  4415. //
  4416. while (*pString)
  4417. {
  4418. *pPos = *pString;
  4419. pPos++;
  4420. pString++;
  4421. }
  4422. *pPos = 0;
  4423. //
  4424. // Call the server to set the registry.
  4425. //
  4426. return (SetMultipleUserInfo( LCType,
  4427. cchData,
  4428. pTemp,
  4429. lpLCData,
  4430. NULL,
  4431. NULL,
  4432. NULL ));
  4433. break;
  4434. }
  4435. case ( LOCALE_SYEARMONTH ) :
  4436. {
  4437. //
  4438. // Validate the new value. It should be no longer than
  4439. // MAX_SYEARMONTH wide characters in length.
  4440. //
  4441. // NOTE: The string may not be NULL, so it must be at least
  4442. // 2 chars long (includes null). This is checked below
  4443. // in the check for whether or not there is a date,
  4444. // month, or year delimeter.
  4445. //
  4446. if (cchData > MAX_SYEARMONTH)
  4447. {
  4448. SetLastError(ERROR_INVALID_PARAMETER);
  4449. return (FALSE);
  4450. }
  4451. //
  4452. // Make sure one of 'M' or 'y' exists in the date
  4453. // format string. If it does not, then return an error.
  4454. //
  4455. pPos = wcspbrk(lpLCData, L"My");
  4456. if (!pPos)
  4457. {
  4458. SetLastError(ERROR_INVALID_PARAMETER);
  4459. return (FALSE);
  4460. }
  4461. //
  4462. // Set the registry with the new SYEARMONTH string.
  4463. //
  4464. return (SetUserInfo( LOCALE_SYEARMONTH,
  4465. (LPWSTR)lpLCData,
  4466. cchData ));
  4467. break;
  4468. }
  4469. case ( LOCALE_SLONGDATE ) :
  4470. {
  4471. //
  4472. // Validate the new value. It should be no longer than
  4473. // MAX_SLONGDATE wide characters in length.
  4474. //
  4475. // NOTE: The string may not be NULL, so it must be at least
  4476. // 2 chars long (includes null). This is checked below
  4477. // in the check for whether or not there is a date,
  4478. // month, or year delimeter.
  4479. //
  4480. if (cchData > MAX_SLONGDATE)
  4481. {
  4482. SetLastError(ERROR_INVALID_PARAMETER);
  4483. return (FALSE);
  4484. }
  4485. //
  4486. // Make sure one of 'd' or 'M' or 'y' exists in the date
  4487. // format string. If it does not, then return an error.
  4488. //
  4489. pPos = wcspbrk(lpLCData, L"dMy");
  4490. if (!pPos)
  4491. {
  4492. SetLastError(ERROR_INVALID_PARAMETER);
  4493. return (FALSE);
  4494. }
  4495. //
  4496. // Set the registry with the new SLONGDATE string.
  4497. //
  4498. return (SetUserInfo( LOCALE_SLONGDATE,
  4499. (LPWSTR)lpLCData,
  4500. cchData ));
  4501. break;
  4502. }
  4503. case ( LOCALE_ICALENDARTYPE ) :
  4504. {
  4505. //
  4506. // Validate the new value. It should be no longer than
  4507. // MAX_ICALTYPE wide characters in length.
  4508. //
  4509. // NOTE: The string may not be NULL, so it must be at least
  4510. // 2 chars long (includes null).
  4511. //
  4512. if ((cchData < 2) || (cchData > MAX_ICALTYPE) ||
  4513. (!IsValidCalendarTypeStr(pHashN, lpLCData)))
  4514. {
  4515. SetLastError(ERROR_INVALID_PARAMETER);
  4516. return (FALSE);
  4517. }
  4518. //
  4519. // Set the registry with the new ICALENDARTYPE string.
  4520. //
  4521. return (SetUserInfo( LOCALE_ICALENDARTYPE,
  4522. (LPWSTR)lpLCData,
  4523. cchData ));
  4524. break;
  4525. }
  4526. case ( LOCALE_IFIRSTDAYOFWEEK ) :
  4527. {
  4528. //
  4529. // Validate the new value. It should be no longer than
  4530. // MAX_IFIRSTDAY wide characters in length.
  4531. // The value should be between 0 and MAX_VALUE_IFIRSTDAY.
  4532. //
  4533. // NOTE: The string may not be NULL, so it must be at least
  4534. // 2 chars long (includes null).
  4535. //
  4536. // Optimized - since MAX_IFIRSTDAY is 2.
  4537. //
  4538. if ((cchData != MAX_IFIRSTDAY) ||
  4539. (*lpLCData < NLS_CHAR_ZERO) ||
  4540. (*lpLCData > MAX_CHAR_IFIRSTDAY))
  4541. {
  4542. SetLastError(ERROR_INVALID_PARAMETER);
  4543. return (FALSE);
  4544. }
  4545. //
  4546. // Set the registry with the new IFIRSTDAYOFWEEK string.
  4547. //
  4548. return (SetUserInfo( LOCALE_IFIRSTDAYOFWEEK,
  4549. (LPWSTR)lpLCData,
  4550. cchData ));
  4551. break;
  4552. }
  4553. case ( LOCALE_IFIRSTWEEKOFYEAR ) :
  4554. {
  4555. //
  4556. // Validate the new value. It should be no longer than
  4557. // MAX_IFIRSTWEEK wide characters in length.
  4558. // The value should be between 0 and MAX_VALUE_IFIRSTWEEK.
  4559. //
  4560. // NOTE: The string may not be NULL, so it must be at least
  4561. // 2 chars long (includes null).
  4562. //
  4563. // Optimized - since MAX_IFIRSTWEEK is 2.
  4564. //
  4565. if ((cchData != MAX_IFIRSTWEEK) ||
  4566. (*lpLCData < NLS_CHAR_ZERO) ||
  4567. (*lpLCData > MAX_CHAR_IFIRSTWEEK))
  4568. {
  4569. SetLastError(ERROR_INVALID_PARAMETER);
  4570. return (FALSE);
  4571. }
  4572. //
  4573. // Set the registry with the new IFIRSTWEEKOFYEAR string.
  4574. //
  4575. return (SetUserInfo( LOCALE_IFIRSTWEEKOFYEAR,
  4576. (LPWSTR)lpLCData,
  4577. cchData ));
  4578. break;
  4579. }
  4580. default :
  4581. {
  4582. SetLastError(ERROR_INVALID_FLAGS);
  4583. return (FALSE);
  4584. }
  4585. }
  4586. //
  4587. // Return success.
  4588. //
  4589. return (TRUE);
  4590. }
  4591. ////////////////////////////////////////////////////////////////////////////
  4592. //
  4593. // GetCalendarInfoW
  4594. //
  4595. // Returns one of the various pieces of information about a particular
  4596. // calendar by querying the configuration registry. This call also
  4597. // indicates how much memory is necessary to contain the desired
  4598. // information.
  4599. //
  4600. // 12-17-97 JulieB Created.
  4601. ////////////////////////////////////////////////////////////////////////////
  4602. int WINAPI GetCalendarInfoW(
  4603. LCID Locale,
  4604. CALID Calendar,
  4605. CALTYPE CalType,
  4606. LPWSTR lpCalData,
  4607. int cchData,
  4608. LPDWORD lpValue)
  4609. {
  4610. PLOC_HASH pHashN; // ptr to LOC hash node
  4611. int Length = 0; // length of info string
  4612. LPWSTR pString; // ptr to the info string
  4613. BOOL UserOverride = TRUE; // use user override
  4614. BOOL ReturnNum = FALSE; // return number instead of string
  4615. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  4616. UNICODE_STRING ObUnicodeStr; // value string
  4617. int Base = 0; // base for str to int conversion
  4618. LPWSTR pOptCal; // ptr to optional calendar values
  4619. PCAL_INFO pCalInfo; // ptr to calendar info
  4620. //
  4621. // Invalid Parameter Check:
  4622. // - validate LCID
  4623. // - count is negative
  4624. // - NULL data pointer AND count is not zero
  4625. //
  4626. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  4627. if ((pHashN == NULL) ||
  4628. (cchData < 0) ||
  4629. ((lpCalData == NULL) && (cchData != 0)))
  4630. {
  4631. SetLastError(ERROR_INVALID_PARAMETER);
  4632. return (0);
  4633. }
  4634. //
  4635. // Need to check the parameters based on the CAL_RETURN_NUMBER
  4636. // CalType.
  4637. //
  4638. if (CalType & CAL_RETURN_NUMBER)
  4639. {
  4640. if ((lpCalData != NULL) || (cchData != 0) || (lpValue == NULL))
  4641. {
  4642. SetLastError(ERROR_INVALID_PARAMETER);
  4643. return (0);
  4644. }
  4645. }
  4646. else
  4647. {
  4648. if ((lpValue != NULL) ||
  4649. (cchData < 0) ||
  4650. ((lpCalData == NULL) && (cchData != 0)))
  4651. {
  4652. SetLastError(ERROR_INVALID_PARAMETER);
  4653. return (0);
  4654. }
  4655. }
  4656. //
  4657. // Check for NO USER OVERRIDE flag and remove the USE CP ACP flag.
  4658. //
  4659. if (CalType & CAL_NOUSEROVERRIDE)
  4660. {
  4661. //
  4662. // Flag is set, so set the boolean value and remove the flag
  4663. // from the CalType parameter (for switch statement).
  4664. //
  4665. UserOverride = FALSE;
  4666. }
  4667. if (CalType & CAL_RETURN_NUMBER)
  4668. {
  4669. //
  4670. // Flag is set, so set the boolean value and remove the flag
  4671. // from the CalType parameter (for switch statement).
  4672. //
  4673. ReturnNum = TRUE;
  4674. }
  4675. CalType &= (~(CAL_NOUSEROVERRIDE | CAL_USE_CP_ACP | CAL_RETURN_NUMBER));
  4676. //
  4677. // Validate the Calendar parameter.
  4678. //
  4679. if (((CalType != CAL_ITWODIGITYEARMAX) &&
  4680. ((pOptCal = IsValidCalendarType(pHashN, Calendar)) == NULL)) ||
  4681. (GetCalendar(Calendar, &pCalInfo) != NO_ERROR))
  4682. {
  4683. SetLastError(ERROR_INVALID_PARAMETER);
  4684. return (0);
  4685. }
  4686. //
  4687. // Return the appropriate information for the given CALTYPE.
  4688. // If user information exists for the given CALTYPE, then
  4689. // the user default is returned instead of the system default.
  4690. //
  4691. switch (CalType)
  4692. {
  4693. case ( CAL_ICALINTVALUE ) :
  4694. {
  4695. Base = 10;
  4696. //
  4697. // Get the integer value for the given calendar.
  4698. //
  4699. pString = ((POPT_CAL)pOptCal)->pCalStr;
  4700. break;
  4701. }
  4702. case ( CAL_SCALNAME ) :
  4703. {
  4704. //
  4705. // Get the calendar name for the given calendar.
  4706. //
  4707. pString = ((POPT_CAL)pOptCal)->pCalStr +
  4708. NlsStrLenW(((POPT_CAL)pOptCal)->pCalStr) + 1;
  4709. break;
  4710. }
  4711. case ( CAL_ITWODIGITYEARMAX ) :
  4712. {
  4713. Base = 10;
  4714. //
  4715. // Check if a policy is enforced for the current user,
  4716. // and if so, let's use it.
  4717. //
  4718. if (GetTwoDigitYearInfo(Calendar, pTemp, ARRAYSIZE(pTemp), NLS_POLICY_TWO_DIGIT_YEAR_KEY))
  4719. {
  4720. pString = pTemp;
  4721. }
  4722. else
  4723. {
  4724. if (UserOverride &&
  4725. GetTwoDigitYearInfo(Calendar, pTemp, ARRAYSIZE(pTemp), NLS_TWO_DIGIT_YEAR_KEY))
  4726. {
  4727. pString = pTemp;
  4728. }
  4729. else
  4730. {
  4731. //
  4732. // Use the default.
  4733. //
  4734. pString = (LPWORD)pCalInfo +
  4735. (((PCALENDAR_VAR)pCalInfo)->STwoDigitYearMax);
  4736. }
  4737. }
  4738. break;
  4739. }
  4740. case ( CAL_IYEAROFFSETRANGE ) :
  4741. {
  4742. Base = 10;
  4743. //
  4744. // Get the pointer to the appropriate calendar string.
  4745. //
  4746. pString = (LPWORD)pCalInfo +
  4747. (((PCALENDAR_VAR)pCalInfo)->SEraRanges);
  4748. //
  4749. // Make sure the string is NOT empty.
  4750. //
  4751. if (*pString)
  4752. {
  4753. pString = ((PERA_RANGE)pString)->pYearStr;
  4754. }
  4755. else
  4756. {
  4757. pString = L"0";
  4758. }
  4759. break;
  4760. }
  4761. case ( CAL_SERASTRING ) :
  4762. {
  4763. //
  4764. // Get the pointer to the appropriate calendar string.
  4765. //
  4766. pString = (LPWORD)pCalInfo +
  4767. (((PCALENDAR_VAR)pCalInfo)->SEraRanges);
  4768. //
  4769. // Make sure the string is NOT empty. If it is, return the
  4770. // empty string.
  4771. //
  4772. if (*pString)
  4773. {
  4774. pString = ((PERA_RANGE)pString)->pYearStr +
  4775. NlsStrLenW(((PERA_RANGE)pString)->pYearStr) + 1;
  4776. }
  4777. break;
  4778. }
  4779. case ( CAL_SSHORTDATE ) :
  4780. {
  4781. //
  4782. // Get the pointer to the appropriate calendar string.
  4783. //
  4784. pString = (LPWORD)pCalInfo +
  4785. (((PCALENDAR_VAR)pCalInfo)->SShortDate);
  4786. //
  4787. // Make sure the string is NOT empty. If it is, use the
  4788. // locale's short date string.
  4789. //
  4790. if (*pString == 0)
  4791. {
  4792. pString = (LPWORD)(pHashN->pLocaleHdr) +
  4793. pHashN->pLocaleHdr->SShortDate;
  4794. }
  4795. break;
  4796. }
  4797. case ( CAL_SLONGDATE ) :
  4798. {
  4799. //
  4800. // Get the pointer to the appropriate calendar string.
  4801. //
  4802. pString = (LPWORD)pCalInfo +
  4803. (((PCALENDAR_VAR)pCalInfo)->SLongDate);
  4804. //
  4805. // Make sure the string is NOT empty. If it is, use the
  4806. // locale's long date string.
  4807. //
  4808. if (*pString == 0)
  4809. {
  4810. pString = (LPWORD)(pHashN->pLocaleHdr) +
  4811. pHashN->pLocaleHdr->SLongDate;
  4812. }
  4813. break;
  4814. }
  4815. case ( CAL_SYEARMONTH ) :
  4816. {
  4817. //
  4818. // Get the pointer to the appropriate calendar string.
  4819. //
  4820. pString = (LPWORD)pCalInfo +
  4821. (((PCALENDAR_VAR)pCalInfo)->SYearMonth);
  4822. //
  4823. // Make sure the string is NOT empty. If it is, use the
  4824. // locale's year month string.
  4825. //
  4826. if (*pString == 0)
  4827. {
  4828. pString = (LPWORD)(pHashN->pLocaleHdr) +
  4829. pHashN->pLocaleHdr->SYearMonth;
  4830. }
  4831. break;
  4832. }
  4833. case ( CAL_SDAYNAME1 ) :
  4834. case ( CAL_SDAYNAME2 ) :
  4835. case ( CAL_SDAYNAME3 ) :
  4836. case ( CAL_SDAYNAME4 ) :
  4837. case ( CAL_SDAYNAME5 ) :
  4838. case ( CAL_SDAYNAME6 ) :
  4839. case ( CAL_SDAYNAME7 ) :
  4840. case ( CAL_SABBREVDAYNAME1 ) :
  4841. case ( CAL_SABBREVDAYNAME2 ) :
  4842. case ( CAL_SABBREVDAYNAME3 ) :
  4843. case ( CAL_SABBREVDAYNAME4 ) :
  4844. case ( CAL_SABBREVDAYNAME5 ) :
  4845. case ( CAL_SABBREVDAYNAME6 ) :
  4846. case ( CAL_SABBREVDAYNAME7 ) :
  4847. case ( CAL_SMONTHNAME1 ) :
  4848. case ( CAL_SMONTHNAME2 ) :
  4849. case ( CAL_SMONTHNAME3 ) :
  4850. case ( CAL_SMONTHNAME4 ) :
  4851. case ( CAL_SMONTHNAME5 ) :
  4852. case ( CAL_SMONTHNAME6 ) :
  4853. case ( CAL_SMONTHNAME7 ) :
  4854. case ( CAL_SMONTHNAME8 ) :
  4855. case ( CAL_SMONTHNAME9 ) :
  4856. case ( CAL_SMONTHNAME10 ) :
  4857. case ( CAL_SMONTHNAME11 ) :
  4858. case ( CAL_SMONTHNAME12 ) :
  4859. case ( CAL_SMONTHNAME13 ) :
  4860. case ( CAL_SABBREVMONTHNAME1 ) :
  4861. case ( CAL_SABBREVMONTHNAME2 ) :
  4862. case ( CAL_SABBREVMONTHNAME3 ) :
  4863. case ( CAL_SABBREVMONTHNAME4 ) :
  4864. case ( CAL_SABBREVMONTHNAME5 ) :
  4865. case ( CAL_SABBREVMONTHNAME6 ) :
  4866. case ( CAL_SABBREVMONTHNAME7 ) :
  4867. case ( CAL_SABBREVMONTHNAME8 ) :
  4868. case ( CAL_SABBREVMONTHNAME9 ) :
  4869. case ( CAL_SABBREVMONTHNAME10 ) :
  4870. case ( CAL_SABBREVMONTHNAME11 ) :
  4871. case ( CAL_SABBREVMONTHNAME12 ) :
  4872. case ( CAL_SABBREVMONTHNAME13 ) :
  4873. {
  4874. //
  4875. // Get the pointer to the appropriate calendar string if the
  4876. // IfNames flag is set for the calendar.
  4877. //
  4878. pString = NULL;
  4879. if (((PCALENDAR_VAR)pCalInfo)->IfNames)
  4880. {
  4881. pString = (LPWORD)pCalInfo +
  4882. *((LPWORD)((LPBYTE)(pCalInfo) +
  4883. (FIELD_OFFSET(CALENDAR_VAR, SDayName1) +
  4884. ((CalType - CAL_SDAYNAME1) * sizeof(WORD)))));
  4885. }
  4886. //
  4887. // Make sure the string is NOT empty. If it is, use the
  4888. // locale's string.
  4889. //
  4890. if ((pString == NULL) || (*pString == 0))
  4891. {
  4892. pString = (LPWORD)(pHashN->pLocaleHdr) +
  4893. *((LPWORD)((LPBYTE)(pHashN->pLocaleHdr) +
  4894. (FIELD_OFFSET(LOCALE_VAR, SDayName1) +
  4895. ((CalType - CAL_SDAYNAME1) * sizeof(WORD)))));
  4896. }
  4897. break;
  4898. }
  4899. default :
  4900. {
  4901. SetLastError(ERROR_INVALID_FLAGS);
  4902. return (0);
  4903. }
  4904. }
  4905. //
  4906. // See if the caller wants the value in the form of a number instead
  4907. // of a string.
  4908. //
  4909. if (ReturnNum)
  4910. {
  4911. //
  4912. // Make sure the flags are valid and that the DWORD buffer
  4913. // is not NULL.
  4914. //
  4915. if (Base == 0)
  4916. {
  4917. SetLastError(ERROR_INVALID_FLAGS);
  4918. return (0);
  4919. }
  4920. if (lpValue == NULL)
  4921. {
  4922. SetLastError(ERROR_INVALID_PARAMETER);
  4923. return (0);
  4924. }
  4925. //
  4926. // Convert the string to an int and return 2 (1 DWORD = 2 WORDS).
  4927. //
  4928. RtlInitUnicodeString(&ObUnicodeStr, pString);
  4929. if (RtlUnicodeStringToInteger(&ObUnicodeStr, Base, lpValue))
  4930. {
  4931. SetLastError(ERROR_INVALID_FLAGS);
  4932. return (0);
  4933. }
  4934. return (2);
  4935. }
  4936. //
  4937. // Get the length (in characters) of the string to copy.
  4938. //
  4939. if (Length == 0)
  4940. {
  4941. Length = NlsStrLenW(pString);
  4942. }
  4943. //
  4944. // Add one for the null termination. All strings should be null
  4945. // terminated.
  4946. //
  4947. Length++;
  4948. //
  4949. // Check cchData for size of given buffer.
  4950. //
  4951. if (cchData == 0)
  4952. {
  4953. //
  4954. // If cchData is 0, then we can't use lpCalData. In this
  4955. // case, we simply want to return the length (in characters) of
  4956. // the string to be copied.
  4957. //
  4958. return (Length);
  4959. }
  4960. else if (cchData < Length)
  4961. {
  4962. //
  4963. // The buffer is too small for the string, so return an error
  4964. // and zero bytes written.
  4965. //
  4966. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  4967. return (0);
  4968. }
  4969. //
  4970. // Copy the string to lpCalData and null terminate it.
  4971. // Return the number of characters copied.
  4972. //
  4973. wcsncpy(lpCalData, pString, Length - 1);
  4974. lpCalData[Length - 1] = 0;
  4975. return (Length);
  4976. }
  4977. ////////////////////////////////////////////////////////////////////////////
  4978. //
  4979. // SetCalendarInfoW
  4980. //
  4981. // Sets one of the various pieces of information about a particular
  4982. // calendar by making an entry in the user's portion of the configuration
  4983. // registry. This will only affect the user override portion of the
  4984. // calendar settings. The system defaults will never be reset.
  4985. //
  4986. // 12-17-97 JulieB Created.
  4987. ////////////////////////////////////////////////////////////////////////////
  4988. BOOL WINAPI SetCalendarInfoW(
  4989. LCID Locale,
  4990. CALID Calendar,
  4991. CALTYPE CalType,
  4992. LPCWSTR lpCalData)
  4993. {
  4994. PLOC_HASH pHashN; // ptr to LOC hash node
  4995. int cchData; // length of lpLCData
  4996. PCAL_INFO pCalInfo; // ptr to calendar info
  4997. UNICODE_STRING ObUnicodeStr; // value string
  4998. DWORD Value; // value
  4999. //
  5000. // Invalid Parameter Check:
  5001. // - validate LCID
  5002. // - NULL data pointer
  5003. //
  5004. // NOTE: invalid type is checked in the switch statement below.
  5005. //
  5006. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  5007. if ((pHashN == NULL) || (lpCalData == NULL))
  5008. {
  5009. SetLastError(ERROR_INVALID_PARAMETER);
  5010. return (FALSE);
  5011. }
  5012. //
  5013. // Get the length of the buffer.
  5014. //
  5015. cchData = NlsStrLenW(lpCalData) + 1;
  5016. //
  5017. // Validate the Calendar parameter.
  5018. //
  5019. if (GetCalendar(Calendar, &pCalInfo) != NO_ERROR)
  5020. {
  5021. SetLastError(ERROR_INVALID_PARAMETER);
  5022. return (FALSE);
  5023. }
  5024. //
  5025. // Set the appropriate user information for the given CALTYPE.
  5026. //
  5027. CalType &= (~CAL_USE_CP_ACP);
  5028. switch (CalType)
  5029. {
  5030. case ( CAL_ITWODIGITYEARMAX ) :
  5031. {
  5032. //
  5033. // Get the default value to make sure the calendar is
  5034. // allowed to be set. Things like the Japanese Era calendar
  5035. // may not be set.
  5036. //
  5037. RtlInitUnicodeString( &ObUnicodeStr,
  5038. ((LPWORD)pCalInfo +
  5039. (((PCALENDAR_VAR)pCalInfo)->STwoDigitYearMax)) );
  5040. RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value);
  5041. if (Value <= 99)
  5042. {
  5043. SetLastError(ERROR_INVALID_PARAMETER);
  5044. return (FALSE);
  5045. }
  5046. //
  5047. // Convert the user data so it can be validated
  5048. //
  5049. Value = 0;
  5050. NLS_STRING_TO_INTEGER(Value, lpCalData);
  5051. //
  5052. // Validate the new value. It should be no longer than
  5053. // MAX_ITWODIGITYEAR wide characters in length.
  5054. // It should be between 99 and 9999.
  5055. //
  5056. if ((cchData > MAX_ITWODIGITYEAR) ||
  5057. (Value < 99) ||
  5058. (Value > 9999))
  5059. {
  5060. SetLastError(ERROR_INVALID_PARAMETER);
  5061. return (FALSE);
  5062. }
  5063. //
  5064. // Set the registry with the new TwoDigitYearMax string.
  5065. //
  5066. return (SetTwoDigitYearInfo(Calendar, lpCalData, cchData));
  5067. break;
  5068. }
  5069. default :
  5070. {
  5071. SetLastError(ERROR_INVALID_FLAGS);
  5072. return (FALSE);
  5073. }
  5074. }
  5075. //
  5076. // Return success.
  5077. //
  5078. return (TRUE);
  5079. }
  5080. //-------------------------------------------------------------------------//
  5081. // INTERNAL ROUTINES //
  5082. //-------------------------------------------------------------------------//
  5083. ////////////////////////////////////////////////////////////////////////////
  5084. //
  5085. // SetUserInfo
  5086. //
  5087. // This routine sets the given value in the registry with the given data.
  5088. // All values must be of the type REG_SZ.
  5089. //
  5090. // NOTE: The handle to the registry key must be closed by the CALLER if
  5091. // the return value is NO_ERROR.
  5092. //
  5093. // 07-14-93 JulieB Created.
  5094. ////////////////////////////////////////////////////////////////////////////
  5095. BOOL SetUserInfo(
  5096. LCTYPE LCType,
  5097. LPWSTR pData,
  5098. ULONG DataLength)
  5099. {
  5100. NTSTATUS Status;
  5101. //
  5102. // Get the length of the value string.
  5103. //
  5104. DataLength *= sizeof(WCHAR);
  5105. //
  5106. // If there is no logged on user or the current security context
  5107. // isn't the logged-on interactive user, then set the registry
  5108. // value directly.
  5109. //
  5110. if (! NT_SUCCESS( NlsCheckForInteractiveUser() ))
  5111. {
  5112. return (SetCurrentUserRegValue(LCType, pData, DataLength));
  5113. }
  5114. // Call into server side (csrss.exe) to set the registry and update the cache for the current user.
  5115. Status = CsrBasepNlsSetUserInfo(LCType,
  5116. pData,
  5117. DataLength);
  5118. //
  5119. // Check to see if the "set" operation succeeded.
  5120. //
  5121. if (!NT_SUCCESS(Status))
  5122. {
  5123. //
  5124. // We got a failure. Try using just the registry apis to set the
  5125. // registry. It's possible that the cache is not valid yet if this
  5126. // is called from setup or winlogon.
  5127. //
  5128. return (SetCurrentUserRegValue(LCType, pData, DataLength));
  5129. }
  5130. //
  5131. // Return success.
  5132. //
  5133. return (TRUE);
  5134. }
  5135. ////////////////////////////////////////////////////////////////////////////
  5136. //
  5137. // SetCurrentUserRegValue
  5138. //
  5139. // Set the registry value for the current security context. This routine
  5140. // is called when the current security context is different from the logged
  5141. // on user.
  5142. //
  5143. // 12-26-98 SamerA Created.
  5144. ////////////////////////////////////////////////////////////////////////////
  5145. BOOL SetCurrentUserRegValue(
  5146. LCTYPE LCType,
  5147. LPWSTR pData,
  5148. ULONG DataLength)
  5149. {
  5150. HANDLE hKey = NULL;
  5151. LPWSTR pValue;
  5152. LPWSTR pCache;
  5153. if (0 == ValidateLCType(pNlsUserInfo, LCType, &pValue, &pCache))
  5154. {
  5155. SetLastError(ERROR_INVALID_PARAMETER);
  5156. return FALSE;
  5157. }
  5158. //
  5159. // Open the registry for the current security context
  5160. //
  5161. OPEN_CPANEL_INTL_KEY(hKey, FALSE, KEY_READ | KEY_WRITE);
  5162. if (SetRegValue(hKey, pValue, pData, DataLength) != NO_ERROR)
  5163. {
  5164. CLOSE_REG_KEY(hKey);
  5165. SetLastError(ERROR_INVALID_ACCESS);
  5166. return (FALSE);
  5167. }
  5168. CLOSE_REG_KEY(hKey);
  5169. //
  5170. // Flush the process cache entry, if needed.
  5171. //
  5172. NlsFlushProcessCache(LCType);
  5173. return (TRUE);
  5174. }
  5175. ////////////////////////////////////////////////////////////////////////////
  5176. //
  5177. // SetMultipleUserInfoInRegistry
  5178. //
  5179. // This routine sets the given multiple values in the registry with the
  5180. // given data. All values must be of the type REG_SZ.
  5181. //
  5182. // 06-11-98 JulieB Created.
  5183. ////////////////////////////////////////////////////////////////////////////
  5184. BOOL SetMultipleUserInfoInRegistry(
  5185. DWORD dwFlags,
  5186. int cchData,
  5187. LPCWSTR pPicture,
  5188. LPCWSTR pSeparator,
  5189. LPCWSTR pOrder,
  5190. LPCWSTR pTLZero,
  5191. LPCWSTR pTimeMarkPosn)
  5192. {
  5193. HANDLE hKey = NULL;
  5194. ULONG rc = 0L;
  5195. //
  5196. // Open the Control Panel International registry key.
  5197. //
  5198. OPEN_CPANEL_INTL_KEY(hKey, FALSE, KEY_READ | KEY_WRITE);
  5199. //
  5200. // Save the appropriate values in the registry based on the flags.
  5201. //
  5202. switch (dwFlags)
  5203. {
  5204. case ( LOCALE_STIMEFORMAT ) :
  5205. {
  5206. rc = SetRegValue( hKey,
  5207. NLS_VALUE_STIMEFORMAT,
  5208. pPicture,
  5209. cchData * sizeof(WCHAR) );
  5210. if (NT_SUCCESS(rc))
  5211. {
  5212. NlsFlushProcessCache(LOCALE_STIMEFORMAT);
  5213. rc = SetRegValue( hKey,
  5214. NLS_VALUE_STIME,
  5215. pSeparator,
  5216. (lstrlen(pSeparator) + 1) * sizeof(WCHAR) );
  5217. }
  5218. if (NT_SUCCESS(rc))
  5219. {
  5220. NlsFlushProcessCache(LOCALE_STIME);
  5221. rc = SetRegValue( hKey,
  5222. NLS_VALUE_ITIME,
  5223. pOrder,
  5224. (lstrlen(pOrder) + 1) * sizeof(WCHAR) );
  5225. }
  5226. if (NT_SUCCESS(rc))
  5227. {
  5228. NlsFlushProcessCache(LOCALE_ITIME);
  5229. rc = SetRegValue( hKey,
  5230. NLS_VALUE_ITLZERO,
  5231. pTLZero,
  5232. (lstrlen(pTLZero) + 1) * sizeof(WCHAR) );
  5233. }
  5234. if (NT_SUCCESS(rc))
  5235. {
  5236. NlsFlushProcessCache(LOCALE_ITLZERO);
  5237. rc = SetRegValue( hKey,
  5238. NLS_VALUE_ITIMEMARKPOSN,
  5239. pTimeMarkPosn,
  5240. (lstrlen(pTimeMarkPosn) + 1) * sizeof(WCHAR) );
  5241. if (NT_SUCCESS(rc))
  5242. {
  5243. NlsFlushProcessCache(LOCALE_ITIMEMARKPOSN);
  5244. }
  5245. }
  5246. break;
  5247. }
  5248. case ( LOCALE_STIME ) :
  5249. {
  5250. rc = SetRegValue( hKey,
  5251. NLS_VALUE_STIME,
  5252. pSeparator,
  5253. cchData * sizeof(WCHAR) );
  5254. if (NT_SUCCESS(rc))
  5255. {
  5256. NlsFlushProcessCache(LOCALE_STIME);
  5257. rc = SetRegValue( hKey,
  5258. NLS_VALUE_STIMEFORMAT,
  5259. pPicture,
  5260. (lstrlen(pPicture) + 1) * sizeof(WCHAR) );
  5261. if (NT_SUCCESS(rc))
  5262. {
  5263. NlsFlushProcessCache(LOCALE_STIMEFORMAT);
  5264. }
  5265. }
  5266. break;
  5267. }
  5268. case ( LOCALE_ITIME ) :
  5269. {
  5270. rc = SetRegValue( hKey,
  5271. NLS_VALUE_ITIME,
  5272. pOrder,
  5273. cchData * sizeof(WCHAR) );
  5274. if (NT_SUCCESS(rc))
  5275. {
  5276. NlsFlushProcessCache(LOCALE_ITIME);
  5277. rc = SetRegValue( hKey,
  5278. NLS_VALUE_STIMEFORMAT,
  5279. pPicture,
  5280. (lstrlen(pPicture) + 1) * sizeof(WCHAR) );
  5281. if (NT_SUCCESS(rc))
  5282. {
  5283. NlsFlushProcessCache(LOCALE_STIMEFORMAT);
  5284. }
  5285. }
  5286. break;
  5287. }
  5288. case ( LOCALE_SSHORTDATE ) :
  5289. {
  5290. rc = SetRegValue( hKey,
  5291. NLS_VALUE_SSHORTDATE,
  5292. pPicture,
  5293. cchData * sizeof(WCHAR) );
  5294. if (NT_SUCCESS(rc))
  5295. {
  5296. NlsFlushProcessCache(LOCALE_SSHORTDATE);
  5297. rc = SetRegValue( hKey,
  5298. NLS_VALUE_SDATE,
  5299. pSeparator,
  5300. (lstrlen(pSeparator) + 1) * sizeof(WCHAR) );
  5301. }
  5302. if (NT_SUCCESS(rc))
  5303. {
  5304. NlsFlushProcessCache(LOCALE_SDATE);
  5305. rc = SetRegValue( hKey,
  5306. NLS_VALUE_IDATE,
  5307. pOrder,
  5308. (lstrlen(pOrder) + 1) * sizeof(WCHAR) );
  5309. if (NT_SUCCESS(rc))
  5310. {
  5311. NlsFlushProcessCache(LOCALE_IDATE);
  5312. }
  5313. }
  5314. break;
  5315. }
  5316. case ( LOCALE_SDATE ) :
  5317. {
  5318. rc = SetRegValue( hKey,
  5319. NLS_VALUE_SDATE,
  5320. pSeparator,
  5321. cchData * sizeof(WCHAR) );
  5322. if (NT_SUCCESS(rc))
  5323. {
  5324. NlsFlushProcessCache(LOCALE_SDATE);
  5325. rc = SetRegValue( hKey,
  5326. NLS_VALUE_SSHORTDATE,
  5327. pPicture,
  5328. (lstrlen(pPicture) + 1) * sizeof(WCHAR) );
  5329. if (NT_SUCCESS(rc))
  5330. {
  5331. NlsFlushProcessCache(LOCALE_SSHORTDATE);
  5332. }
  5333. }
  5334. break;
  5335. }
  5336. default :
  5337. {
  5338. CLOSE_REG_KEY(hKey);
  5339. return (FALSE);
  5340. }
  5341. }
  5342. //
  5343. // Close the registry key.
  5344. //
  5345. CLOSE_REG_KEY(hKey);
  5346. //
  5347. // Return the result.
  5348. //
  5349. return (rc == NO_ERROR);
  5350. }
  5351. ////////////////////////////////////////////////////////////////////////////
  5352. //
  5353. // SetMultipleUserInfo
  5354. //
  5355. // This routine calls the server to set multiple registry values. This way,
  5356. // only one client/server transition is necessary.
  5357. //
  5358. // 08-19-94 JulieB Created.
  5359. ////////////////////////////////////////////////////////////////////////////
  5360. BOOL SetMultipleUserInfo(
  5361. DWORD dwFlags,
  5362. int cchData,
  5363. LPCWSTR pPicture,
  5364. LPCWSTR pSeparator,
  5365. LPCWSTR pOrder,
  5366. LPCWSTR pTLZero,
  5367. LPCWSTR pTimeMarkPosn)
  5368. {
  5369. NTSTATUS Status;
  5370. //
  5371. // If there is no logged on user or the current security context
  5372. // isn't the logged-on interactive user, then set the registry
  5373. // value directly.
  5374. //
  5375. if (! NT_SUCCESS( NlsCheckForInteractiveUser() ))
  5376. {
  5377. if (SetMultipleUserInfoInRegistry( dwFlags,
  5378. cchData,
  5379. pPicture,
  5380. pSeparator,
  5381. pOrder,
  5382. pTLZero,
  5383. pTimeMarkPosn ) == FALSE)
  5384. {
  5385. SetLastError(ERROR_INVALID_ACCESS);
  5386. return (FALSE);
  5387. }
  5388. return (TRUE);
  5389. }
  5390. Status = CsrBasepNlsSetMultipleUserInfo(dwFlags,
  5391. cchData,
  5392. pPicture,
  5393. pSeparator,
  5394. pOrder,
  5395. pTLZero,
  5396. pTimeMarkPosn
  5397. );
  5398. //
  5399. // Check to see if the "set" operation succeeded.
  5400. //
  5401. if (!NT_SUCCESS(Status))
  5402. {
  5403. //
  5404. // We got a failure. Try using just the registry apis to set the
  5405. // registry. It's possible that the cache is not valid yet if this
  5406. // is called from setup or winlogon.
  5407. //
  5408. if (SetMultipleUserInfoInRegistry( dwFlags,
  5409. cchData,
  5410. pPicture,
  5411. pSeparator,
  5412. pOrder,
  5413. pTLZero,
  5414. pTimeMarkPosn ) == FALSE)
  5415. {
  5416. SetLastError(ERROR_INVALID_ACCESS);
  5417. return (FALSE);
  5418. }
  5419. }
  5420. //
  5421. // Return success.
  5422. //
  5423. return (TRUE);
  5424. }
  5425. ////////////////////////////////////////////////////////////////////////////
  5426. //
  5427. // GetTwoDigitYearInfo
  5428. //
  5429. // This routine gets the two digit year info from the registry.
  5430. //
  5431. // 12-17-97 JulieB Created.
  5432. ////////////////////////////////////////////////////////////////////////////
  5433. BOOL GetTwoDigitYearInfo(
  5434. CALID Calendar,
  5435. LPWSTR pYearInfo,
  5436. size_t cchYearInfo,
  5437. PWSTR pwszKeyPath)
  5438. {
  5439. HANDLE hKey = NULL; // handle to key
  5440. WCHAR pCalStr[MAX_PATH]; // ptr to calendar id string
  5441. PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
  5442. BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
  5443. BOOL IfAlloc = FALSE; // if buffer was allocated
  5444. ULONG rc = 0L; // return code
  5445. BOOL bResult = FALSE; // result
  5446. UNICODE_STRING ObUnicodeStr; // year string
  5447. DWORD Year; // year value
  5448. //
  5449. // Open the Control Panel International registry key.
  5450. //
  5451. if (OpenRegKey( &hKey,
  5452. NULL,
  5453. pwszKeyPath,
  5454. KEY_READ ) != NO_ERROR)
  5455. {
  5456. return (FALSE);
  5457. }
  5458. //
  5459. // Convert calendar value to Unicode string.
  5460. //
  5461. if (NlsConvertIntegerToString(Calendar, 10, 0, pCalStr, MAX_PATH))
  5462. {
  5463. NtClose(hKey);
  5464. return (FALSE);
  5465. }
  5466. //
  5467. // Query the registry for the TwoDigitYearMax value.
  5468. //
  5469. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
  5470. rc = QueryRegValue( hKey,
  5471. pCalStr,
  5472. &pKeyValueFull,
  5473. MAX_KEY_VALUE_FULLINFO,
  5474. &IfAlloc );
  5475. //
  5476. // Close the registry key.
  5477. //
  5478. NtClose(hKey);
  5479. //
  5480. // See if the TwoDigitYearMax value is present.
  5481. //
  5482. if (rc != NO_ERROR)
  5483. {
  5484. return (FALSE);
  5485. }
  5486. //
  5487. // See if the TwoDigitYearMax data is present.
  5488. //
  5489. if (pKeyValueFull->DataLength > 2)
  5490. {
  5491. //
  5492. // Copy the info
  5493. //
  5494. if(FAILED(StringCchCopyW(pYearInfo, cchYearInfo, GET_VALUE_DATA_PTR(pKeyValueFull))))
  5495. {
  5496. return(FALSE);
  5497. }
  5498. //
  5499. // Make sure the value is between 99 and 9999.
  5500. //
  5501. RtlInitUnicodeString(&ObUnicodeStr, pYearInfo);
  5502. if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Year) == NO_ERROR) &&
  5503. (Year >= 99) && (Year <= 9999))
  5504. {
  5505. bResult = TRUE;
  5506. }
  5507. }
  5508. //
  5509. // Free the buffer used for the query.
  5510. //
  5511. if (IfAlloc)
  5512. {
  5513. NLS_FREE_MEM(pKeyValueFull);
  5514. }
  5515. //
  5516. // Return the result.
  5517. //
  5518. return (bResult);
  5519. }
  5520. ////////////////////////////////////////////////////////////////////////////
  5521. //
  5522. // SetTwoDigitYearInfo
  5523. //
  5524. // This routine sets the two digit year info in the registry.
  5525. //
  5526. // 12-17-97 JulieB Created.
  5527. ////////////////////////////////////////////////////////////////////////////
  5528. BOOL SetTwoDigitYearInfo(
  5529. CALID Calendar,
  5530. LPCWSTR pYearInfo,
  5531. int cchData)
  5532. {
  5533. HANDLE hKey = NULL; // handle to key
  5534. WCHAR pCalStr[MAX_PATH]; // ptr to calendar id string
  5535. ULONG rc = 0L; // return code
  5536. //
  5537. // Open the Control Panel International registry key.
  5538. // If it doesn't exist, then we have to create each subkey
  5539. // separately.
  5540. //
  5541. if (OpenRegKey( &hKey,
  5542. NULL,
  5543. NLS_TWO_DIGIT_YEAR_KEY,
  5544. KEY_READ | KEY_WRITE ) != NO_ERROR)
  5545. {
  5546. //
  5547. // Registry key does not exist, so create each subkey
  5548. // separately.
  5549. //
  5550. if (CreateRegKey( &hKey,
  5551. NULL,
  5552. NLS_CALENDARS_KEY,
  5553. KEY_READ | KEY_WRITE ) == NO_ERROR)
  5554. {
  5555. NtClose(hKey);
  5556. if (CreateRegKey( &hKey,
  5557. NULL,
  5558. NLS_TWO_DIGIT_YEAR_KEY,
  5559. KEY_READ | KEY_WRITE ) != NO_ERROR)
  5560. {
  5561. return (FALSE);
  5562. }
  5563. }
  5564. else
  5565. {
  5566. return (FALSE);
  5567. }
  5568. }
  5569. //
  5570. // Make sure all Gregorian calendars are set to the same value.
  5571. //
  5572. switch (Calendar)
  5573. {
  5574. case ( 1 ) :
  5575. case ( 2 ) :
  5576. case ( 9 ) :
  5577. case ( 10 ) :
  5578. case ( 11 ) :
  5579. case ( 12 ) :
  5580. {
  5581. rc = SetRegValue(hKey, L"1", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
  5582. if (rc == NO_ERROR)
  5583. {
  5584. rc = SetRegValue(hKey, L"2", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
  5585. }
  5586. if (rc == NO_ERROR)
  5587. {
  5588. rc = SetRegValue(hKey, L"9", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
  5589. }
  5590. if (rc == NO_ERROR)
  5591. {
  5592. rc = SetRegValue(hKey, L"10", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
  5593. }
  5594. if (rc == NO_ERROR)
  5595. {
  5596. rc = SetRegValue(hKey, L"11", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
  5597. }
  5598. if (rc == NO_ERROR)
  5599. {
  5600. rc = SetRegValue(hKey, L"12", pYearInfo, (ULONG)cchData * sizeof(WCHAR));
  5601. }
  5602. break;
  5603. }
  5604. default :
  5605. {
  5606. //
  5607. // Convert calendar value to Unicode string.
  5608. //
  5609. if (NlsConvertIntegerToString(Calendar, 10, 0, pCalStr, MAX_PATH))
  5610. {
  5611. NtClose(hKey);
  5612. return (FALSE);
  5613. }
  5614. //
  5615. // Set the TwoDigitYearMax value in the registry.
  5616. //
  5617. rc = SetRegValue(hKey, pCalStr, pYearInfo, (ULONG)cchData * sizeof(WCHAR));
  5618. break;
  5619. }
  5620. }
  5621. //
  5622. // Update the NlsCacheUpdateCount inside csrss
  5623. //
  5624. if (rc == NO_ERROR)
  5625. {
  5626. CsrBasepNlsUpdateCacheCount();
  5627. }
  5628. //
  5629. // Close the registry key.
  5630. //
  5631. NtClose(hKey);
  5632. //
  5633. // Return the result.
  5634. //
  5635. return (rc == NO_ERROR);
  5636. }
  5637. ////////////////////////////////////////////////////////////////////////////
  5638. //
  5639. // GetNLSVersion
  5640. //
  5641. // Return the version of a specific NLS function.
  5642. //
  5643. // 08-15-2001 LGuindon Created.
  5644. ////////////////////////////////////////////////////////////////////////////
  5645. BOOL WINAPI GetNLSVersion(
  5646. NLS_FUNCTION function,
  5647. LCID locale,
  5648. LPNLSVERSIONINFO lpVersionInformation)
  5649. {
  5650. PLOC_HASH pHashN;
  5651. //
  5652. // Invalid parameter check:
  5653. // - validate LCID
  5654. // - NULL data pointer
  5655. //
  5656. // NOTE: invalid function is checked in the switch statement below.
  5657. //
  5658. VALIDATE_LOCALE(locale, pHashN, FALSE);
  5659. if ((lpVersionInformation == NULL) || (pHashN == NULL))
  5660. {
  5661. SetLastError(ERROR_INVALID_PARAMETER);
  5662. return (FALSE);
  5663. }
  5664. //
  5665. // Buffer size check.
  5666. //
  5667. if (lpVersionInformation->dwNLSVersionInfoSize != sizeof(NLSVERSIONINFO))
  5668. {
  5669. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  5670. return (FALSE);
  5671. }
  5672. //
  5673. // Make sure the appropriate tables are available. If not,
  5674. // return an error.
  5675. //
  5676. if (pTblPtrs->pSortVersion == NULL )
  5677. {
  5678. KdPrint(("NLSAPI: Appropriate Versioning Table Not Loaded.\n"));
  5679. SetLastError(ERROR_FILE_NOT_FOUND);
  5680. return (FALSE);
  5681. }
  5682. //
  5683. // Make sure the appropriate tables are available. If not,
  5684. // return an error.
  5685. //
  5686. if (pTblPtrs->pDefinedVersion == NULL)
  5687. {
  5688. KdPrint(("NLSAPI: Appropriate Defined Code Point Table Not Loaded.\n"));
  5689. SetLastError(ERROR_FILE_NOT_FOUND);
  5690. return (FALSE);
  5691. }
  5692. //
  5693. // Check which NLS functionnality version is requested.
  5694. //
  5695. switch (function)
  5696. {
  5697. case (COMPARE_STRING):
  5698. {
  5699. UINT i;
  5700. //
  5701. // Get the Defined version. Always the first entry
  5702. // in the Defined Code Point version table. The first
  5703. // entry represent the current Defined version.
  5704. //
  5705. lpVersionInformation->dwDefinedVersion = (pTblPtrs->pDefinedVersion)[0].Version;
  5706. //
  5707. // Get the NLS sorting version. Search specific locale
  5708. // version info. Start from the second entry; after default
  5709. // value.
  5710. //
  5711. lpVersionInformation->dwNLSVersion = (pTblPtrs->pSortVersion)[0].Version;
  5712. for (i = 1; i < pTblPtrs->NumSortVersion; i++)
  5713. {
  5714. if (pHashN->Locale == (pTblPtrs->pSortVersion)[i].Locale)
  5715. {
  5716. lpVersionInformation->dwNLSVersion = (pTblPtrs->pSortVersion)[i].Version;
  5717. break;
  5718. }
  5719. }
  5720. break;
  5721. }
  5722. // case (NORMALIZE_STRING):
  5723. // {
  5724. // //
  5725. // // Not implemented yet.
  5726. // //
  5727. // SetLastError(ERROR_NOT_SUPPORTED);
  5728. // return (FALSE);
  5729. // }
  5730. default:
  5731. {
  5732. SetLastError(ERROR_INVALID_FLAGS);
  5733. return (FALSE);
  5734. }
  5735. }
  5736. return (TRUE);
  5737. }
  5738. ////////////////////////////////////////////////////////////////////////////
  5739. //
  5740. // IsNLSDefinedString
  5741. //
  5742. // This routine looks for code points inside a string to see if they are
  5743. // defined within the NSL context. If lpVersionInformation is NULL, the
  5744. // version is the current version. Same thing the dwDefinedVersion is equal
  5745. // to zero.
  5746. //
  5747. // 08-20-2001 LGuindon Created.
  5748. ////////////////////////////////////////////////////////////////////////////
  5749. BOOL WINAPI IsNLSDefinedString(
  5750. NLS_FUNCTION Function,
  5751. DWORD dwFlags,
  5752. LPNLSVERSIONINFO lpVersionInformation,
  5753. LPCWSTR lpString,
  5754. INT cchStr)
  5755. {
  5756. //
  5757. // Invalid Parameter Check:
  5758. // - string is null
  5759. // - length of src string is 0
  5760. //
  5761. // NOTE:
  5762. // - invalid function is checked in the switch statement below.
  5763. // - version validity is checked below.
  5764. // - dwNLSVersionInfoSize is checked later in IsSortingCodePointDefined().
  5765. // - we don't check lpVersionInformation for NULL, since NULL means the current defined version.
  5766. //
  5767. if ((cchStr == 0) ||
  5768. (lpString == NULL))
  5769. {
  5770. SetLastError(ERROR_INVALID_PARAMETER);
  5771. return (FALSE);
  5772. }
  5773. //
  5774. // Invalid Flag Check:
  5775. // - dwFlags is not zero
  5776. //
  5777. if (dwFlags != 0)
  5778. {
  5779. SetLastError(ERROR_INVALID_FLAGS);
  5780. return (FALSE);
  5781. }
  5782. //
  5783. // Buffer size check is checked below.
  5784. //
  5785. //
  5786. // Check string length.
  5787. //
  5788. if (cchStr <= (-1))
  5789. {
  5790. cchStr = NlsStrLenW(lpString);
  5791. }
  5792. //
  5793. // Check which NLS functionnality version is requested.
  5794. //
  5795. switch (Function)
  5796. {
  5797. case (COMPARE_STRING):
  5798. {
  5799. return (IsSortingCodePointDefined(lpVersionInformation, lpString, cchStr));
  5800. }
  5801. // case (NORMALIZE_STRING):
  5802. // {
  5803. //
  5804. // Not implemented yet.
  5805. //
  5806. // SetLastError(ERROR_NOT_SUPPORTED);
  5807. // return (FALSE);
  5808. // }
  5809. default:
  5810. {
  5811. SetLastError(ERROR_INVALID_FLAGS);
  5812. return (FALSE);
  5813. }
  5814. }
  5815. }