Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[mono.git] / mono / metadata / locales.c
1 /*
2  * locales.c: Culture-sensitive handling
3  *
4  * Authors:
5  *      Dick Porter (dick@ximian.com)
6  *      Mohammad DAMT (mdamt@cdl2000.com)
7  *      Marek Safar (marek.safar@gmail.com)
8  *
9  * Copyright 2003 Ximian, Inc (http://www.ximian.com)
10  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11  * (C) 2003 PT Cakram Datalingga Duaribu  http://www.cdl2000.com
12  * Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
13  */
14
15 #include <config.h>
16 #include <glib.h>
17 #include <string.h>
18
19 #include <mono/metadata/debug-helpers.h>
20 #include <mono/metadata/object.h>
21 #include <mono/metadata/appdomain.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/monitor.h>
24 #include <mono/metadata/locales.h>
25 #include <mono/metadata/culture-info.h>
26 #include <mono/metadata/culture-info-tables.h>
27 #include <mono/utils/bsearch.h>
28
29 #ifndef DISABLE_NORMALIZATION
30 #include <mono/metadata/normalization-tables.h>
31 #endif
32
33 #include <locale.h>
34 #if defined(__APPLE__)
35 #include <CoreFoundation/CoreFoundation.h>
36 #endif
37
38 #undef DEBUG
39
40 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
41                                              gint32 options);
42 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
43                                         gint32 len1, MonoString *str2,
44                                         gint32 off2, gint32 len2,
45                                         gint32 options);
46 static MonoString *string_invariant_replace (MonoString *me,
47                                              MonoString *oldValue,
48                                              MonoString *newValue);
49 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
50                                         gint32 count, MonoString *value,
51                                         MonoBoolean first);
52 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
53                                              gint32 count, gunichar2 value,
54                                              MonoBoolean first);
55
56 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
57
58 static const RegionInfoEntry* region_info_entry_from_lcid (int lcid);
59
60 static int
61 culture_lcid_locator (const void *a, const void *b)
62 {
63         const int *lcid = a;
64         const CultureInfoEntry *bb = b;
65
66         return *lcid - bb->lcid;
67 }
68
69 static int
70 culture_name_locator (const void *a, const void *b)
71 {
72         const char *aa = a;
73         const CultureInfoNameEntry *bb = b;
74         int ret;
75         
76         ret = strcmp (aa, idx2string (bb->name));
77
78         return ret;
79 }
80
81 static int
82 region_name_locator (const void *a, const void *b)
83 {
84         const char *aa = a;
85         const RegionInfoNameEntry *bb = b;
86         int ret;
87         
88         ret = strcmp (aa, idx2string (bb->name));
89
90         return ret;
91 }
92
93 static MonoArray*
94 create_group_sizes_array (const gint *gs, gint ml)
95 {
96         MonoArray *ret;
97         int i, len = 0;
98
99         for (i = 0; i < ml; i++) {
100                 if (gs [i] == -1)
101                         break;
102                 len++;
103         }
104         
105         ret = mono_array_new_cached (mono_domain_get (),
106                         mono_get_int32_class (), len);
107
108         for(i = 0; i < len; i++)
109                 mono_array_set (ret, gint32, i, gs [i]);
110
111         return ret;
112 }
113
114 static MonoArray*
115 create_names_array_idx (const guint16 *names, int ml)
116 {
117         MonoArray *ret;
118         MonoDomain *domain;
119         int i;
120
121         if (names == NULL)
122                 return NULL;
123
124         domain = mono_domain_get ();
125
126         ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), ml);
127
128         for(i = 0; i < ml; i++)
129                 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
130
131         return ret;
132 }
133
134 static MonoArray*
135 create_names_array_idx_dynamic (const guint16 *names, int ml)
136 {
137         MonoArray *ret;
138         MonoDomain *domain;
139         int i, len = 0;
140
141         if (names == NULL)
142                 return NULL;
143
144         domain = mono_domain_get ();
145
146         for (i = 0; i < ml; i++) {
147                 if (names [i] == 0)
148                         break;
149                 len++;
150         }
151
152         ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), len);
153
154         for(i = 0; i < len; i++)
155                 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
156
157         return ret;
158 }
159
160 MonoBoolean
161 ves_icall_System_Globalization_CalendarData_fill_calendar_data (MonoCalendarData *this_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 = 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 = 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 = 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 = 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 = 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         MonoArray *ret;
578         MonoClass *class;
579         MonoCultureInfo *culture;
580         MonoDomain *domain;
581         const CultureInfoEntry *ci;
582         gint i, len;
583         gboolean is_neutral;
584
585         domain = mono_domain_get ();
586
587         len = 0;
588         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
589                 ci = &culture_entries [i];
590                 is_neutral = ci->territory == 0;
591                 if ((neutral && is_neutral) || (specific && !is_neutral))
592                         len++;
593         }
594
595         class = mono_class_from_name (mono_get_corlib (),
596                         "System.Globalization", "CultureInfo");
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, class, 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 (domain, class);
617                         mono_runtime_object_init ((MonoObject *) culture);
618                         construct_culture (culture, ci);
619                         culture->use_user_override = TRUE;
620                         mono_array_setref (ret, len++, culture);
621                 }
622         }
623
624         return ret;
625 }
626
627 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)
628 {
629         /* Do a normal ascii string compare, as we only know the
630          * invariant locale if we dont have ICU
631          */
632         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
633                                          options));
634 }
635
636 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this_obj, MonoSortKey *key, MonoString *source, gint32 options)
637 {
638         MonoArray *arr;
639         gint32 keylen, i;
640
641         keylen=mono_string_length (source);
642         
643         arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
644                             keylen);
645         for(i=0; i<keylen; i++) {
646                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
647         }
648         
649         MONO_OBJECT_SETREF (key, key, arr);
650 }
651
652 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
653 {
654         return(string_invariant_indexof (source, sindex, count, value, first));
655 }
656
657 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
658 {
659         return(string_invariant_indexof_char (source, sindex, count, value,
660                                               first));
661 }
662
663 int ves_icall_System_Threading_Thread_current_lcid (void)
664 {
665         /* Invariant */
666         return(0x007F);
667 }
668
669 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this_obj, MonoString *old, MonoString *new, MonoCompareInfo *comp)
670 {
671         /* Do a normal ascii string compare and replace, as we only
672          * know the invariant locale if we dont have ICU
673          */
674         return(string_invariant_replace (this_obj, old, new));
675 }
676
677 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
678                                              gint32 options)
679 {
680         gint32 result;
681
682         /* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
683         if (options & CompareOptions_Ordinal) 
684                 return (gint32) c1 - c2;
685         
686         if (options & CompareOptions_IgnoreCase) {
687                 GUnicodeType c1type, c2type;
688
689                 c1type = g_unichar_type (c1);
690                 c2type = g_unichar_type (c2);
691         
692                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
693                         (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
694         } else {
695                 /*
696                  * No options. Kana, symbol and spacing options don't
697                  * apply to the invariant culture.
698                  */
699
700                 /*
701                  * FIXME: here we must use the information from c1type and c2type
702                  * to find out the proper collation, even on the InvariantCulture, the
703                  * sorting is not done by computing the unicode values, but their
704                  * actual sort order.
705                  */
706                 result = (gint32) c1 - c2;
707         }
708
709         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
710 }
711
712 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
713                                         gint32 len1, MonoString *str2,
714                                         gint32 off2, gint32 len2,
715                                         gint32 options)
716 {
717         /* c translation of C# code from old string.cs.. :) */
718         gint32 length;
719         gint32 charcmp;
720         gunichar2 *ustr1;
721         gunichar2 *ustr2;
722         gint32 pos;
723
724         if(len1 >= len2) {
725                 length=len1;
726         } else {
727                 length=len2;
728         }
729
730         ustr1 = mono_string_chars(str1)+off1;
731         ustr2 = mono_string_chars(str2)+off2;
732
733         pos = 0;
734
735         for (pos = 0; pos != length; pos++) {
736                 if (pos >= len1 || pos >= len2)
737                         break;
738
739                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
740                                                         options);
741                 if (charcmp != 0) {
742                         return(charcmp);
743                 }
744         }
745
746         /* the lesser wins, so if we have looped until length we just
747          * need to check the last char
748          */
749         if (pos == length) {
750                 return(string_invariant_compare_char(ustr1[pos - 1],
751                                                      ustr2[pos - 1], options));
752         }
753
754         /* Test if one of the strings has been compared to the end */
755         if (pos >= len1) {
756                 if (pos >= len2) {
757                         return(0);
758                 } else {
759                         return(-1);
760                 }
761         } else if (pos >= len2) {
762                 return(1);
763         }
764
765         /* if not, check our last char only.. (can this happen?) */
766         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
767 }
768
769 static MonoString *string_invariant_replace (MonoString *me,
770                                              MonoString *oldValue,
771                                              MonoString *newValue)
772 {
773         MonoString *ret;
774         gunichar2 *src;
775         gunichar2 *dest=NULL; /* shut gcc up */
776         gunichar2 *oldstr;
777         gunichar2 *newstr=NULL; /* shut gcc up here too */
778         gint32 i, destpos;
779         gint32 occurr;
780         gint32 newsize;
781         gint32 oldstrlen;
782         gint32 newstrlen;
783         gint32 srclen;
784
785         occurr = 0;
786         destpos = 0;
787
788         oldstr = mono_string_chars(oldValue);
789         oldstrlen = mono_string_length(oldValue);
790
791         if (NULL != newValue) {
792                 newstr = mono_string_chars(newValue);
793                 newstrlen = mono_string_length(newValue);
794         } else
795                 newstrlen = 0;
796
797         src = mono_string_chars(me);
798         srclen = mono_string_length(me);
799
800         if (oldstrlen != newstrlen) {
801                 i = 0;
802                 while (i <= srclen - oldstrlen) {
803                         if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
804                                 occurr++;
805                                 i += oldstrlen;
806                         }
807                         else
808                                 i ++;
809                 }
810                 if (occurr == 0)
811                         return me;
812                 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
813         } else
814                 newsize = srclen;
815
816         ret = NULL;
817         i = 0;
818         while (i < srclen) {
819                 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
820                         if (ret == NULL) {
821                                 ret = mono_string_new_size( mono_domain_get (), newsize);
822                                 dest = mono_string_chars(ret);
823                                 memcpy (dest, src, i * sizeof(gunichar2));
824                         }
825                         if (newstrlen > 0) {
826                                 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
827                                 destpos += newstrlen;
828                         }
829                         i += oldstrlen;
830                         continue;
831                 } else if (ret != NULL) {
832                         dest[destpos] = src[i];
833                 }
834                 destpos++;
835                 i++;
836         }
837         
838         if (ret == NULL)
839                 return me;
840
841         return ret;
842 }
843
844 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
845                                         gint32 count, MonoString *value,
846                                         MonoBoolean first)
847 {
848         gint32 lencmpstr;
849         gunichar2 *src;
850         gunichar2 *cmpstr;
851         gint32 pos,i;
852         
853         lencmpstr = mono_string_length(value);
854         
855         src = mono_string_chars(source);
856         cmpstr = mono_string_chars(value);
857
858         if(first) {
859                 count -= lencmpstr;
860                 for(pos=sindex;pos <= sindex+count;pos++) {
861                         for(i=0;src[pos+i]==cmpstr[i];) {
862                                 if(++i==lencmpstr) {
863                                         return(pos);
864                                 }
865                         }
866                 }
867                 
868                 return(-1);
869         } else {
870                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
871                         if(memcmp (src+pos, cmpstr,
872                                    lencmpstr*sizeof(gunichar2))==0) {
873                                 return(pos);
874                         }
875                 }
876                 
877                 return(-1);
878         }
879 }
880
881 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
882                                              gint32 count, gunichar2 value,
883                                              MonoBoolean first)
884 {
885         gint32 pos;
886         gunichar2 *src;
887
888         src = mono_string_chars(source);
889         if(first) {
890                 for (pos = sindex; pos != count + sindex; pos++) {
891                         if (src [pos] == value) {
892                                 return(pos);
893                         }
894                 }
895
896                 return(-1);
897         } else {
898                 for (pos = sindex; pos > sindex - count; pos--) {
899                         if (src [pos] == value)
900                                 return(pos);
901                 }
902
903                 return(-1);
904         }
905 }
906
907 void load_normalization_resource (guint8 **argProps,
908                                   guint8 **argMappedChars,
909                                   guint8 **argCharMapIndex,
910                                   guint8 **argHelperIndex,
911                                   guint8 **argMapIdxToComposite,
912                                   guint8 **argCombiningClass)
913 {
914 #ifdef DISABLE_NORMALIZATION
915         mono_set_pending_exception (mono_get_exception_not_supported ("This runtime has been compiled without string normalization support."));
916         return;
917 #else
918         *argProps = (guint8*)props;
919         *argMappedChars = (guint8*) mappedChars;
920         *argCharMapIndex = (guint8*) charMapIndex;
921         *argHelperIndex = (guint8*) helperIndex;
922         *argMapIdxToComposite = (guint8*) mapIdxToComposite;
923         *argCombiningClass = (guint8*)combiningClass;
924 #endif
925 }
926
927