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