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