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