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