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