Merge pull request #2816 from xmcclure/profile-clean-0
[mono.git] / mono / metadata / locales.c
1 /*
2  * locales.c: Culture-sensitive handling
3  *
4  * Authors:
5  *      Dick Porter (dick@ximian.com)
6  *      Mohammad DAMT (mdamt@cdl2000.com)
7  *      Marek Safar (marek.safar@gmail.com)
8  *
9  * Copyright 2003 Ximian, Inc (http://www.ximian.com)
10  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11  * (C) 2003 PT Cakram Datalingga Duaribu  http://www.cdl2000.com
12  * Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
13  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14  */
15
16 #include <config.h>
17 #include <glib.h>
18 #include <string.h>
19
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/metadata/object.h>
22 #include <mono/metadata/appdomain.h>
23 #include <mono/metadata/exception.h>
24 #include <mono/metadata/monitor.h>
25 #include <mono/metadata/locales.h>
26 #include <mono/metadata/culture-info.h>
27 #include <mono/metadata/culture-info-tables.h>
28 #include <mono/utils/bsearch.h>
29
30 #ifndef DISABLE_NORMALIZATION
31 #include <mono/metadata/normalization-tables.h>
32 #endif
33
34 #include <locale.h>
35 #if defined(__APPLE__)
36 #include <CoreFoundation/CoreFoundation.h>
37 #endif
38
39 #undef DEBUG
40
41 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
42                                              gint32 options);
43 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
44                                         gint32 len1, MonoString *str2,
45                                         gint32 off2, gint32 len2,
46                                         gint32 options);
47 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
48                                         gint32 count, MonoString *value,
49                                         MonoBoolean first);
50 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
51                                              gint32 count, gunichar2 value,
52                                              MonoBoolean first);
53
54 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
55
56 static const RegionInfoEntry* region_info_entry_from_lcid (int lcid);
57
58 /* Lazy class loading functions */
59 static GENERATE_GET_CLASS_WITH_CACHE (culture_info, System.Globalization, CultureInfo)
60
61 static int
62 culture_lcid_locator (const void *a, const void *b)
63 {
64         const int *lcid = (const int *)a;
65         const CultureInfoEntry *bb = (const CultureInfoEntry *)b;
66
67         return *lcid - bb->lcid;
68 }
69
70 static int
71 culture_name_locator (const void *a, const void *b)
72 {
73         const char *aa = (const char *)a;
74         const CultureInfoNameEntry *bb = (const CultureInfoNameEntry *)b;
75         int ret;
76         
77         ret = strcmp (aa, idx2string (bb->name));
78
79         return ret;
80 }
81
82 static int
83 region_name_locator (const void *a, const void *b)
84 {
85         const char *aa = (const char *)a;
86         const RegionInfoNameEntry *bb = (const RegionInfoNameEntry *)b;
87         int ret;
88         
89         ret = strcmp (aa, idx2string (bb->name));
90
91         return ret;
92 }
93
94 static MonoArray*
95 create_group_sizes_array (const gint *gs, gint ml)
96 {
97         MonoArray *ret;
98         int i, len = 0;
99
100         for (i = 0; i < ml; i++) {
101                 if (gs [i] == -1)
102                         break;
103                 len++;
104         }
105         
106         ret = mono_array_new_cached (mono_domain_get (),
107                         mono_get_int32_class (), len);
108
109         for(i = 0; i < len; i++)
110                 mono_array_set (ret, gint32, i, gs [i]);
111
112         return ret;
113 }
114
115 static MonoArray*
116 create_names_array_idx (const guint16 *names, int ml)
117 {
118         MonoArray *ret;
119         MonoDomain *domain;
120         int i;
121
122         if (names == NULL)
123                 return NULL;
124
125         domain = mono_domain_get ();
126
127         ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), ml);
128
129         for(i = 0; i < ml; i++)
130                 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
131
132         return ret;
133 }
134
135 static MonoArray*
136 create_names_array_idx_dynamic (const guint16 *names, int ml)
137 {
138         MonoArray *ret;
139         MonoDomain *domain;
140         int i, len = 0;
141
142         if (names == NULL)
143                 return NULL;
144
145         domain = mono_domain_get ();
146
147         for (i = 0; i < ml; i++) {
148                 if (names [i] == 0)
149                         break;
150                 len++;
151         }
152
153         ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), len);
154
155         for(i = 0; i < len; i++)
156                 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
157
158         return ret;
159 }
160
161 MonoBoolean
162 ves_icall_System_Globalization_CalendarData_fill_calendar_data (MonoCalendarData *this_obj, MonoString *name, gint32 calendar_index)
163 {
164         MonoDomain *domain;
165         const DateTimeFormatEntry *dfe;
166         const CultureInfoNameEntry *ne;
167         const CultureInfoEntry *ci;
168         char *n;
169
170         n = mono_string_to_utf8 (name);
171         ne = (const CultureInfoNameEntry *)mono_binary_search (n, culture_name_entries, NUM_CULTURE_ENTRIES,
172                         sizeof (CultureInfoNameEntry), culture_name_locator);
173         g_free (n);
174         if (ne == NULL) {
175                 return FALSE;
176         }
177
178         ci = &culture_entries [ne->culture_entry_index];
179         dfe = &datetime_format_entries [ci->datetime_format_index];
180
181         domain = mono_domain_get ();
182
183         MONO_OBJECT_SETREF (this_obj, NativeName, mono_string_new (domain, idx2string (ci->nativename)));
184         MONO_OBJECT_SETREF (this_obj, ShortDatePatterns, create_names_array_idx_dynamic (dfe->short_date_patterns,
185                         NUM_SHORT_DATE_PATTERNS));
186         MONO_OBJECT_SETREF (this_obj, YearMonthPatterns, create_names_array_idx_dynamic (dfe->year_month_patterns,
187                         NUM_YEAR_MONTH_PATTERNS));
188
189         MONO_OBJECT_SETREF (this_obj, LongDatePatterns, create_names_array_idx_dynamic (dfe->long_date_patterns,
190                         NUM_LONG_DATE_PATTERNS));
191         MONO_OBJECT_SETREF (this_obj, MonthDayPattern, mono_string_new (domain, idx2string (dfe->month_day_pattern)));
192
193         MONO_OBJECT_SETREF (this_obj, DayNames, create_names_array_idx (dfe->day_names, NUM_DAYS));
194         MONO_OBJECT_SETREF (this_obj, AbbreviatedDayNames, create_names_array_idx (dfe->abbreviated_day_names, 
195                         NUM_DAYS));
196         MONO_OBJECT_SETREF (this_obj, SuperShortDayNames, create_names_array_idx (dfe->shortest_day_names, NUM_DAYS));
197         MONO_OBJECT_SETREF (this_obj, MonthNames, create_names_array_idx (dfe->month_names, NUM_MONTHS));
198         MONO_OBJECT_SETREF (this_obj, AbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_names,
199                         NUM_MONTHS));
200         MONO_OBJECT_SETREF (this_obj, GenitiveMonthNames, create_names_array_idx (dfe->month_genitive_names, NUM_MONTHS));
201         MONO_OBJECT_SETREF (this_obj, GenitiveAbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_genitive_names, NUM_MONTHS));
202
203         return TRUE;
204 }
205
206 void
207 ves_icall_System_Globalization_CultureData_fill_culture_data (MonoCultureData *this_obj, gint32 datetime_index)
208 {
209         MonoDomain *domain;
210         const DateTimeFormatEntry *dfe;
211
212         g_assert (datetime_index >= 0);
213
214         dfe = &datetime_format_entries [datetime_index];
215
216         domain = mono_domain_get ();
217
218         MONO_OBJECT_SETREF (this_obj, AMDesignator, mono_string_new (domain, idx2string (dfe->am_designator)));
219         MONO_OBJECT_SETREF (this_obj, PMDesignator, mono_string_new (domain, idx2string (dfe->pm_designator)));
220         MONO_OBJECT_SETREF (this_obj, TimeSeparator, mono_string_new (domain, idx2string (dfe->time_separator)));
221         MONO_OBJECT_SETREF (this_obj, LongTimePatterns, create_names_array_idx_dynamic (dfe->long_time_patterns,
222                         NUM_LONG_TIME_PATTERNS));
223         MONO_OBJECT_SETREF (this_obj, ShortTimePatterns, create_names_array_idx_dynamic (dfe->short_time_patterns,
224                         NUM_SHORT_TIME_PATTERNS));
225         this_obj->FirstDayOfWeek = dfe->first_day_of_week;
226         this_obj->CalendarWeekRule = dfe->calendar_week_rule;
227 }
228
229 void
230 ves_icall_System_Globalization_CultureData_fill_number_data (MonoNumberFormatInfo* number, gint32 number_index)
231 {
232         MonoDomain *domain;
233         const NumberFormatEntry *nfe;
234
235         g_assert (number_index >= 0);
236
237         nfe = &number_format_entries [number_index];
238
239         domain = mono_domain_get ();
240
241         number->currencyDecimalDigits = nfe->currency_decimal_digits;
242         MONO_OBJECT_SETREF (number, currencyDecimalSeparator, mono_string_new (domain,
243                         idx2string (nfe->currency_decimal_separator)));
244         MONO_OBJECT_SETREF (number, currencyGroupSeparator, mono_string_new (domain,
245                         idx2string (nfe->currency_group_separator)));
246         MONO_OBJECT_SETREF (number, currencyGroupSizes, create_group_sizes_array (nfe->currency_group_sizes,
247                         GROUP_SIZE));
248         number->currencyNegativePattern = nfe->currency_negative_pattern;
249         number->currencyPositivePattern = nfe->currency_positive_pattern;
250         MONO_OBJECT_SETREF (number, currencySymbol, mono_string_new (domain, idx2string (nfe->currency_symbol)));
251         MONO_OBJECT_SETREF (number, naNSymbol, mono_string_new (domain, idx2string (nfe->nan_symbol)));
252         MONO_OBJECT_SETREF (number, negativeInfinitySymbol, mono_string_new (domain,
253                         idx2string (nfe->negative_infinity_symbol)));
254         MONO_OBJECT_SETREF (number, negativeSign, mono_string_new (domain, idx2string (nfe->negative_sign)));
255         number->numberDecimalDigits = nfe->number_decimal_digits;
256         MONO_OBJECT_SETREF (number, numberDecimalSeparator, mono_string_new (domain,
257                         idx2string (nfe->number_decimal_separator)));
258         MONO_OBJECT_SETREF (number, numberGroupSeparator, mono_string_new (domain, idx2string (nfe->number_group_separator)));
259         MONO_OBJECT_SETREF (number, numberGroupSizes, create_group_sizes_array (nfe->number_group_sizes,
260                         GROUP_SIZE));
261         number->numberNegativePattern = nfe->number_negative_pattern;
262         number->percentNegativePattern = nfe->percent_negative_pattern;
263         number->percentPositivePattern = nfe->percent_positive_pattern;
264         MONO_OBJECT_SETREF (number, percentSymbol, mono_string_new (domain, idx2string (nfe->percent_symbol)));
265         MONO_OBJECT_SETREF (number, perMilleSymbol, mono_string_new (domain, idx2string (nfe->per_mille_symbol)));
266         MONO_OBJECT_SETREF (number, positiveInfinitySymbol, mono_string_new (domain,
267                         idx2string (nfe->positive_infinity_symbol)));
268         MONO_OBJECT_SETREF (number, positiveSign, mono_string_new (domain, idx2string (nfe->positive_sign)));
269 }
270
271 static MonoBoolean
272 construct_culture (MonoCultureInfo *this_obj, const CultureInfoEntry *ci)
273 {
274         MonoDomain *domain = mono_domain_get ();
275
276         this_obj->lcid = ci->lcid;
277         MONO_OBJECT_SETREF (this_obj, name, mono_string_new (domain, idx2string (ci->name)));
278         MONO_OBJECT_SETREF (this_obj, englishname, mono_string_new (domain, idx2string (ci->englishname)));
279         MONO_OBJECT_SETREF (this_obj, nativename, mono_string_new (domain, idx2string (ci->nativename)));
280         MONO_OBJECT_SETREF (this_obj, win3lang, mono_string_new (domain, idx2string (ci->win3lang)));
281         MONO_OBJECT_SETREF (this_obj, iso3lang, mono_string_new (domain, idx2string (ci->iso3lang)));
282         MONO_OBJECT_SETREF (this_obj, iso2lang, mono_string_new (domain, idx2string (ci->iso2lang)));
283
284         // It's null for neutral cultures
285         if (ci->territory > 0)
286                 MONO_OBJECT_SETREF (this_obj, territory, mono_string_new (domain, idx2string (ci->territory)));
287         MONO_OBJECT_SETREF (this_obj, native_calendar_names, create_names_array_idx (ci->native_calendar_names, NUM_CALENDARS));
288         this_obj->parent_lcid = ci->parent_lcid;
289         this_obj->datetime_index = ci->datetime_format_index;
290         this_obj->number_index = ci->number_format_index;
291         this_obj->calendar_type = ci->calendar_type;
292         this_obj->text_info_data = &ci->text_info;
293         
294         return TRUE;
295 }
296
297 static MonoBoolean
298 construct_region (MonoRegionInfo *this_obj, const RegionInfoEntry *ri)
299 {
300         MonoDomain *domain = mono_domain_get ();
301
302         this_obj->geo_id = ri->geo_id;
303         MONO_OBJECT_SETREF (this_obj, iso2name, mono_string_new (domain, idx2string (ri->iso2name)));
304         MONO_OBJECT_SETREF (this_obj, iso3name, mono_string_new (domain, idx2string (ri->iso3name)));
305         MONO_OBJECT_SETREF (this_obj, win3name, mono_string_new (domain, idx2string (ri->win3name)));
306         MONO_OBJECT_SETREF (this_obj, english_name, mono_string_new (domain, idx2string (ri->english_name)));
307         MONO_OBJECT_SETREF (this_obj, native_name, mono_string_new (domain, idx2string (ri->native_name)));
308         MONO_OBJECT_SETREF (this_obj, currency_symbol, mono_string_new (domain, idx2string (ri->currency_symbol)));
309         MONO_OBJECT_SETREF (this_obj, iso_currency_symbol, mono_string_new (domain, idx2string (ri->iso_currency_symbol)));
310         MONO_OBJECT_SETREF (this_obj, currency_english_name, mono_string_new (domain, idx2string (ri->currency_english_name)));
311         MONO_OBJECT_SETREF (this_obj, currency_native_name, mono_string_new (domain, idx2string (ri->currency_native_name)));
312         
313         return TRUE;
314 }
315
316 static const CultureInfoEntry*
317 culture_info_entry_from_lcid (int lcid)
318 {
319         const CultureInfoEntry *ci;
320
321         ci = (const CultureInfoEntry *)mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
322
323         return ci;
324 }
325
326 static const RegionInfoEntry*
327 region_info_entry_from_lcid (int lcid)
328 {
329         const RegionInfoEntry *entry;
330         const CultureInfoEntry *ne;
331
332         ne = (const CultureInfoEntry *)mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
333
334         if (ne == NULL)
335                 return FALSE;
336
337         entry = &region_entries [ne->region_entry_index];
338
339         return entry;
340 }
341
342 #if defined (__APPLE__)
343 static gchar*
344 get_darwin_locale (void)
345 {
346         static gchar *darwin_locale = NULL;
347         CFLocaleRef locale = NULL;
348         CFStringRef locale_language = NULL;
349         CFStringRef locale_country = NULL;
350         CFStringRef locale_script = NULL;
351         CFStringRef locale_cfstr = NULL;
352         CFIndex bytes_converted;
353         CFIndex bytes_written;
354         CFIndex len;
355         int i;
356
357         if (darwin_locale != NULL)
358                 return g_strdup (darwin_locale);
359
360         locale = CFLocaleCopyCurrent ();
361
362         if (locale) {
363                 locale_language = CFLocaleGetValue (locale, kCFLocaleLanguageCode);
364                 if (locale_language != NULL && CFStringGetBytes(locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
365                         len = bytes_converted + 1;
366
367                         locale_country = CFLocaleGetValue (locale, kCFLocaleCountryCode);
368                         if (locale_country != NULL && CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
369                                 len += bytes_converted + 1;
370
371                                 locale_script = CFLocaleGetValue (locale, kCFLocaleScriptCode);
372                                 if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
373                                         len += bytes_converted + 1;
374                                 }
375
376                                 darwin_locale = (char *) malloc (len + 1);
377                                 CFStringGetBytes (locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) darwin_locale, len, &bytes_converted);
378
379                                 darwin_locale[bytes_converted] = '-';
380                                 bytes_written = bytes_converted + 1;
381                                 if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted) > 0) {
382                                         darwin_locale[bytes_written + bytes_converted] = '-';
383                                         bytes_written += bytes_converted + 1;
384                                 }
385
386                                 CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted);
387                                 darwin_locale[bytes_written + bytes_converted] = '\0';
388                         }
389                 }
390
391                 if (darwin_locale == NULL) {
392                         locale_cfstr = CFLocaleGetIdentifier (locale);
393
394                         if (locale_cfstr) {
395                                 len = CFStringGetMaximumSizeForEncoding (CFStringGetLength (locale_cfstr), kCFStringEncodingMacRoman) + 1;
396                                 darwin_locale = (char *) malloc (len);
397                                 if (!CFStringGetCString (locale_cfstr, darwin_locale, len, kCFStringEncodingMacRoman)) {
398                                         free (darwin_locale);
399                                         CFRelease (locale);
400                                         darwin_locale = NULL;
401                                         return NULL;
402                                 }
403
404                                 for (i = 0; i < strlen (darwin_locale); i++)
405                                         if (darwin_locale [i] == '_')
406                                                 darwin_locale [i] = '-';
407                         }                       
408                 }
409
410                 CFRelease (locale);
411         }
412
413         return g_strdup (darwin_locale);
414 }
415 #endif
416
417 static char *
418 get_posix_locale (void)
419 {
420         const char *locale;
421
422         locale = g_getenv ("LC_ALL");
423         if (locale == NULL) {
424                 locale = g_getenv ("LANG");
425                 if (locale == NULL)
426                         locale = setlocale (LC_ALL, NULL);
427         }
428         if (locale == NULL)
429                 return NULL;
430
431         /* Skip English-only locale 'C' */
432         if (strcmp (locale, "C") == 0)
433                 return NULL;
434
435         return g_strdup (locale);
436 }
437
438
439 static gchar *
440 get_current_locale_name (void)
441 {
442         char *locale;
443         char *p, *ret;
444                 
445 #ifdef HOST_WIN32
446         locale = g_win32_getlocale ();
447 #elif defined (__APPLE__)       
448         locale = get_darwin_locale ();
449         if (!locale)
450                 locale = get_posix_locale ();
451 #else
452         locale = get_posix_locale ();
453 #endif
454
455         if (locale == NULL)
456                 return NULL;
457
458         p = strchr (locale, '.');
459         if (p != NULL)
460                 *p = 0;
461         p = strchr (locale, '@');
462         if (p != NULL)
463                 *p = 0;
464         p = strchr (locale, '_');
465         if (p != NULL)
466                 *p = '-';
467
468         ret = g_ascii_strdown (locale, -1);
469         g_free (locale);
470
471         return ret;
472 }
473
474 MonoString*
475 ves_icall_System_Globalization_CultureInfo_get_current_locale_name (void)
476 {
477         gchar *locale;
478         MonoString* ret;
479         MonoDomain *domain;
480
481         locale = get_current_locale_name ();
482         if (locale == NULL)
483                 return NULL;
484
485         domain = mono_domain_get ();
486         ret = mono_string_new (domain, locale);
487         g_free (locale);
488
489         return ret;
490 }
491
492 MonoBoolean
493 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this_obj,
494                 gint lcid)
495 {
496         const CultureInfoEntry *ci;
497         
498         ci = culture_info_entry_from_lcid (lcid);
499         if(ci == NULL)
500                 return FALSE;
501
502         return construct_culture (this_obj, ci);
503 }
504
505 MonoBoolean
506 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this_obj,
507                 MonoString *name)
508 {
509         const CultureInfoNameEntry *ne;
510         char *n;
511         
512         n = mono_string_to_utf8 (name);
513         ne = (const CultureInfoNameEntry *)mono_binary_search (n, culture_name_entries, NUM_CULTURE_ENTRIES,
514                         sizeof (CultureInfoNameEntry), culture_name_locator);
515
516         if (ne == NULL) {
517                 /*g_print ("ne (%s) is null\n", n);*/
518                 g_free (n);
519                 return FALSE;
520         }
521         g_free (n);
522
523         return construct_culture (this_obj, &culture_entries [ne->culture_entry_index]);
524 }
525 /*
526 MonoBoolean
527 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
528                 MonoString *name)
529 {
530         gchar *locale;
531         gboolean ret;
532
533         locale = mono_string_to_utf8 (name);
534         ret = construct_culture_from_specific_name (ci, locale);
535         g_free (locale);
536
537         return ret;
538 }
539 */
540 MonoBoolean
541 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this_obj,
542                 gint lcid)
543 {
544         const RegionInfoEntry *ri;
545         
546         ri = region_info_entry_from_lcid (lcid);
547         if(ri == NULL)
548                 return FALSE;
549
550         return construct_region (this_obj, ri);
551 }
552
553 MonoBoolean
554 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this_obj,
555                 MonoString *name)
556 {
557         const RegionInfoNameEntry *ne;
558         char *n;
559         
560         n = mono_string_to_utf8 (name);
561         ne = (const RegionInfoNameEntry *)mono_binary_search (n, region_name_entries, NUM_REGION_ENTRIES,
562                 sizeof (RegionInfoNameEntry), region_name_locator);
563
564         if (ne == NULL) {
565                 /*g_print ("ne (%s) is null\n", n);*/
566                 g_free (n);
567                 return FALSE;
568         }
569         g_free (n);
570
571         return construct_region (this_obj, &region_entries [ne->region_entry_index]);
572 }
573
574 MonoArray*
575 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
576                 MonoBoolean specific, MonoBoolean installed)
577 {
578         MonoError error;
579         MonoArray *ret;
580         MonoClass *klass;
581         MonoCultureInfo *culture;
582         MonoDomain *domain;
583         const CultureInfoEntry *ci;
584         gint i, len;
585         gboolean is_neutral;
586
587         domain = mono_domain_get ();
588
589         len = 0;
590         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
591                 ci = &culture_entries [i];
592                 is_neutral = ci->territory == 0;
593                 if ((neutral && is_neutral) || (specific && !is_neutral))
594                         len++;
595         }
596
597         klass = mono_class_get_culture_info_class ();
598
599         /* The InvariantCulture is not in culture_entries */
600         /* We reserve the first slot in the array for it */
601         if (neutral)
602                 len++;
603
604         ret = mono_array_new (domain, klass, len);
605
606         if (len == 0)
607                 return ret;
608
609         len = 0;
610         if (neutral)
611                 mono_array_setref (ret, len++, NULL);
612
613         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
614                 ci = &culture_entries [i];
615                 is_neutral = ci->territory == 0;
616                 if ((neutral && is_neutral) || (specific && !is_neutral)) {
617                         culture = (MonoCultureInfo *) mono_object_new_checked (domain, klass, &error);
618                         if (!is_ok (&error)) goto fail;
619                         mono_runtime_object_init_checked ((MonoObject *) culture, &error);
620                         if (!is_ok (&error)) goto fail;
621                         construct_culture (culture, ci);
622                         culture->use_user_override = TRUE;
623                         mono_array_setref (ret, len++, culture);
624                 }
625         }
626
627         return ret;
628
629 fail:
630         mono_error_set_pending_exception (&error);
631         return ret;
632 }
633
634 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this_obj, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
635 {
636         /* Do a normal ascii string compare, as we only know the
637          * invariant locale if we dont have ICU
638          */
639         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
640                                          options));
641 }
642
643 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this_obj, MonoSortKey *key, MonoString *source, gint32 options)
644 {
645         MonoArray *arr;
646         gint32 keylen, i;
647
648         keylen=mono_string_length (source);
649         
650         arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
651                             keylen);
652         for(i=0; i<keylen; i++) {
653                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
654         }
655         
656         MONO_OBJECT_SETREF (key, key, arr);
657 }
658
659 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
660 {
661         return(string_invariant_indexof (source, sindex, count, value, first));
662 }
663
664 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
665 {
666         return(string_invariant_indexof_char (source, sindex, count, value,
667                                               first));
668 }
669
670 int ves_icall_System_Threading_Thread_current_lcid (void)
671 {
672         /* Invariant */
673         return(0x007F);
674 }
675
676 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
677                                              gint32 options)
678 {
679         gint32 result;
680
681         /* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
682         if (options & CompareOptions_Ordinal) 
683                 return (gint32) c1 - c2;
684         
685         if (options & CompareOptions_IgnoreCase) {
686                 GUnicodeType c1type, c2type;
687
688                 c1type = g_unichar_type (c1);
689                 c2type = g_unichar_type (c2);
690         
691                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
692                         (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
693         } else {
694                 /*
695                  * No options. Kana, symbol and spacing options don't
696                  * apply to the invariant culture.
697                  */
698
699                 /*
700                  * FIXME: here we must use the information from c1type and c2type
701                  * to find out the proper collation, even on the InvariantCulture, the
702                  * sorting is not done by computing the unicode values, but their
703                  * actual sort order.
704                  */
705                 result = (gint32) c1 - c2;
706         }
707
708         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
709 }
710
711 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
712                                         gint32 len1, MonoString *str2,
713                                         gint32 off2, gint32 len2,
714                                         gint32 options)
715 {
716         /* c translation of C# code from old string.cs.. :) */
717         gint32 length;
718         gint32 charcmp;
719         gunichar2 *ustr1;
720         gunichar2 *ustr2;
721         gint32 pos;
722
723         if(len1 >= len2) {
724                 length=len1;
725         } else {
726                 length=len2;
727         }
728
729         ustr1 = mono_string_chars(str1)+off1;
730         ustr2 = mono_string_chars(str2)+off2;
731
732         pos = 0;
733
734         for (pos = 0; pos != length; pos++) {
735                 if (pos >= len1 || pos >= len2)
736                         break;
737
738                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
739                                                         options);
740                 if (charcmp != 0) {
741                         return(charcmp);
742                 }
743         }
744
745         /* the lesser wins, so if we have looped until length we just
746          * need to check the last char
747          */
748         if (pos == length) {
749                 return(string_invariant_compare_char(ustr1[pos - 1],
750                                                      ustr2[pos - 1], options));
751         }
752
753         /* Test if one of the strings has been compared to the end */
754         if (pos >= len1) {
755                 if (pos >= len2) {
756                         return(0);
757                 } else {
758                         return(-1);
759                 }
760         } else if (pos >= len2) {
761                 return(1);
762         }
763
764         /* if not, check our last char only.. (can this happen?) */
765         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
766 }
767
768 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
769                                         gint32 count, MonoString *value,
770                                         MonoBoolean first)
771 {
772         gint32 lencmpstr;
773         gunichar2 *src;
774         gunichar2 *cmpstr;
775         gint32 pos,i;
776         
777         lencmpstr = mono_string_length(value);
778         
779         src = mono_string_chars(source);
780         cmpstr = mono_string_chars(value);
781
782         if(first) {
783                 count -= lencmpstr;
784                 for(pos=sindex;pos <= sindex+count;pos++) {
785                         for(i=0;src[pos+i]==cmpstr[i];) {
786                                 if(++i==lencmpstr) {
787                                         return(pos);
788                                 }
789                         }
790                 }
791                 
792                 return(-1);
793         } else {
794                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
795                         if(memcmp (src+pos, cmpstr,
796                                    lencmpstr*sizeof(gunichar2))==0) {
797                                 return(pos);
798                         }
799                 }
800                 
801                 return(-1);
802         }
803 }
804
805 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
806                                              gint32 count, gunichar2 value,
807                                              MonoBoolean first)
808 {
809         gint32 pos;
810         gunichar2 *src;
811
812         src = mono_string_chars(source);
813         if(first) {
814                 for (pos = sindex; pos != count + sindex; pos++) {
815                         if (src [pos] == value) {
816                                 return(pos);
817                         }
818                 }
819
820                 return(-1);
821         } else {
822                 for (pos = sindex; pos > sindex - count; pos--) {
823                         if (src [pos] == value)
824                                 return(pos);
825                 }
826
827                 return(-1);
828         }
829 }
830
831 void ves_icall_System_Text_Normalization_load_normalization_resource (guint8 **argProps,
832                                                                                                                                           guint8 **argMappedChars,
833                                                                                                                                           guint8 **argCharMapIndex,
834                                                                                                                                           guint8 **argHelperIndex,
835                                                                                                                                           guint8 **argMapIdxToComposite,
836                                                                                                                                           guint8 **argCombiningClass)
837 {
838 #ifdef DISABLE_NORMALIZATION
839         mono_set_pending_exception (mono_get_exception_not_supported ("This runtime has been compiled without string normalization support."));
840         return;
841 #else
842         *argProps = (guint8*)props;
843         *argMappedChars = (guint8*) mappedChars;
844         *argCharMapIndex = (guint8*) charMapIndex;
845         *argHelperIndex = (guint8*) helperIndex;
846         *argMapIdxToComposite = (guint8*) mapIdxToComposite;
847         *argCombiningClass = (guint8*)combiningClass;
848 #endif
849 }
850
851