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