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