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