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