Port servicing fix (#15802)
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Globalization / CultureData.Windows.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
9 using System.Text;
10 using Internal.Runtime.CompilerServices;
11
12 #if ENABLE_WINRT
13 using Internal.Runtime.Augments;
14 #endif
15
16 namespace System.Globalization
17 {
18 #if CORECLR
19     using StringList = List<string>;
20 #else
21     using StringList = LowLevelList<string>;
22 #endif
23
24     internal partial class CultureData
25     {
26         private const uint LOCALE_NOUSEROVERRIDE = 0x80000000;
27         private const uint LOCALE_RETURN_NUMBER = 0x20000000;
28         private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A;
29
30         private const uint TIME_NOSECONDS = 0x00000002;
31
32         /// <summary>
33         /// Check with the OS to see if this is a valid culture.
34         /// If so we populate a limited number of fields.  If its not valid we return false.
35         ///
36         /// The fields we populate:
37         ///
38         /// sWindowsName -- The name that windows thinks this culture is, ie:
39         ///                            en-US if you pass in en-US
40         ///                            de-DE_phoneb if you pass in de-DE_phoneb
41         ///                            fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
42         ///                            fj if you pass in fj (neutral, post-Windows 7 machine)
43         ///
44         /// sRealName -- The name you used to construct the culture, in pretty form
45         ///                       en-US if you pass in EN-us
46         ///                       en if you pass in en
47         ///                       de-DE_phoneb if you pass in de-DE_phoneb
48         ///
49         /// sSpecificCulture -- The specific culture for this culture
50         ///                             en-US for en-US
51         ///                             en-US for en
52         ///                             de-DE_phoneb for alt sort
53         ///                             fj-FJ for fj (neutral)
54         ///
55         /// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
56         ///                en-US if you pass in en-US
57         ///                en if you pass in en
58         ///                de-DE if you pass in de-DE_phoneb
59         ///
60         /// bNeutral -- TRUE if it is a neutral locale
61         ///
62         /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the
63         /// windows locale that's going to provide data for us.
64         /// </summary>
65         private unsafe bool InitCultureData()
66         {
67             Debug.Assert(!GlobalizationMode.Invariant);
68
69             const uint LOCALE_ILANGUAGE = 0x00000001;
70             const uint LOCALE_INEUTRAL = 0x00000071;
71             const uint LOCALE_SNAME = 0x0000005c;
72
73             int result;
74             string realNameBuffer = _sRealName;
75             char* pBuffer = stackalloc char[LOCALE_NAME_MAX_LENGTH];
76
77             result = GetLocaleInfoEx(realNameBuffer, LOCALE_SNAME, pBuffer, LOCALE_NAME_MAX_LENGTH);
78
79             // Did it fail?
80             if (result == 0)
81             {
82                 return false;
83             }
84
85             // It worked, note that the name is the locale name, so use that (even for neutrals)
86             // We need to clean up our "real" name, which should look like the windows name right now
87             // so overwrite the input with the cleaned up name
88             _sRealName = new String(pBuffer, 0, result - 1);
89             realNameBuffer = _sRealName;
90
91             // Check for neutrality, don't expect to fail
92             // (buffer has our name in it, so we don't have to do the gc. stuff)
93
94             result = GetLocaleInfoEx(realNameBuffer, LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
95             if (result == 0)
96             {
97                 return false;
98             }
99
100             // Remember our neutrality
101             _bNeutral = *((uint*)pBuffer) != 0;
102
103             // Note: Parents will be set dynamically
104
105             // Start by assuming the windows name will be the same as the specific name since windows knows
106             // about specifics on all versions. Only for downlevel Neutral locales does this have to change.
107             _sWindowsName = realNameBuffer;
108
109             // Neutrals and non-neutrals are slightly different
110             if (_bNeutral)
111             {
112                 // Neutral Locale
113
114                 // IETF name looks like neutral name
115                 _sName = realNameBuffer;
116
117                 // Specific locale name is whatever ResolveLocaleName (win7+) returns.
118                 // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer)
119                 result = Interop.Kernel32.ResolveLocaleName(realNameBuffer, pBuffer, LOCALE_NAME_MAX_LENGTH);
120
121                 // 0 is failure, 1 is invariant (""), which we expect
122                 if (result < 1)
123                 {
124                     return false;
125                 }
126                 // We found a locale name, so use it.
127                 // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
128                 _sSpecificCulture = new String(pBuffer, 0, result - 1);
129             }
130             else
131             {
132                 // Specific Locale
133
134                 // Specific culture's the same as the locale name since we know its not neutral
135                 // On mac we'll use this as well, even for neutrals. There's no obvious specific
136                 // culture to use and this isn't exposed, but behaviorally this is correct on mac.
137                 // Note that specifics include the sort name (de-DE_phoneb)
138                 _sSpecificCulture = realNameBuffer;
139
140                 _sName = realNameBuffer;
141
142                 // We need the IETF name (sname)
143                 // If we aren't an alt sort locale then this is the same as the windows name.
144                 // If we are an alt sort locale then this is the same as the part before the _ in the windows name
145                 // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
146
147                 result = GetLocaleInfoEx(realNameBuffer, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
148                 if (result == 0)
149                 {
150                     return false;
151                 }
152
153                 _iLanguage = *((int*)pBuffer);
154
155                 if (!IsCustomCultureId(_iLanguage))
156                 {
157                     // not custom locale
158                     int index = realNameBuffer.IndexOf('_');
159                     if (index > 0 && index < realNameBuffer.Length)
160                     {
161                         _sName = realNameBuffer.Substring(0, index);
162                     }
163                 }
164             }
165
166             // It succeeded.
167             return true;
168         }
169
170         // Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned
171         // data as either and Int or String.
172         internal static unsafe String GetLocaleInfoEx(String localeName, uint field)
173         {
174             // REVIEW: Determine the maximum size for the buffer
175             const int BUFFER_SIZE = 530;
176
177             char* pBuffer = stackalloc char[BUFFER_SIZE];
178             int resultCode = GetLocaleInfoEx(localeName, field, pBuffer, BUFFER_SIZE);
179             if (resultCode > 0)
180             {
181                 return new String(pBuffer);
182             }
183
184             return null;
185         }
186
187         internal static unsafe int GetLocaleInfoExInt(String localeName, uint field)
188         {
189             const uint LOCALE_RETURN_NUMBER = 0x20000000;
190             field |= LOCALE_RETURN_NUMBER;
191             int value = 0;
192             GetLocaleInfoEx(localeName, field, (char*) &value, sizeof(int));
193             return value;
194         }
195
196         internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, char* lpLCData, int cchData)
197         {
198             Debug.Assert(!GlobalizationMode.Invariant);
199
200             return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, lpLCData, cchData);
201         }
202
203         private string GetLocaleInfo(LocaleStringData type)
204         {
205             Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected _sWindowsName to be populated by already");
206             return GetLocaleInfo(_sWindowsName, type);
207         }
208
209         // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
210         // "windows" name, which can be specific for downlevel (< windows 7) os's.
211         private string GetLocaleInfo(string localeName, LocaleStringData type)
212         {
213             uint lctype = (uint)type;
214
215             return GetLocaleInfoFromLCType(localeName, lctype, UseUserOverride);
216         }
217
218         private int GetLocaleInfo(LocaleNumberData type)
219         {
220             uint lctype = (uint)type;
221
222             // Fix lctype if we don't want overrides
223             if (!UseUserOverride)
224             {
225                 lctype |= LOCALE_NOUSEROVERRIDE;
226             }
227
228             // Ask OS for data, note that we presume it returns success, so we have to know that
229             // sWindowsName is valid before calling.
230             Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
231             return  GetLocaleInfoExInt(_sWindowsName, lctype);
232         }
233
234         private int[] GetLocaleInfo(LocaleGroupingData type)
235         {
236             return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sWindowsName, (uint)type, UseUserOverride));
237         }
238
239         private string GetTimeFormatString()
240         {
241             const uint LOCALE_STIMEFORMAT = 0x00001003;
242
243             return ReescapeWin32String(GetLocaleInfoFromLCType(_sWindowsName, LOCALE_STIMEFORMAT, UseUserOverride));
244         }
245
246         private int GetFirstDayOfWeek()
247         {
248             Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
249
250             const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C;
251
252             int result = GetLocaleInfoExInt(_sWindowsName, LOCALE_IFIRSTDAYOFWEEK | (!UseUserOverride ? LOCALE_NOUSEROVERRIDE : 0));
253
254             // Win32 and .NET disagree on the numbering for days of the week, so we have to convert.
255             return ConvertFirstDayOfWeekMonToSun(result);
256         }
257
258         private String[] GetTimeFormats()
259         {
260             // Note that this gets overrides for us all the time
261             Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected _sWindowsName to be populated by already");
262             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride));
263
264             return result;
265         }
266
267         private String[] GetShortTimeFormats()
268         {
269             // Note that this gets overrides for us all the time
270             Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected _sWindowsName to be populated by already");
271             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, TIME_NOSECONDS, UseUserOverride));
272
273             return result;
274         }
275
276         // Enumerate all system cultures and then try to find out which culture has
277         // region name match the requested region name
278         private static CultureData GetCultureDataFromRegionName(String regionName)
279         {
280             Debug.Assert(!GlobalizationMode.Invariant);
281             Debug.Assert(regionName != null);
282
283             const uint LOCALE_SUPPLEMENTAL = 0x00000002;
284             const uint LOCALE_SPECIFICDATA = 0x00000020;
285
286             EnumLocaleData context = new EnumLocaleData();
287             context.cultureName = null;
288             context.regionName = regionName;
289
290             unsafe
291             {
292                 Interop.Kernel32.EnumSystemLocalesEx(EnumSystemLocalesProc, LOCALE_SPECIFICDATA | LOCALE_SUPPLEMENTAL, Unsafe.AsPointer(ref context), IntPtr.Zero);
293             }
294
295             if (context.cultureName != null)
296             {
297                 // we got a matched culture
298                 return GetCultureData(context.cultureName, true);
299             }
300
301             return null;
302         }
303
304         private string GetLanguageDisplayName(string cultureName)
305         {
306 #if ENABLE_WINRT
307             return WinRTInterop.Callbacks.GetLanguageDisplayName(cultureName);
308 #else
309             // Usually the UI culture shouldn't be different than what we got from WinRT except
310             // if DefaultThreadCurrentUICulture was set
311             CultureInfo ci;
312
313             if (CultureInfo.DefaultThreadCurrentUICulture != null &&
314                 ((ci = GetUserDefaultCulture()) != null) &&
315                 !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
316             {
317                 return SNATIVEDISPLAYNAME;
318             }
319             else
320             {
321                 return GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName);
322             }
323 #endif // ENABLE_WINRT
324         }
325
326         private string GetRegionDisplayName(string isoCountryCode)
327         {
328 #if ENABLE_WINRT
329             return WinRTInterop.Callbacks.GetRegionDisplayName(isoCountryCode);
330 #else
331             // If the current UI culture matching the OS UI language, we'll get the display name from the OS.
332             // otherwise, we use the native name as we don't carry resources for the region display names anyway.
333             if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name))
334             {
335                 return GetLocaleInfo(LocaleStringData.LocalizedCountryName);
336             }
337
338             return SNATIVECOUNTRY;
339 #endif // ENABLE_WINRT
340         }
341
342         private static CultureInfo GetUserDefaultCulture()
343         {
344 #if ENABLE_WINRT
345             return (CultureInfo)WinRTInterop.Callbacks.GetUserDefaultCulture();
346 #else
347             return CultureInfo.GetUserDefaultCulture();
348 #endif // ENABLE_WINRT
349         }
350
351         // PAL methods end here.
352
353         private static string GetLocaleInfoFromLCType(string localeName, uint lctype, bool useUserOveride)
354         {
355             Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoFromLCType] Expected localeName to be not be null");
356
357             // Fix lctype if we don't want overrides
358             if (!useUserOveride)
359             {
360                 lctype |= LOCALE_NOUSEROVERRIDE;
361             }
362
363             // Ask OS for data
364             string result = GetLocaleInfoEx(localeName, lctype);
365             if (result == null)
366             {
367                 // Failed, just use empty string
368                 result = String.Empty;
369             }
370
371             return result;
372         }
373
374         ////////////////////////////////////////////////////////////////////////////
375         //
376         // Reescape a Win32 style quote string as a NLS+ style quoted string
377         //
378         // This is also the escaping style used by custom culture data files
379         //
380         // NLS+ uses \ to escape the next character, whether in a quoted string or
381         // not, so we always have to change \ to \\.
382         //
383         // NLS+ uses \' to escape a quote inside a quoted string so we have to change
384         // '' to \' (if inside a quoted string)
385         //
386         // We don't build the stringbuilder unless we find something to change
387         ////////////////////////////////////////////////////////////////////////////
388         internal static String ReescapeWin32String(String str)
389         {
390             // If we don't have data, then don't try anything
391             if (str == null)
392                 return null;
393
394             StringBuilder result = null;
395
396             bool inQuote = false;
397             for (int i = 0; i < str.Length; i++)
398             {
399                 // Look for quote
400                 if (str[i] == '\'')
401                 {
402                     // Already in quote?
403                     if (inQuote)
404                     {
405                         // See another single quote.  Is this '' of 'fred''s' or '''', or is it an ending quote?
406                         if (i + 1 < str.Length && str[i + 1] == '\'')
407                         {
408                             // Found another ', so we have ''.  Need to add \' instead.
409                             // 1st make sure we have our stringbuilder
410                             if (result == null)
411                                 result = new StringBuilder(str, 0, i, str.Length * 2);
412
413                             // Append a \' and keep going (so we don't turn off quote mode)
414                             result.Append("\\'");
415                             i++;
416                             continue;
417                         }
418
419                         // Turning off quote mode, fall through to add it
420                         inQuote = false;
421                     }
422                     else
423                     {
424                         // Found beginning quote, fall through to add it
425                         inQuote = true;
426                     }
427                 }
428                 // Is there a single \ character?
429                 else if (str[i] == '\\')
430                 {
431                     // Found a \, need to change it to \\
432                     // 1st make sure we have our stringbuilder
433                     if (result == null)
434                         result = new StringBuilder(str, 0, i, str.Length * 2);
435
436                     // Append our \\ to the string & continue
437                     result.Append("\\\\");
438                     continue;
439                 }
440
441                 // If we have a builder we need to add our character
442                 if (result != null)
443                     result.Append(str[i]);
444             }
445
446             // Unchanged string? , just return input string
447             if (result == null)
448                 return str;
449
450             // String changed, need to use the builder
451             return result.ToString();
452         }
453
454         internal static String[] ReescapeWin32Strings(String[] array)
455         {
456             if (array != null)
457             {
458                 for (int i = 0; i < array.Length; i++)
459                 {
460                     array[i] = ReescapeWin32String(array[i]);
461                 }
462             }
463
464             return array;
465         }
466
467         // If we get a group from windows, then its in 3;0 format with the 0 backwards
468         // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
469         // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
470         private static int[] ConvertWin32GroupString(String win32Str)
471         {
472             // None of these cases make any sense
473             if (win32Str == null || win32Str.Length == 0)
474             {
475                 return (new int[] { 3 });
476             }
477
478             if (win32Str[0] == '0')
479             {
480                 return (new int[] { 0 });
481             }
482
483             // Since its in n;n;n;n;n format, we can always get the length quickly
484             int[] values;
485             if (win32Str[win32Str.Length - 1] == '0')
486             {
487                 // Trailing 0 gets dropped. 1;0 -> 1
488                 values = new int[(win32Str.Length / 2)];
489             }
490             else
491             {
492                 // Need extra space for trailing zero 1 -> 1;0
493                 values = new int[(win32Str.Length / 2) + 2];
494                 values[values.Length - 1] = 0;
495             }
496
497             int i;
498             int j;
499             for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
500             {
501                 // Note that this # shouldn't ever be zero, 'cause 0 is only at end
502                 // But we'll test because its registry that could be anything
503                 if (win32Str[i] < '1' || win32Str[i] > '9')
504                     return new int[] { 3 };
505
506                 values[j] = (int)(win32Str[i] - '0');
507             }
508
509             return (values);
510         }
511
512         private static int ConvertFirstDayOfWeekMonToSun(int iTemp)
513         {
514             // Convert Mon-Sun to Sun-Sat format
515             iTemp++;
516             if (iTemp > 6)
517             {
518                 // Wrap Sunday and convert invalid data to Sunday
519                 iTemp = 0;
520             }
521             return iTemp;
522         }
523
524
525         // Context for EnumCalendarInfoExEx callback.
526         private class EnumLocaleData
527         {
528             public string regionName;
529             public string cultureName;
530         }
531
532         // EnumSystemLocaleEx callback.
533         // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
534         private static unsafe Interop.BOOL EnumSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
535         {
536             ref EnumLocaleData context = ref Unsafe.As<byte, EnumLocaleData>(ref *(byte*)contextHandle);
537             try
538             {
539                 string cultureName = new string(lpLocaleString);
540                 string regionName = GetLocaleInfoEx(cultureName, LOCALE_SISO3166CTRYNAME);
541                 if (regionName != null && regionName.Equals(context.regionName, StringComparison.OrdinalIgnoreCase))
542                 {
543                     context.cultureName = cultureName;
544                     return Interop.BOOL.FALSE; // we found a match, then stop the enumeration
545                 }
546
547                 return Interop.BOOL.TRUE;
548             }
549             catch (Exception)
550             {
551                 return Interop.BOOL.FALSE;
552             }
553         }
554
555         // EnumSystemLocaleEx callback.
556         // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
557         private static unsafe Interop.BOOL EnumAllSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
558         {
559             ref EnumData context = ref Unsafe.As<byte, EnumData>(ref *(byte*)contextHandle);
560             try
561             {
562                 context.strings.Add(new string(lpLocaleString));
563                 return Interop.BOOL.TRUE;
564             }
565             catch (Exception)
566             {
567                 return Interop.BOOL.FALSE;
568             }
569         }
570
571         // Context for EnumTimeFormatsEx callback.
572         private class EnumData
573         {
574             public StringList strings;
575         }
576
577         // EnumTimeFormatsEx callback itself.
578         // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
579         private static unsafe Interop.BOOL EnumTimeCallback(char* lpTimeFormatString, void* lParam)
580         {
581             ref EnumData context = ref Unsafe.As<byte, EnumData>(ref *(byte*)lParam);
582             try
583             {
584                 context.strings.Add(new string(lpTimeFormatString));
585                 return Interop.BOOL.TRUE;
586             }
587             catch (Exception)
588             {
589                 return Interop.BOOL.FALSE;
590             }
591         }
592
593         private static unsafe String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride)
594         {
595             const uint LOCALE_SSHORTTIME = 0x00000079;
596             const uint LOCALE_STIMEFORMAT = 0x00001003;
597
598             EnumData data = new EnumData();
599             data.strings = new StringList();
600
601             // Now call the enumeration API. Work is done by our callback function
602             Interop.Kernel32.EnumTimeFormatsEx(EnumTimeCallback, localeName, (uint)dwFlags, Unsafe.AsPointer(ref data));
603
604             if (data.strings.Count > 0)
605             {
606                 // Now we need to allocate our stringarray and populate it
607                 string[] results = data.strings.ToArray();
608
609                 if (!useUserOverride && data.strings.Count > 1)
610                 {
611                     // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override
612                     // The override is the first entry if it is overriden.
613                     // We can check if we have overrides by checking the GetLocaleInfo with no override
614                     // If we do have an override, we don't know if it is a user defined override or if the
615                     // user has just selected one of the predefined formats so we can't just remove it
616                     // but we can move it down.
617                     uint lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT;
618                     string timeFormatNoUserOverride = GetLocaleInfoFromLCType(localeName, lcType, useUserOverride);
619                     if (timeFormatNoUserOverride != "")
620                     {
621                         string firstTimeFormat = results[0];
622                         if (timeFormatNoUserOverride != firstTimeFormat)
623                         {
624                             results[0] = results[1];
625                             results[1] = firstTimeFormat;
626                         }
627                     }
628                 }
629
630                 return results;
631             }
632
633             return null;
634         }
635
636         private static int LocaleNameToLCID(string cultureName)
637         {
638             Debug.Assert(!GlobalizationMode.Invariant);
639
640             return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
641         }
642
643         private static unsafe string LCIDToLocaleName(int culture)
644         {
645             Debug.Assert(!GlobalizationMode.Invariant);
646
647             char *pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination
648             int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
649
650             if (length > 0)
651             {
652                 return new String(pBuffer);
653             }
654
655             return null;
656         }
657
658         private int GetAnsiCodePage(string cultureName)
659         {
660             return GetLocaleInfo(LocaleNumberData.AnsiCodePage);
661         }
662
663         private int GetOemCodePage(string cultureName)
664         {
665             return GetLocaleInfo(LocaleNumberData.OemCodePage);
666         }
667
668         private int GetMacCodePage(string cultureName)
669         {
670             return GetLocaleInfo(LocaleNumberData.MacCodePage);
671         }
672
673         private int GetEbcdicCodePage(string cultureName)
674         {
675             return GetLocaleInfo(LocaleNumberData.EbcdicCodePage);
676         }
677
678         private int GetGeoId(string cultureName)
679         {
680             return GetLocaleInfo(LocaleNumberData.GeoId);
681         }
682
683         private int GetDigitSubstitution(string cultureName)
684         {
685             return GetLocaleInfo(LocaleNumberData.DigitSubstitution);
686         }
687
688         private string GetThreeLetterWindowsLanguageName(string cultureName)
689         {
690             return GetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName);
691         }
692
693         private static CultureInfo[] EnumCultures(CultureTypes types)
694         {
695             Debug.Assert(!GlobalizationMode.Invariant);
696
697             uint flags = 0;
698
699 #pragma warning disable 618
700             if ((types & (CultureTypes.FrameworkCultures | CultureTypes.InstalledWin32Cultures | CultureTypes.ReplacementCultures)) != 0)
701             {
702                 flags |= Interop.Kernel32.LOCALE_NEUTRALDATA | Interop.Kernel32.LOCALE_SPECIFICDATA;
703             }
704 #pragma warning restore 618
705
706             if ((types & CultureTypes.NeutralCultures) != 0)
707             {
708                 flags |= Interop.Kernel32.LOCALE_NEUTRALDATA;
709             }
710
711             if ((types & CultureTypes.SpecificCultures) != 0)
712             {
713                 flags |= Interop.Kernel32.LOCALE_SPECIFICDATA;
714             }
715
716             if ((types & CultureTypes.UserCustomCulture) != 0)
717             {
718                 flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL;
719             }
720
721             if ((types & CultureTypes.ReplacementCultures) != 0)
722             {
723                 flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL;
724             }
725
726             EnumData context = new EnumData();
727             context.strings = new StringList();
728
729             unsafe
730             {
731                 Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, flags, Unsafe.AsPointer(ref context), IntPtr.Zero);
732             }
733
734             CultureInfo [] cultures = new CultureInfo[context.strings.Count];
735             for (int i = 0; i < cultures.Length; i++)
736             {
737                 cultures[i] = new CultureInfo(context.strings[i]);
738             }
739
740             return cultures;
741         }
742
743         private string GetConsoleFallbackName(string cultureName)
744         {
745             return GetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName);
746         }
747
748         internal bool IsFramework
749         {
750             get { return false; }
751         }
752
753         internal bool IsWin32Installed
754         {
755             get { return true; }
756         }
757
758         internal bool IsReplacementCulture
759         {
760             get
761             {
762                 EnumData context = new EnumData();
763                 context.strings = new StringList();
764
765                 unsafe
766                 {
767                     Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT, Unsafe.AsPointer(ref context), IntPtr.Zero);
768                 }
769
770                 for (int i=0; i<context.strings.Count; i++)
771                 {
772                     if (String.Compare(context.strings[i], _sWindowsName, StringComparison.OrdinalIgnoreCase) == 0)
773                         return true;
774                 }
775
776                 return false;
777             }
778         }
779     }
780 }