2004-06-05 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mono / metadata / locales.c
1 /*
2  * locales.c: Culture-sensitive handling
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *      Mohammad DAMT (mdamt@cdl2000.com)
7  *
8  * (C) 2003 Ximian, Inc.
9  * (C) 2003 PT Cakram Datalingga Duaribu  http://www.cdl2000.com
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/object.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/monitor.h>
21 #include <mono/metadata/locales.h>
22 #include <mono/metadata/culture-info.h>
23 #include <mono/metadata/culture-info-tables.h>
24
25
26 #include <locale.h>
27
28 #undef DEBUG
29
30 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
31                                              gint32 options);
32 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
33                                         gint32 len1, MonoString *str2,
34                                         gint32 off2, gint32 len2,
35                                         gint32 options);
36 static MonoString *string_invariant_replace (MonoString *me,
37                                              MonoString *oldValue,
38                                              MonoString *newValue);
39 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
40                                         gint32 count, MonoString *value,
41                                         MonoBoolean first);
42 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
43                                              gint32 count, gunichar2 value,
44                                              MonoBoolean first);
45
46 static MonoString *string_invariant_tolower (MonoString *this);
47 static MonoString *string_invariant_toupper (MonoString *this);
48 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
49
50 static int
51 culture_lcid_locator (const void *a, const void *b)
52 {
53         const CultureInfoEntry *aa = a;
54         const CultureInfoEntry *bb = b;
55
56         return (aa->lcid - bb->lcid);
57 }
58
59 static int
60 culture_name_locator (const void *a, const void *b)
61 {
62         const CultureInfoNameEntry *aa = a;
63         const CultureInfoNameEntry *bb = b;
64         int ret;
65         
66         ret = strcmp (aa->name, bb->name);
67
68         return ret;
69 }
70
71 static MonoArray*
72 create_group_sizes_array (const gint *gs, gint ml)
73 {
74         MonoArray *ret;
75         int i, len = 0;
76
77         for (i = 0; i < ml; i++) {
78                 if (gs [i] == -1)
79                         break;
80                 len++;
81         }
82         
83         ret = mono_array_new (mono_domain_get (),
84                         mono_defaults.int32_class, len);
85
86         for(i = 0; i < len; i++)
87                 mono_array_set (ret, gint32, i, gs [i]);
88
89         return ret;
90 }
91
92 static MonoArray*
93 create_names_array (const gchar **names, int ml)
94 {
95         MonoArray *ret;
96         MonoDomain *domain;
97         int i, len = 0;
98
99         if (names == NULL)
100                 return NULL;
101
102         domain = mono_domain_get ();
103
104         for (i = 0; i < ml; i++) {
105                 if (names [i] == NULL)
106                         break;
107                 len++;
108         }
109
110         ret = mono_array_new (mono_domain_get (), mono_defaults.string_class, len);
111
112         for(i = 0; i < len; i++)
113                 mono_array_set (ret, MonoString *, i, mono_string_new (domain, names [i]));
114
115         return ret;
116 }
117
118 void
119 ves_icall_System_Globalization_CultureInfo_construct_datetime_format (MonoCultureInfo *this)
120 {
121         MonoDomain *domain;
122         MonoDateTimeFormatInfo *datetime;
123         const DateTimeFormatEntry *dfe;
124
125         MONO_ARCH_SAVE_REGS;
126
127         g_assert (this->datetime_index >= 0);
128
129         datetime = this->datetime_format;
130         dfe = &datetime_format_entries [this->datetime_index];
131
132         domain = mono_domain_get ();
133
134         datetime->AbbreviatedDayNames = create_names_array (dfe->abbreviated_day_names,
135                         NUM_DAYS);
136         datetime->AbbreviatedMonthNames = create_names_array (dfe->abbreviated_month_names,
137                         NUM_MONTHS);
138         datetime->AMDesignator = mono_string_new (domain, dfe->am_designator);
139         datetime->CalendarWeekRule = dfe->calendar_week_rule;
140         datetime->DateSeparator = mono_string_new (domain, dfe->date_separator);
141         datetime->DayNames = create_names_array (dfe->day_names, NUM_DAYS);
142         datetime->FirstDayOfWeek = dfe->first_day_of_week;
143         datetime->FullDateTimePattern = mono_string_new (domain, dfe->full_date_time_pattern);
144         datetime->LongDatePattern = mono_string_new (domain, dfe->long_date_pattern);
145         datetime->LongTimePattern = mono_string_new (domain, dfe->long_time_pattern);
146         datetime->MonthDayPattern = mono_string_new (domain, dfe->month_day_pattern);
147         datetime->MonthNames = create_names_array (dfe->month_names, NUM_MONTHS);
148         datetime->PMDesignator = mono_string_new (domain, dfe->pm_designator);
149         datetime->ShortDatePattern = mono_string_new (domain, dfe->short_date_pattern);
150         datetime->ShortTimePattern = mono_string_new (domain, dfe->short_time_pattern);
151         datetime->TimeSeparator = mono_string_new (domain, dfe->time_separator);
152         datetime->YearMonthPattern = mono_string_new (domain, dfe->year_month_pattern);
153
154 }
155
156 void
157 ves_icall_System_Globalization_CultureInfo_construct_number_format (MonoCultureInfo *this)
158 {
159         MonoDomain *domain;
160         MonoNumberFormatInfo *number;
161         const NumberFormatEntry *nfe;
162
163         MONO_ARCH_SAVE_REGS;
164
165         g_assert (this->number_format != 0);
166
167         number = this->number_format;
168         nfe = &number_format_entries [this->number_index];
169
170         domain = mono_domain_get ();
171
172         number->currencyDecimalDigits = nfe->currency_decimal_digits;
173         number->currencyDecimalSeparator = mono_string_new (domain,
174                         nfe->currency_decimal_separator);
175         number->currencyGroupSeparator = mono_string_new (domain,
176                         nfe->currency_group_separator);
177         number->currencyGroupSizes = create_group_sizes_array (nfe->currency_group_sizes,
178                         GROUP_SIZE);
179         number->currencyNegativePattern = nfe->currency_negative_pattern;
180         number->currencyPositivePattern = nfe->currency_positive_pattern;
181         number->currencySymbol = mono_string_new (domain, nfe->currency_symbol);
182         number->naNSymbol = mono_string_new (domain, nfe->nan_symbol);
183         number->negativeInfinitySymbol = mono_string_new (domain,
184                         nfe->negative_infinity_symbol);
185         number->negativeSign = mono_string_new (domain, nfe->negative_sign);
186         number->numberDecimalDigits = nfe->number_decimal_digits;
187         number->numberDecimalSeparator = mono_string_new (domain,
188                         nfe->number_decimal_separator);
189         number->numberGroupSeparator = mono_string_new (domain, nfe->number_group_separator);
190         number->numberGroupSizes = create_group_sizes_array (nfe->number_group_sizes,
191                         GROUP_SIZE);
192         number->numberNegativePattern = nfe->number_negative_pattern;
193         number->percentDecimalDigits = nfe->percent_decimal_digits;
194         number->percentDecimalSeparator = mono_string_new (domain,
195                         nfe->percent_decimal_separator);
196         number->percentGroupSeparator = mono_string_new (domain,
197                         nfe->percent_group_separator);
198         number->percentGroupSizes = create_group_sizes_array (nfe->percent_group_sizes,
199                         GROUP_SIZE);
200         number->percentNegativePattern = nfe->percent_negative_pattern;
201         number->percentPositivePattern = nfe->percent_positive_pattern;
202         number->percentSymbol = mono_string_new (domain, nfe->percent_symbol);
203         number->perMilleSymbol = mono_string_new (domain, nfe->per_mille_symbol);
204         number->positiveInfinitySymbol = mono_string_new (domain,
205                         nfe->positive_infinity_symbol);
206         number->positiveSign = mono_string_new (domain, nfe->positive_sign);
207 }
208
209 static MonoBoolean
210 construct_culture (MonoCultureInfo *this, const CultureInfoEntry *ci)
211 {
212         MonoDomain *domain = mono_domain_get ();
213
214         this->lcid = ci->lcid;
215         this->name = mono_string_new (domain, ci->name);
216         this->icu_name = mono_string_new (domain, ci->icu_name);
217         this->displayname = mono_string_new (domain, ci->displayname);
218         this->englishname = mono_string_new (domain, ci->englishname);
219         this->nativename = mono_string_new (domain, ci->nativename);
220         this->win3lang = mono_string_new (domain, ci->win3lang);
221         this->iso3lang = mono_string_new (domain, ci->iso3lang);
222         this->iso2lang = mono_string_new (domain, ci->iso2lang);
223         this->parent_lcid = ci->parent_lcid;
224         this->specific_lcid = ci->specific_lcid;
225         this->datetime_index = ci->datetime_format_index;
226         this->number_index = ci->number_format_index;
227         this->calendar_data = ci->calendar_data;
228         
229         return TRUE;
230 }
231
232 gboolean
233 construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name)
234 {
235         const CultureInfoEntry *entry;
236         CultureInfoNameEntry key;
237         const CultureInfoNameEntry *ne;
238
239         MONO_ARCH_SAVE_REGS;
240
241         key.name = name;
242         ne = bsearch (&key, culture_name_entries, NUM_CULTURE_ENTRIES,
243                         sizeof (CultureInfoNameEntry), culture_name_locator);
244
245         if (ne == NULL)
246                 return FALSE;
247
248         entry = &culture_entries [ne->culture_entry_index];
249
250         /* try avoiding another lookup, often the culture is its own specific culture */
251         if (entry->lcid != entry->specific_lcid)
252                 entry = culture_info_entry_from_lcid (entry->specific_lcid);
253
254         return construct_culture (ci, entry);
255 }
256
257 static const CultureInfoEntry*
258 culture_info_entry_from_lcid (int lcid)
259 {
260         const CultureInfoEntry *ci;
261         CultureInfoEntry key;
262
263         key.lcid = lcid;
264         ci = bsearch (&key, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
265
266         return ci;
267 }
268
269 /**
270  * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu)
271  * Copyright (c) 1995-2003 International Business Machines Corporation and others
272  * All rights reserved.
273  */
274 static gchar*
275 get_posix_locale (void)
276 {
277         const gchar* posix_locale = NULL;
278
279         posix_locale = g_getenv("LC_ALL");
280         if (posix_locale == 0) {
281                 posix_locale = g_getenv("LANG");
282                 if (posix_locale == 0) {
283                         posix_locale = setlocale(LC_ALL, NULL);
284                 }
285         }
286
287         if (posix_locale == NULL)
288                 return NULL;
289
290         if ((strcmp ("C", posix_locale) == 0) || (strchr (posix_locale, ' ') != NULL)
291                         || (strchr (posix_locale, '/') != NULL)) {
292                 /**
293                  * HPUX returns 'C C C C C C C'
294                  * Solaris can return /en_US/C/C/C/C/C on the second try.
295                  * Maybe we got some garbage.
296                  */
297                 return NULL;
298         }
299
300         return g_strdup (posix_locale);
301 }
302
303 static gchar*
304 get_current_locale_name (void)
305 {
306         gchar *locale;
307         gchar *corrected = NULL;
308         const gchar *p;
309         gchar *c;
310
311 #ifdef PLATFORM_WIN32
312         locale = g_win32_getlocale ();
313 #else   
314         locale = get_posix_locale ();
315 #endif  
316
317         if (locale == NULL)
318                 return NULL;
319
320         if ((p = strchr (locale, '.')) != NULL) {
321                 /* assume new locale can't be larger than old one? */
322                 corrected = malloc (strlen (locale));
323                 strncpy (corrected, locale, p - locale);
324                 corrected [p - locale] = 0;
325
326                 /* do not copy after the @ */
327                 if ((p = strchr (corrected, '@')) != NULL)
328                         corrected [p - corrected] = 0;
329         }
330
331         /* Note that we scan the *uncorrected* ID. */
332         if ((p = strrchr (locale, '@')) != NULL) {
333
334                 /**
335                  * In Mono we dont handle the '@' modifier because we do
336                  * not have any cultures that use it. We just trim it
337                  * off of the end of the name.
338                  */
339
340                 if (corrected == NULL) {
341                         corrected = malloc (strlen (locale));
342                         strncpy (corrected, locale, p - locale);
343                         corrected [p - locale] = 0;
344                 }
345         }
346
347         if (corrected == NULL)
348                 corrected = locale;
349         else
350                 g_free (locale);
351
352         if ((c = strchr (corrected, '_')) != NULL)
353                 *c = '-';
354
355         g_strdown (corrected);
356
357         return corrected;
358 }        
359
360 MonoBoolean
361 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (MonoCultureInfo *ci)
362 {
363         gchar *locale;
364         gboolean ret;
365
366         MONO_ARCH_SAVE_REGS;
367
368         locale = get_current_locale_name ();
369         if (locale == NULL)
370                 return FALSE;
371
372         ret = construct_culture_from_specific_name (ci, locale);
373         g_free (locale);
374
375         return ret;
376 }
377
378 MonoBoolean
379 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this,
380                 gint lcid)
381 {
382         const CultureInfoEntry *ci;
383         
384         MONO_ARCH_SAVE_REGS;
385
386         ci = culture_info_entry_from_lcid (lcid);
387         if(ci == NULL)
388                 return FALSE;
389
390         return construct_culture (this, ci);
391 }
392
393 MonoBoolean
394 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this,
395                 MonoString *name)
396 {
397         CultureInfoNameEntry key;
398         const CultureInfoNameEntry *ne;
399         
400         MONO_ARCH_SAVE_REGS;
401
402         key.name = mono_string_to_utf8 (name);
403         ne = bsearch (&key, culture_name_entries, NUM_CULTURE_ENTRIES,
404                         sizeof (CultureInfoNameEntry), culture_name_locator);
405
406         g_free ((gpointer) key.name);
407
408         if (ne == NULL) {
409                 g_print ("ne (%s) is null\n", key.name);
410                 return FALSE;
411         }
412
413         return construct_culture (this, &culture_entries [ne->culture_entry_index]);
414 }
415
416 MonoBoolean
417 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
418                 MonoString *name)
419 {
420         gchar *locale;
421         gboolean ret;
422
423         MONO_ARCH_SAVE_REGS;
424
425         locale = mono_string_to_utf8 (name);
426         ret = construct_culture_from_specific_name (ci, locale);
427         g_free (locale);
428
429         return ret;
430 }
431
432 MonoArray*
433 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
434                 MonoBoolean specific, MonoBoolean installed)
435 {
436         MonoArray *ret;
437         MonoClass *class;
438         MonoCultureInfo *culture;
439         MonoDomain *domain;
440         const CultureInfoEntry *ci;
441         gint i, len;
442         gboolean is_neutral;
443
444         MONO_ARCH_SAVE_REGS;
445
446         domain = mono_domain_get ();
447
448         len = 0;
449         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
450                 ci = &culture_entries [i];
451                 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
452                 if ((neutral && is_neutral) || (specific && !is_neutral))
453                         len++;
454         }
455
456         class = mono_class_from_name (mono_defaults.corlib,
457                         "System.Globalization", "CultureInfo");
458         ret = mono_array_new (domain, class, len);
459
460         if (len == 0)
461                 return ret;
462
463         len = 0;
464         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
465                 ci = &culture_entries [i];
466                 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
467                 if ((neutral && is_neutral) || (specific && !is_neutral)) {
468                         culture = (MonoCultureInfo *) mono_object_new (domain, class);
469                         mono_runtime_object_init ((MonoObject *) culture);
470                         construct_culture (culture, ci);
471                         mono_array_set (ret, MonoCultureInfo *, len++, culture);
472                 }
473         }
474
475         return ret;
476 }
477
478 /**
479  * Set is_neutral and return TRUE if the culture is found. If it is not found return FALSE.
480  */
481 MonoBoolean
482 ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral (gint lcid, MonoBoolean *is_neutral)
483 {
484         const CultureInfoEntry *entry;
485
486         MONO_ARCH_SAVE_REGS;
487
488         entry = culture_info_entry_from_lcid (lcid);
489
490         if (entry == NULL)
491                 return FALSE;
492
493         *is_neutral = (entry->specific_lcid == 0);
494
495         return TRUE;
496 }
497
498 #ifdef HAVE_ICU
499
500 #include <unicode/utypes.h>
501 #include <unicode/ustring.h>
502 #include <unicode/ures.h>
503 #include <unicode/ucol.h>
504 #include <unicode/usearch.h>
505
506 static MonoString *monostring_from_resource_index (const UResourceBundle *bundle, int32_t idx)
507 {
508         gunichar2 *res_str;
509         int32_t res_strlen;
510         UErrorCode ec;
511         
512         ec=U_ZERO_ERROR;
513         res_str=(gunichar2 *)ures_getStringByIndex (bundle, idx, &res_strlen,
514                                                    &ec);
515         if(U_FAILURE (ec)) {
516                 return(NULL);
517         }
518
519         return(mono_string_from_utf16 (res_str));
520 }
521
522 static UResourceBundle *open_subbundle (const UResourceBundle *bundle,
523                                         const char *name, int32_t req_count)
524 {
525         UResourceBundle *subbundle;
526         UErrorCode ec;
527         int32_t count;
528         
529         ec=U_ZERO_ERROR;
530         subbundle=ures_getByKey (bundle, name, NULL, &ec);
531         if(U_FAILURE (ec)) {
532                 /* Couldn't find the subbundle */
533                 return(NULL);
534         }
535         
536         count=ures_countArrayItems (bundle, name, &ec);
537         if(U_FAILURE (ec)) {
538                 /* Couldn't count the subbundle */
539                 ures_close (subbundle);
540                 return(NULL);
541         }
542         
543         if(count!=req_count) {
544                 /* Bummer */
545                 ures_close (subbundle);
546                 return(NULL);
547         }
548
549         return(subbundle);
550 }
551
552 static MonoArray *build_array (const UResourceBundle *bundle,
553                                const char *resname, int32_t req_count)
554 {
555         MonoArray *arr=NULL;
556         UResourceBundle *subbundle;
557         int i;
558         
559         subbundle=open_subbundle (bundle, resname, req_count);
560         if(subbundle!=NULL) {
561                 arr=mono_array_new(mono_domain_get (),
562                                    mono_defaults.string_class, req_count);
563                 
564                 for(i=0; i<req_count; i++) {
565                         mono_array_set(arr, MonoString *, i, monostring_from_resource_index (subbundle, i));
566                 }
567
568                 ures_close (subbundle);
569         }
570
571         return(arr);
572 }
573
574 static MonoDateTimeFormatInfo *create_DateTimeFormat (const char *locale)
575 {
576         MonoDateTimeFormatInfo *new_dtf;
577         MonoClass *class;
578         UResourceBundle *bundle, *subbundle;
579         UErrorCode ec;
580         
581         class=mono_class_from_name (mono_defaults.corlib,
582                                     "System.Globalization",
583                                     "DateTimeFormatInfo");
584         new_dtf=(MonoDateTimeFormatInfo *)mono_object_new (mono_domain_get (),
585                                                            class);
586         mono_runtime_object_init ((MonoObject *)new_dtf);
587         
588         ec=U_ZERO_ERROR;
589
590         bundle=ures_open (NULL, locale, &ec);
591         if(U_FAILURE (ec)) {
592                 goto error1;
593         }
594         
595         /* AM/PM markers */
596         subbundle=open_subbundle (bundle, "AmPmMarkers", 2);
597         if(subbundle!=NULL) {
598                 new_dtf->AMDesignator=monostring_from_resource_index (subbundle, 0);
599                 new_dtf->PMDesignator=monostring_from_resource_index (subbundle, 1);
600                 
601                 ures_close (subbundle);
602         }
603         
604         /* Date/Time patterns.  Don't set FullDateTimePattern.  As it
605          * seems to always default to LongDatePattern + " " +
606          * LongTimePattern, let the property accessor deal with it.
607          */
608         subbundle=open_subbundle (bundle, "DateTimePatterns", 9);
609         if(subbundle!=NULL) {
610                 new_dtf->ShortDatePattern=monostring_from_resource_index (subbundle, 7);
611                 new_dtf->LongDatePattern=monostring_from_resource_index (subbundle, 5);
612                 new_dtf->ShortTimePattern=monostring_from_resource_index (subbundle, 3);
613                 new_dtf->LongTimePattern=monostring_from_resource_index (subbundle, 2);
614
615                 /* RFC1123Pattern, SortableDateTimePattern and
616                  * UniversalSortableDateTimePattern all seem to be
617                  * constant, and all the same as the invariant default
618                  * set in the ctor
619                  */
620         
621                 ures_close (subbundle);
622         }
623         
624 #if 0
625         /* Not sure what to do with these yet, so leave them set to
626          * the invariant default
627          */
628         set_field_string (new_dtf, "_DateSeparator", str);
629         set_field_string (new_dtf, "_TimeSeparator", str);
630         set_field_string (new_dtf, "_MonthDayPattern", str);
631         set_field_string (new_dtf, "_YearMonthPattern", str);
632 #endif
633
634         /* Day names.  Luckily both ICU and .net start Sunday at index 0 */
635         new_dtf->DayNames=build_array (bundle, "DayNames", 7);
636
637         /* Abbreviated day names */
638         new_dtf->AbbreviatedDayNames=build_array (bundle, "DayAbbreviations",
639                                                   7);
640
641         /* Month names */
642         new_dtf->MonthNames=build_array (bundle, "MonthNames", 12);
643         
644         /* Abbreviated month names */
645         new_dtf->AbbreviatedMonthNames=build_array (bundle,
646                                                     "MonthAbbreviations", 12);
647
648         /* TODO: DayOfWeek _FirstDayOfWeek, Calendar _Calendar, CalendarWeekRule _CalendarWeekRule */
649
650         ures_close (bundle);
651 error1:
652         return(new_dtf);
653 }
654
655 static MonoNumberFormatInfo *create_NumberFormat (const char *locale)
656 {
657         MonoNumberFormatInfo *new_nf;
658         MonoClass *class;
659         MonoMethodDesc* methodDesc;
660         MonoMethod *method;
661         UResourceBundle *bundle, *subbundle, *table_entries;
662         UErrorCode ec;
663         int32_t count;
664         static char country [7]; //FIXME
665         const UChar *res_str;
666         int32_t res_strlen;
667
668         class=mono_class_from_name (mono_defaults.corlib,
669                                     "System.Globalization",
670                                     "NumberFormatInfo");
671         new_nf=(MonoNumberFormatInfo *)mono_object_new (mono_domain_get (),
672                                                         class);
673         mono_runtime_object_init ((MonoObject *)new_nf);
674
675         ec=U_ZERO_ERROR;
676
677         bundle=ures_open (NULL, locale, &ec);
678         if(U_FAILURE (ec)) {
679                 goto error1;
680         }
681
682         /* Number Elements */
683         ec=U_ZERO_ERROR;
684         subbundle=ures_getByKey (bundle, "NumberElements", NULL, &ec);
685         if(U_FAILURE (ec)) {
686                 /* Couldn't find the subbundle */
687                 goto error1;
688         }
689                 
690         count=ures_countArrayItems (bundle, "NumberElements", &ec);
691         if(U_FAILURE (ec)) {
692                 /* Couldn't count the subbundle */
693                 ures_close (subbundle);
694                 goto error1;
695         }
696
697         if(subbundle!=NULL) {
698                 new_nf->numberDecimalSeparator=monostring_from_resource_index (subbundle, 0);
699                 new_nf->numberGroupSeparator=monostring_from_resource_index (subbundle, 1);
700                 new_nf->percentDecimalSeparator=monostring_from_resource_index (subbundle, 0);
701                 new_nf->percentGroupSeparator=monostring_from_resource_index (subbundle, 1);
702                 new_nf->percentSymbol=monostring_from_resource_index (subbundle, 3);
703                 new_nf->zeroPattern=monostring_from_resource_index (subbundle, 4);
704                 new_nf->digitPattern=monostring_from_resource_index (subbundle, 5);
705                 new_nf->negativeSign=monostring_from_resource_index (subbundle, 6);
706                 new_nf->perMilleSymbol=monostring_from_resource_index (subbundle, 8);
707                 new_nf->positiveInfinitySymbol=monostring_from_resource_index (subbundle, 9);
708                 /* we dont have this in CLDR, so copy it from positiveInfinitySymbol */
709                 new_nf->negativeInfinitySymbol=monostring_from_resource_index (subbundle, 9);
710                 new_nf->naNSymbol=monostring_from_resource_index (subbundle, 10);
711                 new_nf->currencyDecimalSeparator=monostring_from_resource_index (subbundle, 0);
712                 new_nf->currencyGroupSeparator=monostring_from_resource_index (subbundle, 1);
713
714                 ures_close (subbundle);
715         }
716  
717         /* get country name */
718         ec = U_ZERO_ERROR;
719         uloc_getCountry (locale, country, sizeof (country), &ec);
720         if (U_SUCCESS (ec)) {                                           
721                 ec = U_ZERO_ERROR;
722                 /* find country name in root.CurrencyMap */
723                 subbundle = ures_getByKey (bundle, "CurrencyMap", NULL, &ec);
724                 if (U_SUCCESS (ec)) {
725                         ec = U_ZERO_ERROR;
726                         /* get currency id for specified country */
727                         table_entries = ures_getByKey (subbundle, country, NULL, &ec);
728                         if (U_SUCCESS (ec)) {
729                                 ures_close (subbundle);
730                                 ec = U_ZERO_ERROR;
731                                 
732                                 res_str = ures_getStringByIndex (
733                                         table_entries, 0, &res_strlen, &ec);                            
734                                 if(U_SUCCESS (ec)) {
735                                         /* now we have currency id string */
736                                         ures_close (table_entries);
737                                         ec = U_ZERO_ERROR;
738                                         u_UCharsToChars (res_str, country,
739                                                          sizeof (country));
740                                         if(U_SUCCESS (ec)) {
741                                                 ec = U_ZERO_ERROR;
742                                                 /* find currency string in locale data */
743                                                 subbundle = ures_getByKey (
744                                                         bundle, "Currencies",
745                                                         NULL, &ec);
746                                                         
747                                                 if (U_SUCCESS (ec)) {
748                                                         ec = U_ZERO_ERROR;
749                                                         /* find currency symbol under specified currency id */
750                                                         table_entries = ures_getByKey (subbundle, country, NULL, &ec);
751                                                         if (U_SUCCESS (ec)) {
752                                                                 /* get the first string only, 
753                                                                  * the second is international currency symbol (not used)*/
754                                                                 new_nf->currencySymbol=monostring_from_resource_index (table_entries, 0);
755                                                                 ures_close (table_entries);
756                                                         }
757                                                         ures_close (subbundle);
758                                                 }               
759                                         }
760                                 }
761                         }
762                 }
763         }
764
765         subbundle=open_subbundle (bundle, "NumberPatterns", 4);
766         if(subbundle!=NULL) {
767                 new_nf->decimalFormats=monostring_from_resource_index (subbundle, 0);
768                 new_nf->currencyFormats=monostring_from_resource_index (subbundle, 1);
769                 new_nf->percentFormats=monostring_from_resource_index (subbundle, 2);
770                 ures_close (subbundle);
771                 
772                 /* calls InitPatterns to parse the patterns
773                  */
774                 methodDesc = mono_method_desc_new (
775                         "System.Globalization.NumberFormatInfo:InitPatterns()",
776                         TRUE);
777                 method = mono_method_desc_search_in_class (methodDesc, class);
778                 if(method!=NULL) {
779                         mono_runtime_invoke (method, new_nf, NULL, NULL);
780                 } else {
781                         g_warning (G_GNUC_PRETTY_FUNCTION ": Runtime mismatch with class lib! (Looking for System.Globalization.NumberFormatInfo:InitPatterns())");
782                 }
783         }
784
785         ures_close (bundle);
786 error1:
787         return(new_nf);
788 }
789
790 static char *mono_string_to_icu_locale (MonoString *locale)
791 {
792         UErrorCode ec;
793         char *passed_locale, *icu_locale=NULL;
794         int32_t loc_len, ret;
795
796         passed_locale=mono_string_to_utf8 (locale);
797         
798         ec=U_ZERO_ERROR;
799         ret=uloc_getName (passed_locale, NULL, 0, &ec);
800         if(ec==U_BUFFER_OVERFLOW_ERROR) {
801                 ec=U_ZERO_ERROR;
802                 loc_len=ret+1;
803                 icu_locale=(char *)g_malloc0 (sizeof(char)*loc_len);
804                 ret=uloc_getName (passed_locale, icu_locale, loc_len, &ec);
805         }
806         g_free (passed_locale);
807         
808         return(icu_locale);
809 }
810
811 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
812 {
813         UChar *ustr;
814         char *str;
815         UErrorCode ec;
816         char *icu_locale;
817         int32_t str_len, ret;
818         
819         MONO_ARCH_SAVE_REGS;
820
821         icu_locale=mono_string_to_icu_locale (locale);
822         if(icu_locale==NULL) {
823                 /* Something went wrong */
824                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
825                 return;
826         }
827         
828         /* Fill in the static fields */
829
830         /* TODO: Calendar, InstalledUICulture, OptionalCalendars,
831          * TextInfo
832          */
833
834         str_len=256;    /* Should be big enough for anything */
835         str=(char *)g_malloc0 (sizeof(char)*str_len);
836         ustr=(UChar *)g_malloc0 (sizeof(UChar)*str_len);
837         
838         ec=U_ZERO_ERROR;
839         
840         ret=uloc_getDisplayName (icu_locale, "en", ustr, str_len, &ec);
841         if(U_SUCCESS (ec) && ret<str_len) {
842                 this->englishname=mono_string_from_utf16 ((gunichar2 *)ustr);
843         }
844         
845         ret=uloc_getDisplayName (icu_locale, uloc_getDefault (), ustr, str_len,
846                                  &ec);
847         if(U_SUCCESS (ec) && ret<str_len) {
848                 this->displayname=mono_string_from_utf16 ((gunichar2 *)ustr);
849         }
850         
851         ret=uloc_getDisplayName (icu_locale, icu_locale, ustr, str_len, &ec);
852         if(U_SUCCESS (ec) && ret<str_len) {
853                 this->nativename=mono_string_from_utf16 ((gunichar2 *)ustr);
854         }
855
856         this->iso3lang=mono_string_new_wrapper (uloc_getISO3Language (icu_locale));
857
858         ret=uloc_getLanguage (icu_locale, str, str_len, &ec);
859         if(U_SUCCESS (ec) && ret<str_len) {
860                 this->iso2lang=mono_string_new_wrapper (str);
861         }
862
863         this->datetime_format=create_DateTimeFormat (icu_locale);
864         this->number_format=create_NumberFormat (icu_locale);
865  
866         g_free (str);
867         g_free (ustr);
868         g_free (icu_locale);
869 }
870
871 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
872 {
873         UCollator *coll;
874         UErrorCode ec;
875         char *icu_locale;
876         
877         MONO_ARCH_SAVE_REGS;
878         
879 #ifdef DEBUG
880         g_message (G_GNUC_PRETTY_FUNCTION ": Constructing collator for locale [%s]", mono_string_to_utf8 (locale));
881 #endif
882
883         icu_locale=mono_string_to_icu_locale (locale);
884         if(icu_locale==NULL) {
885                 /* Something went wrong */
886                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
887                 return;
888         }
889
890         ec=U_ZERO_ERROR;
891         coll=ucol_open (icu_locale, &ec);
892         if(U_SUCCESS (ec)) {
893                 comp->ICU_collator=coll;
894         } else {
895                 comp->ICU_collator=NULL;
896         }
897
898         g_free (icu_locale);
899 }
900
901 /* Set up the collator to reflect the options required.  Some of these
902  * options clash, as they adjust the collator strength level.  Try to
903  * make later checks reduce the strength level, and attempt to take
904  * previous options into account.
905  *
906  * Don't bother to check the error returns when setting the
907  * attributes, as a failure here is hardly grounds to error out.
908  */
909 static void set_collator_options (UCollator *coll, gint32 options)
910 {
911         UErrorCode ec=U_ZERO_ERROR;
912         
913         /* Set up the defaults */
914         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
915                            &ec);
916         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_OFF, &ec);
917         
918         /* Do this first so other options will override the quaternary
919          * level strength setting if necessary
920          */
921         if(!(options & CompareOptions_IgnoreKanaType)) {
922                 ucol_setAttribute (coll, UCOL_HIRAGANA_QUATERNARY_MODE,
923                                    UCOL_ON, &ec);
924                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
925         }
926
927         /* Word sort, the default */
928         if(!(options & CompareOptions_StringSort)) {
929                 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING,
930                                    UCOL_SHIFTED, &ec);
931                 /* Tertiary strength is the default, but it might have
932                  * been set to quaternary above.  (We don't want that
933                  * here, because that will order all the punctuation
934                  * first instead of just ignoring it.)
935                  *
936                  * Unfortunately, tertiary strength with
937                  * ALTERNATE_HANDLING==SHIFTED means that '/' and '@'
938                  * compare to equal, which has the nasty side effect
939                  * of killing mcs :-( (We can't specify a
940                  * culture-insensitive compare, because
941                  * String.StartsWith doesn't have that option.)
942                  *
943                  * ALTERNATE_HANDLING==SHIFTED is needed to accomplish
944                  * the word-sorting-ignoring-punctuation feature.  So
945                  * we have to live with the slightly mis-ordered
946                  * punctuation and a working mcs...
947                  */
948                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
949         }
950
951         if(options & CompareOptions_IgnoreCase) {
952                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
953                 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, &ec);
954         }
955
956         if(options & CompareOptions_IgnoreWidth) {
957                 /* Kana width is a tertiary strength difference.  This
958                  * will totally break the !IgnoreKanaType option
959                  */
960                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
961         }
962                 
963         if(options & CompareOptions_IgnoreNonSpace) {
964                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
965                 /* We can still compare case even when just checking
966                  * primary strength
967                  */
968                 if(!(options & CompareOptions_IgnoreCase) ||
969                    !(options & CompareOptions_IgnoreWidth)) {
970                         /* Not sure if CASE_LEVEL handles kana width
971                          */
972                         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON,
973                                            &ec);
974                 }
975         }
976
977         if(options & CompareOptions_IgnoreSymbols) {
978                 /* Don't know what to do here */
979         }
980
981         if(options == CompareOptions_Ordinal) {
982                 /* This one is handled elsewhere */
983         }
984 }
985
986 gint32 ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
987 {
988         UCollator *coll;
989         UCollationResult result;
990         
991         MONO_ARCH_SAVE_REGS;
992
993 #ifdef DEBUG
994         g_message (G_GNUC_PRETTY_FUNCTION ": Comparing [%s] and [%s]", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2));
995 #endif
996
997         coll=this->ICU_collator;
998
999 #ifdef DEBUG
1000         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1001 #endif
1002         
1003         if(coll==NULL || this->lcid==0x007F ||
1004            options & CompareOptions_Ordinal) {
1005 #ifdef DEBUG
1006                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1007 #endif
1008
1009                 return(string_invariant_compare (str1, off1, len1, str2, off2,
1010                                                  len2, options));
1011         }
1012         
1013         if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1014                 return(NULL);
1015         
1016         set_collator_options (coll, options);
1017                         
1018         result=ucol_strcoll (coll, mono_string_chars (str1)+off1, len1,
1019                              mono_string_chars (str2)+off2, len2);
1020
1021         mono_monitor_exit ((MonoObject *)this);
1022
1023 #ifdef DEBUG
1024         g_message (G_GNUC_PRETTY_FUNCTION ": Comparison of [%s] and [%s] returning %d", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2), result);
1025 #endif
1026         
1027         return(result);
1028 }
1029
1030 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1031 {
1032         UCollator *coll;
1033         
1034         MONO_ARCH_SAVE_REGS;
1035         
1036         coll=this->ICU_collator;
1037         if(coll!=NULL) {
1038                 ucol_close (coll);
1039         }
1040 }
1041
1042 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1043 {
1044         UCollator *coll;
1045         MonoArray *arr;
1046         char *keybuf;
1047         int32_t keylen, i;
1048         
1049         MONO_ARCH_SAVE_REGS;
1050         
1051         coll=this->ICU_collator;
1052         if(coll==NULL) {
1053                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
1054                 return;
1055         }
1056         
1057         if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1058                 return;
1059         
1060         set_collator_options (coll, options);
1061
1062         keylen=ucol_getSortKey (coll, mono_string_chars (source), -1, NULL, 0);
1063         keybuf=g_malloc (sizeof(char)* keylen);
1064         ucol_getSortKey (coll, mono_string_chars (source), -1, keybuf, keylen);
1065
1066         mono_monitor_exit ((MonoObject *)this);
1067         
1068         arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
1069                             keylen);
1070         for(i=0; i<keylen; i++) {
1071                 mono_array_set (arr, guint8, i, keybuf[i]);
1072         }
1073         
1074         key->key=arr;
1075
1076         g_free (keybuf);
1077 }
1078
1079 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1080 {
1081         UCollator *coll;
1082         UChar *usrcstr;
1083         UErrorCode ec;
1084         UStringSearch *search;
1085         int32_t pos= -1;
1086         
1087         MONO_ARCH_SAVE_REGS;
1088         
1089 #ifdef DEBUG
1090         g_message (G_GNUC_PRETTY_FUNCTION ": Finding %s [%s] in [%s] (sindex %d,count %d)", first?"first":"last", mono_string_to_utf8 (value), mono_string_to_utf8 (source), sindex, count);
1091 #endif
1092
1093         coll=this->ICU_collator;
1094
1095 #ifdef DEBUG
1096         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1097 #endif
1098         
1099         if(coll==NULL || this->lcid==0x007F ||
1100            options & CompareOptions_Ordinal) {
1101 #ifdef DEBUG
1102                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1103 #endif
1104
1105                 return(string_invariant_indexof (source, sindex, count, value,
1106                                                  first));
1107         }
1108         
1109         usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
1110         if(first) {
1111                 memcpy (usrcstr, mono_string_chars (source)+sindex,
1112                         sizeof(UChar)*count);
1113         } else {
1114                 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
1115                         sizeof(UChar)*count);
1116         }
1117         
1118         if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1119                 return(-1);
1120         
1121         ec=U_ZERO_ERROR;
1122         
1123         /* Need to set the collator to a fairly weak level, so that it
1124          * treats characters that can be written differently as
1125          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
1126          * that this means that the search string and the original
1127          * text might have differing lengths.
1128          */
1129         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1130
1131         /* Still notice case differences though (normally a tertiary
1132          * difference)
1133          */
1134         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1135
1136         /* Don't ignore some codepoints */
1137         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1138                            &ec);
1139                         
1140         search=usearch_openFromCollator (mono_string_chars (value), -1, usrcstr, -1, coll, NULL,
1141                                          &ec);
1142         if(U_SUCCESS (ec)) {
1143                 if(first) {
1144                         pos=usearch_first (search, &ec);
1145                 } else {
1146                         pos=usearch_last (search, &ec);
1147                 }
1148
1149                 if(pos!=USEARCH_DONE) {
1150 #ifdef DEBUG
1151                         g_message (G_GNUC_PRETTY_FUNCTION
1152                                    ": Got match at %d (sindex %d) len %d", pos,
1153                                    sindex, usearch_getMatchedLength (search));
1154 #endif
1155                         if(sindex>0) {
1156                                 if(first) {
1157                                         pos+=sindex;
1158                                 } else {
1159                                         pos+=(sindex-count+1);
1160                                 }
1161                         }
1162                 }
1163         } else {
1164                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1165                            u_errorName (ec));
1166         }
1167
1168         usearch_close (search);
1169         
1170         mono_monitor_exit ((MonoObject *)this);
1171         
1172         g_free (usrcstr);
1173
1174         return(pos);
1175 }
1176
1177 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1178 {
1179         UCollator *coll;
1180         UChar *usrcstr, uvalstr[2]={0, 0};
1181         UErrorCode ec;
1182         UStringSearch *search;
1183         int32_t pos= -1;
1184         
1185         MONO_ARCH_SAVE_REGS;
1186         
1187 #ifdef DEBUG
1188         g_message (G_GNUC_PRETTY_FUNCTION ": Finding %s 0x%0x in [%s] (sindex %d,count %d)", first?"first":"last", value, mono_string_to_utf8 (source), sindex, count);
1189 #endif
1190
1191         coll=this->ICU_collator;
1192
1193 #ifdef DEBUG
1194         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1195 #endif
1196         
1197         if(coll==NULL || this->lcid==0x007F ||
1198            options & CompareOptions_Ordinal) {
1199 #ifdef DEBUG
1200                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1201 #endif
1202
1203                 return(string_invariant_indexof_char (source, sindex, count,
1204                                                       value, first));
1205         }
1206         
1207         usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
1208         if(first) {
1209                 memcpy (usrcstr, mono_string_chars (source)+sindex,
1210                         sizeof(UChar)*count);
1211         } else {
1212                 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
1213                         sizeof(UChar)*count);
1214         }
1215         uvalstr[0]=value;
1216         
1217         if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1218                 return(-1);
1219         
1220         ec=U_ZERO_ERROR;
1221         
1222         /* Need to set the collator to a fairly weak level, so that it
1223          * treats characters that can be written differently as
1224          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
1225          * that this means that the search string and the original
1226          * text might have differing lengths.
1227          */
1228         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1229
1230         /* Still notice case differences though (normally a tertiary
1231          * difference)
1232          */
1233         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1234
1235         /* Don't ignore some codepoints */
1236         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1237                            &ec);
1238                         
1239         search=usearch_openFromCollator (uvalstr, -1, usrcstr, -1, coll, NULL,
1240                                          &ec);
1241         if(U_SUCCESS (ec)) {
1242                 if(first) {
1243                         pos=usearch_first (search, &ec);
1244                 } else {
1245                         pos=usearch_last (search, &ec);
1246                 }
1247
1248                 if(pos!=USEARCH_DONE) {
1249 #ifdef DEBUG
1250                         g_message (G_GNUC_PRETTY_FUNCTION
1251                                    ": Got match at %d (sindex %d) len %d", pos,
1252                                    sindex, usearch_getMatchedLength (search));
1253 #endif
1254                         if(sindex>0) {
1255                                 if(first) {
1256                                         pos+=sindex;
1257                                 } else {
1258                                         pos+=(sindex-count+1);
1259                                 }
1260                         }
1261                 }
1262         } else {
1263                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1264                            u_errorName (ec));
1265         }
1266
1267         usearch_close (search);
1268         
1269         mono_monitor_exit ((MonoObject *)this);
1270         
1271         g_free (uvalstr);
1272
1273         return(pos);
1274 }
1275
1276 int ves_icall_System_Threading_Thread_current_lcid (void)
1277 {
1278         MONO_ARCH_SAVE_REGS;
1279
1280         return(uloc_getLCID (uloc_getDefault ()));
1281 }
1282
1283 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1284 {
1285         MonoString *ret=NULL;
1286         UCollator *coll;
1287         UErrorCode ec;
1288         UStringSearch *search;
1289         
1290         MONO_ARCH_SAVE_REGS;
1291         
1292 #ifdef DEBUG
1293         g_message (G_GNUC_PRETTY_FUNCTION ": Replacing [%s] with [%s] in [%s]", mono_string_to_utf8 (old), mono_string_to_utf8 (new), mono_string_to_utf8 (this));
1294 #endif
1295
1296         coll=comp->ICU_collator;
1297
1298 #ifdef DEBUG
1299         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", comp->lcid);
1300 #endif
1301         
1302         if(coll==NULL || comp->lcid==0x007F) {
1303 #ifdef DEBUG
1304                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1305 #endif
1306
1307                 return(string_invariant_replace (this, old, new));
1308         }
1309         
1310         if (!mono_monitor_try_enter ((MonoObject *)comp, INFINITE))
1311                 return(NULL);
1312         
1313         ec=U_ZERO_ERROR;
1314         
1315         /* Need to set the collator to a fairly weak level, so that it
1316          * treats characters that can be written differently as
1317          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
1318          * that this means that the search string and the original
1319          * text might have differing lengths.
1320          */
1321         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1322
1323         /* Still notice case differences though (normally a tertiary
1324          * difference)
1325          */
1326         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1327
1328         /* Don't ignore some codepoints */
1329         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1330                            &ec);
1331                         
1332         search=usearch_openFromCollator (mono_string_chars (old), -1,
1333                                          mono_string_chars (this), -1, coll,
1334                                          NULL, &ec);
1335         if(U_SUCCESS (ec)) {
1336                 int pos, oldpos, len_delta=0;
1337                 int32_t newstr_len=mono_string_length (new), match_len;
1338                 UChar *uret, *match;
1339                 
1340                 for(pos=usearch_first (search, &ec);
1341                     pos!=USEARCH_DONE;
1342                     pos=usearch_next (search, &ec)) {
1343                         /* ICU usearch currently ignores most of the collator
1344                          * attributes :-(
1345                          *
1346                          * Check the returned match to see if it really
1347                          * does match properly...
1348                          */
1349                         match_len = usearch_getMatchedLength (search);
1350                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1351                         usearch_getMatchedText (search, match, match_len, &ec);
1352
1353                         if (ucol_strcoll (coll, match, -1, mono_string_chars (old), -1) == UCOL_EQUAL) {
1354                                 /* OK, we really did get a match */
1355 #ifdef DEBUG
1356                                 g_message (G_GNUC_PRETTY_FUNCTION
1357                                            ": Got match at %d len %d", pos,
1358                                            match_len);
1359 #endif
1360
1361                                 len_delta += (newstr_len - match_len);
1362                         } else {
1363                                 /* False alarm */
1364 #ifdef DEBUG
1365                                 g_message (G_GNUC_PRETTY_FUNCTION
1366                                            ": Got false match at %d len %d",
1367                                            pos, match_len);
1368 #endif
1369                         }
1370                         g_free (match);
1371                 }
1372 #ifdef DEBUG
1373                 g_message (G_GNUC_PRETTY_FUNCTION
1374                            ": New string length is %d (delta %d)",
1375                            mono_string_length (this)+len_delta, len_delta);
1376 #endif
1377                 
1378                 uret=(UChar *)g_malloc0 (sizeof(UChar) * (mono_string_length (this)+len_delta+2));
1379                 
1380                 for(oldpos=0, pos=usearch_first (search, &ec);
1381                     pos!=USEARCH_DONE;
1382                     pos=usearch_next (search, &ec)) {
1383                         match_len = usearch_getMatchedLength (search);
1384                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1385                         usearch_getMatchedText (search, match, match_len, &ec);
1386
1387                         /* Add the unmatched text */
1388                         u_strncat (uret, mono_string_chars (this)+oldpos,
1389                                    pos-oldpos);
1390                         if (ucol_strcoll (coll, match, -1, mono_string_chars (old), -1) == UCOL_EQUAL) {
1391                                 /* Then the replacement */
1392                                 u_strcat (uret, mono_string_chars (new));
1393                         } else {
1394                                 /* Then the original, because this is a
1395                                  * false match
1396                                  */
1397                                 u_strncat (uret, mono_string_chars (this)+pos,
1398                                            match_len);
1399                         }
1400                         oldpos=pos+match_len;
1401                         g_free (match);
1402                 }
1403                 
1404                 /* Finish off with the trailing unmatched text */
1405                 u_strcat (uret, mono_string_chars (this)+oldpos);
1406
1407                 ret=mono_string_from_utf16 ((gunichar2 *)uret);
1408         } else {
1409                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1410                            u_errorName (ec));
1411         }
1412
1413         usearch_close (search);
1414         
1415         mono_monitor_exit ((MonoObject *)comp);
1416         
1417 #ifdef DEBUG
1418         g_message (G_GNUC_PRETTY_FUNCTION ": Replacing [%s] with [%s] in [%s] returns [%s]", mono_string_to_utf8 (old), mono_string_to_utf8 (new), mono_string_to_utf8 (this), mono_string_to_utf8 (ret));
1419 #endif
1420         
1421         return(ret);
1422 }
1423
1424 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1425 {
1426         MonoString *ret;
1427         UChar *udest;
1428         UErrorCode ec;
1429         char *icu_loc;
1430         int32_t len;
1431
1432         MONO_ARCH_SAVE_REGS;
1433
1434 #ifdef DEBUG
1435         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1436                    mono_string_to_utf8 (this));
1437 #endif
1438
1439 #ifdef DEBUG
1440         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1441 #endif
1442
1443         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1444         if(icu_loc==NULL) {
1445                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1446                 return(NULL);
1447         }
1448         
1449         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1450         
1451         /* According to the docs, this might result in a longer or
1452          * shorter string than we started with...
1453          */
1454
1455         ec=U_ZERO_ERROR;
1456         len=u_strToLower (udest, mono_string_length (this)+1,
1457                           mono_string_chars (this), -1, icu_loc, &ec);
1458         if(ec==U_BUFFER_OVERFLOW_ERROR ||
1459            ec==U_STRING_NOT_TERMINATED_WARNING) {
1460                 g_free (udest);
1461                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1462                 len=u_strToLower (udest, len+1, mono_string_chars (this), -1,
1463                                   icu_loc, &ec);
1464         }
1465
1466         if(U_SUCCESS (ec)) {
1467                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1468         } else {
1469                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToLower error: %s",
1470                            u_errorName (ec));
1471                 /* return something */
1472                 ret=this;
1473         }
1474         
1475         g_free (icu_loc);
1476         g_free (udest);
1477         
1478 #ifdef DEBUG
1479         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1480                    mono_string_to_utf8 (ret));
1481 #endif
1482
1483         return(ret);
1484 }
1485
1486 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1487 {
1488         MonoString *ret;
1489         UChar *udest;
1490         UErrorCode ec;
1491         char *icu_loc;
1492         int32_t len;
1493
1494         MONO_ARCH_SAVE_REGS;
1495
1496 #ifdef DEBUG
1497         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1498                    mono_string_to_utf8 (this));
1499 #endif
1500
1501 #ifdef DEBUG
1502         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1503 #endif
1504
1505         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1506         if(icu_loc==NULL) {
1507                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1508                 return(NULL);
1509         }
1510         
1511         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1512         
1513         /* According to the docs, this might result in a longer or
1514          * shorter string than we started with...
1515          */
1516
1517         ec=U_ZERO_ERROR;
1518         len=u_strToUpper (udest, mono_string_length (this)+1,
1519                           mono_string_chars (this), -1, icu_loc, &ec);
1520         if(ec==U_BUFFER_OVERFLOW_ERROR ||
1521            ec==U_STRING_NOT_TERMINATED_WARNING) {
1522                 g_free (udest);
1523                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1524                 len=u_strToUpper (udest, len+1, mono_string_chars (this), -1,
1525                                   icu_loc, &ec);
1526         }
1527
1528         if(U_SUCCESS (ec)) {
1529                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1530         } else {
1531                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToUpper error: %s",
1532                            u_errorName (ec));
1533                 /* return something */
1534                 ret=this;
1535         }
1536         
1537         g_free (icu_loc);
1538         g_free (udest);
1539         
1540 #ifdef DEBUG
1541         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1542                    mono_string_to_utf8 (ret));
1543 #endif
1544         
1545         return(ret);
1546 }
1547
1548 gunichar2 ves_icall_System_Char_InternalToUpper_Comp (gunichar2 c, MonoCultureInfo *cult)
1549 {
1550         UChar udest;
1551         UErrorCode ec;
1552         char *icu_loc;
1553         int32_t len;
1554         
1555         MONO_ARCH_SAVE_REGS;
1556
1557         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1558         if(icu_loc==NULL) {
1559                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1560                 return(0);
1561         }
1562         
1563         ec=U_ZERO_ERROR;
1564         len=u_strToUpper (&udest, 1, &c, 1, icu_loc, &ec);
1565
1566         if(U_SUCCESS (ec) && len==1) {
1567                 return udest;
1568         } else {
1569                 /* return something */
1570                 return c;
1571         }
1572 }
1573
1574
1575 gunichar2 ves_icall_System_Char_InternalToLower_Comp (gunichar2 c, MonoCultureInfo *cult)
1576 {
1577         UChar udest;
1578         UErrorCode ec;
1579         char *icu_loc;
1580         int32_t len;
1581         
1582         MONO_ARCH_SAVE_REGS;
1583
1584         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1585         if(icu_loc==NULL) {
1586                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1587                 return(0);
1588         }
1589         
1590         ec=U_ZERO_ERROR;
1591         len=u_strToLower (&udest, 1, &c, 1, icu_loc, &ec);
1592
1593         if(U_SUCCESS (ec) && len==1) {
1594                 return udest;
1595         } else {
1596                 /* return something */
1597                 return c;
1598         }
1599 }
1600
1601 #else /* HAVE_ICU */
1602 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
1603 {
1604         MONO_ARCH_SAVE_REGS;
1605         
1606         /* Always claim "unknown locale" if we don't have ICU (only
1607          * called for non-invariant locales)
1608          */
1609         mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "ArgumentException"));
1610 }
1611
1612 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
1613 {
1614         /* Nothing to do here */
1615 }
1616
1617 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
1618 {
1619         MONO_ARCH_SAVE_REGS;
1620         
1621         /* Do a normal ascii string compare, as we only know the
1622          * invariant locale if we dont have ICU
1623          */
1624         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
1625                                          options));
1626 }
1627
1628 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1629 {
1630         /* Nothing to do here */
1631 }
1632
1633 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1634 {
1635         MonoArray *arr;
1636         gint32 keylen, i;
1637
1638         MONO_ARCH_SAVE_REGS;
1639         
1640         keylen=mono_string_length (source);
1641         
1642         arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
1643                             keylen);
1644         for(i=0; i<keylen; i++) {
1645                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
1646         }
1647         
1648         key->key=arr;
1649 }
1650
1651 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1652 {
1653         MONO_ARCH_SAVE_REGS;
1654         
1655         return(string_invariant_indexof (source, sindex, count, value, first));
1656 }
1657
1658 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1659 {
1660         MONO_ARCH_SAVE_REGS;
1661         
1662         return(string_invariant_indexof_char (source, sindex, count, value,
1663                                               first));
1664 }
1665
1666 int ves_icall_System_Threading_Thread_current_lcid (void)
1667 {
1668         MONO_ARCH_SAVE_REGS;
1669         
1670         /* Invariant */
1671         return(0x007F);
1672 }
1673
1674 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1675 {
1676         MONO_ARCH_SAVE_REGS;
1677         
1678         /* Do a normal ascii string compare and replace, as we only
1679          * know the invariant locale if we dont have ICU
1680          */
1681         return(string_invariant_replace (this, old, new));
1682 }
1683
1684 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1685 {
1686         MONO_ARCH_SAVE_REGS;
1687         
1688         return(string_invariant_tolower (this));
1689 }
1690
1691 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1692 {
1693         MONO_ARCH_SAVE_REGS;
1694         
1695         return(string_invariant_toupper (this));
1696 }
1697
1698 gunichar2 ves_icall_System_Char_InternalToUpper_Comp (gunichar2 c, MonoCultureInfo *cult)
1699 {
1700         MONO_ARCH_SAVE_REGS;
1701
1702         return g_unichar_toupper (c);
1703 }
1704
1705
1706 gunichar2 ves_icall_System_Char_InternalToLower_Comp (gunichar2 c, MonoCultureInfo *cult)
1707 {
1708         MONO_ARCH_SAVE_REGS;
1709
1710         return g_unichar_tolower (c);
1711 }
1712
1713 #endif /* HAVE_ICU */
1714
1715 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
1716                                              gint32 options)
1717 {
1718         gint32 result;
1719         GUnicodeType c1type, c2type;
1720
1721         c1type = g_unichar_type (c1);
1722         c2type = g_unichar_type (c2);
1723
1724         if (options & CompareOptions_IgnoreCase) {
1725                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) - (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
1726         } else if (options & CompareOptions_Ordinal) {
1727                 // Rotor/ms return the full value just not -1 and 1
1728                 return (gint32) c1 - c2;
1729         } else {
1730                 /* No options. Kana, symbol and spacing options don't
1731                  * apply to the invariant culture.
1732                  */
1733                 if (c1type == G_UNICODE_UPPERCASE_LETTER &&
1734                     c2type == G_UNICODE_LOWERCASE_LETTER) {
1735                         return(1);
1736                 }
1737                                         
1738                 if (c1type == G_UNICODE_LOWERCASE_LETTER &&
1739                     c2type == G_UNICODE_UPPERCASE_LETTER) {
1740                         return(-1);
1741                 }
1742                 
1743                 result = (gint32) c1 - c2;
1744         }
1745
1746         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
1747 }
1748
1749 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
1750                                         gint32 len1, MonoString *str2,
1751                                         gint32 off2, gint32 len2,
1752                                         gint32 options)
1753 {
1754         /* c translation of C# code from old string.cs.. :) */
1755         gint32 length;
1756         gint32 charcmp;
1757         gunichar2 *ustr1;
1758         gunichar2 *ustr2;
1759         gint32 pos;
1760
1761         if(len1 >= len2) {
1762                 length=len1;
1763         } else {
1764                 length=len2;
1765         }
1766
1767         ustr1 = mono_string_chars(str1)+off1;
1768         ustr2 = mono_string_chars(str2)+off2;
1769
1770         pos = 0;
1771
1772         for (pos = 0; pos != length; pos++) {
1773                 if (pos >= len1 || pos >= len2)
1774                         break;
1775
1776                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
1777                                                         options);
1778                 if (charcmp != 0) {
1779                         return(charcmp);
1780                 }
1781         }
1782
1783         /* the lesser wins, so if we have looped until length we just
1784          * need to check the last char
1785          */
1786         if (pos == length) {
1787                 return(string_invariant_compare_char(ustr1[pos - 1],
1788                                                      ustr2[pos - 1], options));
1789         }
1790
1791         /* Test if one of the strings has been compared to the end */
1792         if (pos >= len1) {
1793                 if (pos >= len2) {
1794                         return(0);
1795                 } else {
1796                         return(-1);
1797                 }
1798         } else if (pos >= len2) {
1799                 return(1);
1800         }
1801
1802         /* if not, check our last char only.. (can this happen?) */
1803         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
1804 }
1805
1806 static MonoString *string_invariant_replace (MonoString *me,
1807                                              MonoString *oldValue,
1808                                              MonoString *newValue)
1809 {
1810         MonoString *ret;
1811         gunichar2 *src;
1812         gunichar2 *dest=NULL; /* shut gcc up */
1813         gunichar2 *oldstr;
1814         gunichar2 *newstr=NULL; /* shut gcc up here too */
1815         gint32 i, destpos;
1816         gint32 occurr;
1817         gint32 newsize;
1818         gint32 oldstrlen;
1819         gint32 newstrlen;
1820         gint32 srclen;
1821
1822         occurr = 0;
1823         destpos = 0;
1824
1825         oldstr = mono_string_chars(oldValue);
1826         oldstrlen = mono_string_length(oldValue);
1827
1828         if (NULL != newValue) {
1829                 newstr = mono_string_chars(newValue);
1830                 newstrlen = mono_string_length(newValue);
1831         } else
1832                 newstrlen = 0;
1833
1834         src = mono_string_chars(me);
1835         srclen = mono_string_length(me);
1836
1837         if (oldstrlen != newstrlen) {
1838                 i = 0;
1839                 while (i <= srclen - oldstrlen) {
1840                         if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1841                                 occurr++;
1842                                 i += oldstrlen;
1843                         }
1844                         else
1845                                 i ++;
1846                 }
1847                 if (occurr == 0)
1848                         return me;
1849                 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
1850         } else
1851                 newsize = srclen;
1852
1853         ret = NULL;
1854         i = 0;
1855         while (i < srclen) {
1856                 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1857                         if (ret == NULL) {
1858                                 ret = mono_string_new_size( mono_domain_get (), newsize);
1859                                 dest = mono_string_chars(ret);
1860                                 memcpy (dest, src, i * sizeof(gunichar2));
1861                         }
1862                         if (newstrlen > 0) {
1863                                 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
1864                                 destpos += newstrlen;
1865                         }
1866                         i += oldstrlen;
1867                         continue;
1868                 } else if (ret != NULL) {
1869                         dest[destpos] = src[i];
1870                 }
1871                 destpos++;
1872                 i++;
1873         }
1874         
1875         if (ret == NULL)
1876                 return me;
1877
1878         return ret;
1879 }
1880
1881 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
1882                                         gint32 count, MonoString *value,
1883                                         MonoBoolean first)
1884 {
1885         gint32 lencmpstr;
1886         gunichar2 *src;
1887         gunichar2 *cmpstr;
1888         gint32 pos,i;
1889         
1890         lencmpstr = mono_string_length(value);
1891         
1892         src = mono_string_chars(source);
1893         cmpstr = mono_string_chars(value);
1894
1895         if(first) {
1896                 count -= lencmpstr;
1897                 for(pos=sindex;pos <= sindex+count;pos++) {
1898                         for(i=0;src[pos+i]==cmpstr[i];) {
1899                                 if(++i==lencmpstr) {
1900                                         return(pos);
1901                                 }
1902                         }
1903                 }
1904                 
1905                 return(-1);
1906         } else {
1907                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
1908                         if(memcmp (src+pos, cmpstr,
1909                                    lencmpstr*sizeof(gunichar2))==0) {
1910                                 return(pos);
1911                         }
1912                 }
1913                 
1914                 return(-1);
1915         }
1916 }
1917
1918 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
1919                                              gint32 count, gunichar2 value,
1920                                              MonoBoolean first)
1921 {
1922         gint32 pos;
1923         gunichar2 *src;
1924
1925         src = mono_string_chars(source);
1926         if(first) {
1927                 for (pos = sindex; pos != count + sindex; pos++) {
1928                         if (src [pos] == value) {
1929                                 return(pos);
1930                         }
1931                 }
1932
1933                 return(-1);
1934         } else {
1935                 for (pos = sindex; pos > sindex - count; pos--) {
1936                         if (src [pos] == value)
1937                                 return(pos);
1938                 }
1939
1940                 return(-1);
1941         }
1942 }
1943
1944 static MonoString *string_invariant_tolower (MonoString *this)
1945 {
1946         MonoString *ret;
1947         gunichar2 *src; 
1948         gunichar2 *dest;
1949         gint32 i;
1950
1951         ret = mono_string_new_size(mono_domain_get (),
1952                                    mono_string_length(this));
1953
1954         src = mono_string_chars (this);
1955         dest = mono_string_chars (ret);
1956
1957         for (i = 0; i < mono_string_length (this); ++i) {
1958                 dest[i] = g_unichar_tolower(src[i]);
1959         }
1960
1961         return(ret);
1962 }
1963
1964 static MonoString *string_invariant_toupper (MonoString *this)
1965 {
1966         MonoString *ret;
1967         gunichar2 *src; 
1968         gunichar2 *dest;
1969         guint32 i;
1970
1971         ret = mono_string_new_size(mono_domain_get (),
1972                                    mono_string_length(this));
1973
1974         src = mono_string_chars (this);
1975         dest = mono_string_chars (ret);
1976
1977         for (i = 0; i < mono_string_length (this); ++i) {
1978                 dest[i] = g_unichar_toupper(src[i]);
1979         }
1980
1981         return(ret);
1982 }
1983