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