2004-06-23 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mono / metadata / locales.c
1 /*
2  * locales.c: Culture-sensitive handling
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *      Mohammad DAMT (mdamt@cdl2000.com)
7  *
8  * (C) 2003 Ximian, Inc.
9  * (C) 2003 PT Cakram Datalingga Duaribu  http://www.cdl2000.com
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/object.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/monitor.h>
21 #include <mono/metadata/locales.h>
22 #include <mono/metadata/culture-info.h>
23 #include <mono/metadata/culture-info-tables.h>
24
25
26 #include <locale.h>
27
28 //#undef DEBUG
29
30 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
31                                              gint32 options);
32 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
33                                         gint32 len1, MonoString *str2,
34                                         gint32 off2, gint32 len2,
35                                         gint32 options);
36 static MonoString *string_invariant_replace (MonoString *me,
37                                              MonoString *oldValue,
38                                              MonoString *newValue);
39 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
40                                         gint32 count, MonoString *value,
41                                         MonoBoolean first);
42 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
43                                              gint32 count, gunichar2 value,
44                                              MonoBoolean first);
45
46 static 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_get_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_get_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_get_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_get_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_get_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_get_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_get_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_get_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_enter ((MonoObject *)this))
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_get_corlib (), "System", "SystemException"));
1060                 return;
1061         }
1062         
1063         if (!mono_monitor_enter ((MonoObject *)this))
1064                 return;
1065         
1066         set_collator_options (coll, options);
1067
1068         keylen=ucol_getSortKey (coll, mono_string_chars (source),
1069                                 mono_string_length (source), NULL, 0);
1070         keybuf=g_malloc (sizeof(char)* keylen);
1071         ucol_getSortKey (coll, mono_string_chars (source),
1072                          mono_string_length (source), keybuf, keylen);
1073
1074         mono_monitor_exit ((MonoObject *)this);
1075         
1076         arr=mono_array_new (mono_domain_get (), mono_get_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_enter ((MonoObject *)this))
1127                 return(-1);
1128         
1129         ec=U_ZERO_ERROR;
1130         
1131         /* Need to set the collator to a fairly weak level, so that it
1132          * treats characters that can be written differently as
1133          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
1134          * that this means that the search string and the original
1135          * text might have differing lengths.
1136          */
1137         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1138
1139         /* Still notice case differences though (normally a tertiary
1140          * difference)
1141          */
1142         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1143
1144         /* Don't ignore some codepoints */
1145         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1146                            &ec);
1147         
1148         search=usearch_openFromCollator (mono_string_chars (value),
1149                                          mono_string_length (value),
1150                                          usrcstr, count, coll, NULL, &ec);
1151         if(U_SUCCESS (ec)) {
1152                 if(first) {
1153                         pos=usearch_first (search, &ec);
1154                 } else {
1155                         pos=usearch_last (search, &ec);
1156                 }
1157
1158                 while (pos!=USEARCH_DONE) {
1159                         int32_t match_len;
1160                         UChar *match;
1161                         UCollationResult uret;
1162                         
1163 #ifdef DEBUG
1164                         g_message (G_GNUC_PRETTY_FUNCTION
1165                                    ": Got potential match at %d (sindex %d) len %d", pos, sindex, usearch_getMatchedLength (search));
1166 #endif
1167
1168                         /* ICU usearch currently ignores most of the
1169                          * collator attributes :-(
1170                          *
1171                          * Check the returned match to see if it
1172                          * really does match properly...
1173                          */
1174                         match_len = usearch_getMatchedLength (search);
1175                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1176                         usearch_getMatchedText (search, match, match_len, &ec);
1177
1178                         uret = ucol_strcoll (coll, match, match_len,
1179                                              mono_string_chars (value),
1180                                              mono_string_length (value));
1181                         g_free (match);
1182                         
1183                         if (uret == UCOL_EQUAL) {
1184                                 /* OK, we really did get a match */
1185 #ifdef DEBUG
1186                                 g_message (G_GNUC_PRETTY_FUNCTION
1187                                            ": Got match at %d len %d", pos,
1188                                            match_len);
1189 #endif
1190
1191                                 if(sindex>0) {
1192                                         if(first) {
1193                                                 pos+=sindex;
1194                                         } else {
1195                                                 pos+=(sindex-count+1);
1196                                         }
1197                                 }
1198
1199                                 break;
1200                         }
1201
1202                         /* False alarm, keep looking */
1203                         if(first) {
1204                                 pos=usearch_next (search, &ec);
1205                         } else {
1206                                 pos=usearch_previous (search, &ec);
1207                         }       
1208                 }
1209         } else {
1210                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1211                            u_errorName (ec));
1212         }
1213
1214         usearch_close (search);
1215         
1216         mono_monitor_exit ((MonoObject *)this);
1217         
1218         g_free (usrcstr);
1219
1220         return(pos);
1221 }
1222
1223 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1224 {
1225         UCollator *coll;
1226         UChar *usrcstr, uvalstr[2]={0, 0};
1227         UErrorCode ec;
1228         UStringSearch *search;
1229         int32_t pos= -1;
1230         
1231         MONO_ARCH_SAVE_REGS;
1232         
1233 #ifdef DEBUG
1234         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);
1235 #endif
1236
1237         coll=this->ICU_collator;
1238
1239 #ifdef DEBUG
1240         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1241 #endif
1242         
1243         if(coll==NULL || this->lcid==0x007F ||
1244            options & CompareOptions_Ordinal) {
1245 #ifdef DEBUG
1246                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1247 #endif
1248
1249                 return(string_invariant_indexof_char (source, sindex, count,
1250                                                       value, first));
1251         }
1252         
1253         usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
1254         if(first) {
1255                 memcpy (usrcstr, mono_string_chars (source)+sindex,
1256                         sizeof(UChar)*count);
1257         } else {
1258                 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
1259                         sizeof(UChar)*count);
1260         }
1261         uvalstr[0]=value;
1262         
1263         if (!mono_monitor_enter ((MonoObject *)this))
1264                 return(-1);
1265         
1266         ec=U_ZERO_ERROR;
1267         
1268         /* Need to set the collator to a fairly weak level, so that it
1269          * treats characters that can be written differently as
1270          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
1271          * that this means that the search string and the original
1272          * text might have differing lengths.
1273          */
1274         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1275
1276         /* Still notice case differences though (normally a tertiary
1277          * difference)
1278          */
1279         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1280
1281         /* Don't ignore some codepoints */
1282         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1283                            &ec);
1284                         
1285         search=usearch_openFromCollator (uvalstr, 1, usrcstr, count, coll,
1286                                          NULL, &ec);
1287         if(U_SUCCESS (ec)) {
1288                 if(first) {
1289                         pos=usearch_first (search, &ec);
1290                 } else {
1291                         pos=usearch_last (search, &ec);
1292                 }
1293
1294                 while (pos!=USEARCH_DONE) {
1295                         int32_t match_len;
1296                         UChar *match;
1297                         UCollationResult uret;
1298                         
1299 #ifdef DEBUG
1300                         g_message (G_GNUC_PRETTY_FUNCTION
1301                                    ": Got potential match at %d (sindex %d) len %d", pos, sindex, usearch_getMatchedLength (search));
1302 #endif
1303
1304                         /* ICU usearch currently ignores most of the
1305                          * collator attributes :-(
1306                          *
1307                          * Check the returned match to see if it
1308                          * really does match properly...
1309                          */
1310                         match_len = usearch_getMatchedLength (search);
1311                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1312                         usearch_getMatchedText (search, match, match_len, &ec);
1313
1314                         uret = ucol_strcoll (coll, match, match_len, uvalstr,
1315                                              1);
1316                         g_free (match);
1317                         
1318                         if (uret == UCOL_EQUAL) {
1319                                 /* OK, we really did get a match */
1320 #ifdef DEBUG
1321                                 g_message (G_GNUC_PRETTY_FUNCTION
1322                                            ": Got match at %d len %d", pos,
1323                                            match_len);
1324 #endif
1325
1326                                 if(sindex>0) {
1327                                         if(first) {
1328                                                 pos+=sindex;
1329                                         } else {
1330                                                 pos+=(sindex-count+1);
1331                                         }
1332                                 }
1333
1334                                 break;
1335                         }
1336
1337                         /* False alarm, keep looking */
1338                         if(first) {
1339                                 pos=usearch_next (search, &ec);
1340                         } else {
1341                                 pos=usearch_previous (search, &ec);
1342                         }       
1343                 }
1344         } else {
1345                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1346                            u_errorName (ec));
1347         }
1348
1349         usearch_close (search);
1350         
1351         mono_monitor_exit ((MonoObject *)this);
1352         
1353         g_free (usrcstr);
1354
1355         return(pos);
1356 }
1357
1358 int ves_icall_System_Threading_Thread_current_lcid (void)
1359 {
1360         MONO_ARCH_SAVE_REGS;
1361
1362         return(uloc_getLCID (uloc_getDefault ()));
1363 }
1364
1365 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1366 {
1367         MonoString *ret=NULL;
1368         UCollator *coll;
1369         UErrorCode ec;
1370         UStringSearch *search;
1371         
1372         MONO_ARCH_SAVE_REGS;
1373         
1374 #ifdef DEBUG
1375         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));
1376 #endif
1377
1378         coll=comp->ICU_collator;
1379
1380 #ifdef DEBUG
1381         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", comp->lcid);
1382 #endif
1383         
1384         if(coll==NULL || comp->lcid==0x007F) {
1385 #ifdef DEBUG
1386                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1387 #endif
1388
1389                 return(string_invariant_replace (this, old, new));
1390         }
1391         
1392         if (!mono_monitor_enter ((MonoObject *)comp))
1393                 return(NULL);
1394         
1395         ec=U_ZERO_ERROR;
1396         
1397         /* Need to set the collator to a fairly weak level, so that it
1398          * treats characters that can be written differently as
1399          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
1400          * that this means that the search string and the original
1401          * text might have differing lengths.
1402          */
1403         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1404
1405         /* Still notice case differences though (normally a tertiary
1406          * difference)
1407          */
1408         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1409
1410         /* Don't ignore some codepoints */
1411         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1412                            &ec);
1413                         
1414         search=usearch_openFromCollator (mono_string_chars (old),
1415                                          mono_string_length (old),
1416                                          mono_string_chars (this),
1417                                          mono_string_length (this),
1418                                          coll, NULL, &ec);
1419         if(U_SUCCESS (ec)) {
1420                 int pos, oldpos, len_delta=0;
1421                 int32_t newstr_len=mono_string_length (new), match_len;
1422                 UChar *uret, *match;
1423                 
1424                 for(pos=usearch_first (search, &ec);
1425                     pos!=USEARCH_DONE;
1426                     pos=usearch_next (search, &ec)) {
1427                         /* ICU usearch currently ignores most of the collator
1428                          * attributes :-(
1429                          *
1430                          * Check the returned match to see if it really
1431                          * does match properly...
1432                          */
1433                         match_len = usearch_getMatchedLength (search);
1434                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1435                         usearch_getMatchedText (search, match, match_len, &ec);
1436
1437                         if (ucol_strcoll (coll, match, match_len,
1438                                           mono_string_chars (old),
1439                                           mono_string_length (old)) == UCOL_EQUAL) {
1440                                 /* OK, we really did get a match */
1441 #ifdef DEBUG
1442                                 g_message (G_GNUC_PRETTY_FUNCTION
1443                                            ": Got match at %d len %d", pos,
1444                                            match_len);
1445 #endif
1446
1447                                 len_delta += (newstr_len - match_len);
1448                         } else {
1449                                 /* False alarm */
1450 #ifdef DEBUG
1451                                 g_message (G_GNUC_PRETTY_FUNCTION
1452                                            ": Got false match at %d len %d",
1453                                            pos, match_len);
1454 #endif
1455                         }
1456                         g_free (match);
1457                 }
1458 #ifdef DEBUG
1459                 g_message (G_GNUC_PRETTY_FUNCTION
1460                            ": New string length is %d (delta %d)",
1461                            mono_string_length (this)+len_delta, len_delta);
1462 #endif
1463                 
1464                 uret=(UChar *)g_malloc0 (sizeof(UChar) * (mono_string_length (this)+len_delta+2));
1465                 
1466                 for(oldpos=0, pos=usearch_first (search, &ec);
1467                     pos!=USEARCH_DONE;
1468                     pos=usearch_next (search, &ec)) {
1469                         match_len = usearch_getMatchedLength (search);
1470                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1471                         usearch_getMatchedText (search, match, match_len, &ec);
1472
1473                         /* Add the unmatched text */
1474                         u_strncat (uret, mono_string_chars (this)+oldpos,
1475                                    pos-oldpos);
1476                         if (ucol_strcoll (coll, match, match_len,
1477                                           mono_string_chars (old),
1478                                           mono_string_length (old)) == UCOL_EQUAL) {
1479                                 /* Then the replacement */
1480                                 u_strcat (uret, mono_string_chars (new));
1481                         } else {
1482                                 /* Then the original, because this is a
1483                                  * false match
1484                                  */
1485                                 u_strncat (uret, mono_string_chars (this)+pos,
1486                                            match_len);
1487                         }
1488                         oldpos=pos+match_len;
1489                         g_free (match);
1490                 }
1491                 
1492                 /* Finish off with the trailing unmatched text */
1493                 u_strcat (uret, mono_string_chars (this)+oldpos);
1494
1495                 ret=mono_string_from_utf16 ((gunichar2 *)uret);
1496         } else {
1497                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1498                            u_errorName (ec));
1499         }
1500
1501         usearch_close (search);
1502         
1503         mono_monitor_exit ((MonoObject *)comp);
1504         
1505 #ifdef DEBUG
1506         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));
1507 #endif
1508         
1509         return(ret);
1510 }
1511
1512 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1513 {
1514         MonoString *ret;
1515         UChar *udest;
1516         UErrorCode ec;
1517         char *icu_loc;
1518         int32_t len;
1519
1520         MONO_ARCH_SAVE_REGS;
1521
1522 #ifdef DEBUG
1523         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1524                    mono_string_to_utf8 (this));
1525 #endif
1526
1527 #ifdef DEBUG
1528         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1529 #endif
1530
1531         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1532         if(icu_loc==NULL) {
1533                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
1534                 return(NULL);
1535         }
1536         
1537         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1538         
1539         /* According to the docs, this might result in a longer or
1540          * shorter string than we started with...
1541          */
1542
1543         ec=U_ZERO_ERROR;
1544         len=u_strToLower (udest, mono_string_length (this)+1,
1545                           mono_string_chars (this), mono_string_length (this),
1546                           icu_loc, &ec);
1547         if(ec==U_BUFFER_OVERFLOW_ERROR ||
1548            ec==U_STRING_NOT_TERMINATED_WARNING) {
1549                 g_free (udest);
1550                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1551                 len=u_strToLower (udest, len+1, mono_string_chars (this),
1552                                   mono_string_length (this), icu_loc, &ec);
1553         }
1554
1555         if(U_SUCCESS (ec)) {
1556                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1557         } else {
1558                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToLower error: %s",
1559                            u_errorName (ec));
1560                 /* return something */
1561                 ret=this;
1562         }
1563         
1564         g_free (icu_loc);
1565         g_free (udest);
1566         
1567 #ifdef DEBUG
1568         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1569                    mono_string_to_utf8 (ret));
1570 #endif
1571
1572         return(ret);
1573 }
1574
1575 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1576 {
1577         MonoString *ret;
1578         UChar *udest;
1579         UErrorCode ec;
1580         char *icu_loc;
1581         int32_t len;
1582
1583         MONO_ARCH_SAVE_REGS;
1584
1585 #ifdef DEBUG
1586         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1587                    mono_string_to_utf8 (this));
1588 #endif
1589
1590 #ifdef DEBUG
1591         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1592 #endif
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_get_corlib (), "System", "SystemException"));
1597                 return(NULL);
1598         }
1599         
1600         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1601         
1602         /* According to the docs, this might result in a longer or
1603          * shorter string than we started with...
1604          */
1605
1606         ec=U_ZERO_ERROR;
1607         len=u_strToUpper (udest, mono_string_length (this)+1,
1608                           mono_string_chars (this), mono_string_length (this),
1609                           icu_loc, &ec);
1610         if(ec==U_BUFFER_OVERFLOW_ERROR ||
1611            ec==U_STRING_NOT_TERMINATED_WARNING) {
1612                 g_free (udest);
1613                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1614                 len=u_strToUpper (udest, len+1, mono_string_chars (this),
1615                                   mono_string_length (this), icu_loc, &ec);
1616         }
1617
1618         if(U_SUCCESS (ec)) {
1619                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1620         } else {
1621                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToUpper error: %s",
1622                            u_errorName (ec));
1623                 /* return something */
1624                 ret=this;
1625         }
1626         
1627         g_free (icu_loc);
1628         g_free (udest);
1629         
1630 #ifdef DEBUG
1631         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1632                    mono_string_to_utf8 (ret));
1633 #endif
1634         
1635         return(ret);
1636 }
1637
1638 gunichar2 ves_icall_System_Char_InternalToUpper_Comp (gunichar2 c, MonoCultureInfo *cult)
1639 {
1640         UChar udest;
1641         UErrorCode ec;
1642         char *icu_loc;
1643         int32_t len;
1644         
1645         MONO_ARCH_SAVE_REGS;
1646
1647         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1648         if(icu_loc==NULL) {
1649                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
1650                 return(0);
1651         }
1652         
1653         ec=U_ZERO_ERROR;
1654         len=u_strToUpper (&udest, 1, &c, 1, icu_loc, &ec);
1655         g_free (icu_loc);
1656
1657         if(U_SUCCESS (ec) && len==1) {
1658                 return udest;
1659         } else {
1660                 /* return something */
1661                 return c;
1662         }
1663 }
1664
1665
1666 gunichar2 ves_icall_System_Char_InternalToLower_Comp (gunichar2 c, MonoCultureInfo *cult)
1667 {
1668         UChar udest;
1669         UErrorCode ec;
1670         char *icu_loc;
1671         int32_t len;
1672         
1673         MONO_ARCH_SAVE_REGS;
1674
1675         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1676         if(icu_loc==NULL) {
1677                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
1678                 return(0);
1679         }
1680         
1681         ec=U_ZERO_ERROR;
1682         len=u_strToLower (&udest, 1, &c, 1, icu_loc, &ec);
1683         g_free (icu_loc);
1684
1685         if(U_SUCCESS (ec) && len==1) {
1686                 return udest;
1687         } else {
1688                 /* return something */
1689                 return c;
1690         }
1691 }
1692
1693 #else /* HAVE_ICU */
1694 static MonoString *string_invariant_tolower (MonoString *this);
1695 static MonoString *string_invariant_toupper (MonoString *this);
1696
1697 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
1698 {
1699         MONO_ARCH_SAVE_REGS;
1700         
1701         /* Always claim "unknown locale" if we don't have ICU (only
1702          * called for non-invariant locales)
1703          */
1704         mono_raise_exception((MonoException *)mono_exception_from_name(mono_get_corlib (), "System", "ArgumentException"));
1705 }
1706
1707 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
1708 {
1709         /* Nothing to do here */
1710 }
1711
1712 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
1713 {
1714         MONO_ARCH_SAVE_REGS;
1715         
1716         /* Do a normal ascii string compare, as we only know the
1717          * invariant locale if we dont have ICU
1718          */
1719         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
1720                                          options));
1721 }
1722
1723 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1724 {
1725         /* Nothing to do here */
1726 }
1727
1728 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1729 {
1730         MonoArray *arr;
1731         gint32 keylen, i;
1732
1733         MONO_ARCH_SAVE_REGS;
1734         
1735         keylen=mono_string_length (source);
1736         
1737         arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
1738                             keylen);
1739         for(i=0; i<keylen; i++) {
1740                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
1741         }
1742         
1743         key->key=arr;
1744 }
1745
1746 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1747 {
1748         MONO_ARCH_SAVE_REGS;
1749         
1750         return(string_invariant_indexof (source, sindex, count, value, first));
1751 }
1752
1753 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1754 {
1755         MONO_ARCH_SAVE_REGS;
1756         
1757         return(string_invariant_indexof_char (source, sindex, count, value,
1758                                               first));
1759 }
1760
1761 int ves_icall_System_Threading_Thread_current_lcid (void)
1762 {
1763         MONO_ARCH_SAVE_REGS;
1764         
1765         /* Invariant */
1766         return(0x007F);
1767 }
1768
1769 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1770 {
1771         MONO_ARCH_SAVE_REGS;
1772         
1773         /* Do a normal ascii string compare and replace, as we only
1774          * know the invariant locale if we dont have ICU
1775          */
1776         return(string_invariant_replace (this, old, new));
1777 }
1778
1779 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1780 {
1781         MONO_ARCH_SAVE_REGS;
1782         
1783         return(string_invariant_tolower (this));
1784 }
1785
1786 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1787 {
1788         MONO_ARCH_SAVE_REGS;
1789         
1790         return(string_invariant_toupper (this));
1791 }
1792
1793 gunichar2 ves_icall_System_Char_InternalToUpper_Comp (gunichar2 c, MonoCultureInfo *cult)
1794 {
1795         MONO_ARCH_SAVE_REGS;
1796
1797         return g_unichar_toupper (c);
1798 }
1799
1800
1801 gunichar2 ves_icall_System_Char_InternalToLower_Comp (gunichar2 c, MonoCultureInfo *cult)
1802 {
1803         MONO_ARCH_SAVE_REGS;
1804
1805         return g_unichar_tolower (c);
1806 }
1807
1808 static MonoString *string_invariant_tolower (MonoString *this)
1809 {
1810         MonoString *ret;
1811         gunichar2 *src; 
1812         gunichar2 *dest;
1813         gint32 i;
1814
1815         ret = mono_string_new_size(mono_domain_get (),
1816                                    mono_string_length(this));
1817
1818         src = mono_string_chars (this);
1819         dest = mono_string_chars (ret);
1820
1821         for (i = 0; i < mono_string_length (this); ++i) {
1822                 dest[i] = g_unichar_tolower(src[i]);
1823         }
1824
1825         return(ret);
1826 }
1827
1828 static MonoString *string_invariant_toupper (MonoString *this)
1829 {
1830         MonoString *ret;
1831         gunichar2 *src; 
1832         gunichar2 *dest;
1833         guint32 i;
1834
1835         ret = mono_string_new_size(mono_domain_get (),
1836                                    mono_string_length(this));
1837
1838         src = mono_string_chars (this);
1839         dest = mono_string_chars (ret);
1840
1841         for (i = 0; i < mono_string_length (this); ++i) {
1842                 dest[i] = g_unichar_toupper(src[i]);
1843         }
1844
1845         return(ret);
1846 }
1847
1848 #endif /* HAVE_ICU */
1849
1850 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
1851                                              gint32 options)
1852 {
1853         gint32 result;
1854         GUnicodeType c1type, c2type;
1855
1856         c1type = g_unichar_type (c1);
1857         c2type = g_unichar_type (c2);
1858
1859         if (options & CompareOptions_IgnoreCase) {
1860                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) - (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
1861         } else if (options & CompareOptions_Ordinal) {
1862                 // Rotor/ms return the full value just not -1 and 1
1863                 return (gint32) c1 - c2;
1864         } else {
1865                 /* No options. Kana, symbol and spacing options don't
1866                  * apply to the invariant culture.
1867                  */
1868                 if (c1type == G_UNICODE_UPPERCASE_LETTER &&
1869                     c2type == G_UNICODE_LOWERCASE_LETTER) {
1870                         return(1);
1871                 }
1872                                         
1873                 if (c1type == G_UNICODE_LOWERCASE_LETTER &&
1874                     c2type == G_UNICODE_UPPERCASE_LETTER) {
1875                         return(-1);
1876                 }
1877                 
1878                 result = (gint32) c1 - c2;
1879         }
1880
1881         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
1882 }
1883
1884 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
1885                                         gint32 len1, MonoString *str2,
1886                                         gint32 off2, gint32 len2,
1887                                         gint32 options)
1888 {
1889         /* c translation of C# code from old string.cs.. :) */
1890         gint32 length;
1891         gint32 charcmp;
1892         gunichar2 *ustr1;
1893         gunichar2 *ustr2;
1894         gint32 pos;
1895
1896         if(len1 >= len2) {
1897                 length=len1;
1898         } else {
1899                 length=len2;
1900         }
1901
1902         ustr1 = mono_string_chars(str1)+off1;
1903         ustr2 = mono_string_chars(str2)+off2;
1904
1905         pos = 0;
1906
1907         for (pos = 0; pos != length; pos++) {
1908                 if (pos >= len1 || pos >= len2)
1909                         break;
1910
1911                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
1912                                                         options);
1913                 if (charcmp != 0) {
1914                         return(charcmp);
1915                 }
1916         }
1917
1918         /* the lesser wins, so if we have looped until length we just
1919          * need to check the last char
1920          */
1921         if (pos == length) {
1922                 return(string_invariant_compare_char(ustr1[pos - 1],
1923                                                      ustr2[pos - 1], options));
1924         }
1925
1926         /* Test if one of the strings has been compared to the end */
1927         if (pos >= len1) {
1928                 if (pos >= len2) {
1929                         return(0);
1930                 } else {
1931                         return(-1);
1932                 }
1933         } else if (pos >= len2) {
1934                 return(1);
1935         }
1936
1937         /* if not, check our last char only.. (can this happen?) */
1938         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
1939 }
1940
1941 static MonoString *string_invariant_replace (MonoString *me,
1942                                              MonoString *oldValue,
1943                                              MonoString *newValue)
1944 {
1945         MonoString *ret;
1946         gunichar2 *src;
1947         gunichar2 *dest=NULL; /* shut gcc up */
1948         gunichar2 *oldstr;
1949         gunichar2 *newstr=NULL; /* shut gcc up here too */
1950         gint32 i, destpos;
1951         gint32 occurr;
1952         gint32 newsize;
1953         gint32 oldstrlen;
1954         gint32 newstrlen;
1955         gint32 srclen;
1956
1957         occurr = 0;
1958         destpos = 0;
1959
1960         oldstr = mono_string_chars(oldValue);
1961         oldstrlen = mono_string_length(oldValue);
1962
1963         if (NULL != newValue) {
1964                 newstr = mono_string_chars(newValue);
1965                 newstrlen = mono_string_length(newValue);
1966         } else
1967                 newstrlen = 0;
1968
1969         src = mono_string_chars(me);
1970         srclen = mono_string_length(me);
1971
1972         if (oldstrlen != newstrlen) {
1973                 i = 0;
1974                 while (i <= srclen - oldstrlen) {
1975                         if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1976                                 occurr++;
1977                                 i += oldstrlen;
1978                         }
1979                         else
1980                                 i ++;
1981                 }
1982                 if (occurr == 0)
1983                         return me;
1984                 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
1985         } else
1986                 newsize = srclen;
1987
1988         ret = NULL;
1989         i = 0;
1990         while (i < srclen) {
1991                 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1992                         if (ret == NULL) {
1993                                 ret = mono_string_new_size( mono_domain_get (), newsize);
1994                                 dest = mono_string_chars(ret);
1995                                 memcpy (dest, src, i * sizeof(gunichar2));
1996                         }
1997                         if (newstrlen > 0) {
1998                                 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
1999                                 destpos += newstrlen;
2000                         }
2001                         i += oldstrlen;
2002                         continue;
2003                 } else if (ret != NULL) {
2004                         dest[destpos] = src[i];
2005                 }
2006                 destpos++;
2007                 i++;
2008         }
2009         
2010         if (ret == NULL)
2011                 return me;
2012
2013         return ret;
2014 }
2015
2016 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
2017                                         gint32 count, MonoString *value,
2018                                         MonoBoolean first)
2019 {
2020         gint32 lencmpstr;
2021         gunichar2 *src;
2022         gunichar2 *cmpstr;
2023         gint32 pos,i;
2024         
2025         lencmpstr = mono_string_length(value);
2026         
2027         src = mono_string_chars(source);
2028         cmpstr = mono_string_chars(value);
2029
2030         if(first) {
2031                 count -= lencmpstr;
2032                 for(pos=sindex;pos <= sindex+count;pos++) {
2033                         for(i=0;src[pos+i]==cmpstr[i];) {
2034                                 if(++i==lencmpstr) {
2035                                         return(pos);
2036                                 }
2037                         }
2038                 }
2039                 
2040                 return(-1);
2041         } else {
2042                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
2043                         if(memcmp (src+pos, cmpstr,
2044                                    lencmpstr*sizeof(gunichar2))==0) {
2045                                 return(pos);
2046                         }
2047                 }
2048                 
2049                 return(-1);
2050         }
2051 }
2052
2053 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
2054                                              gint32 count, gunichar2 value,
2055                                              MonoBoolean first)
2056 {
2057         gint32 pos;
2058         gunichar2 *src;
2059
2060         src = mono_string_chars(source);
2061         if(first) {
2062                 for (pos = sindex; pos != count + sindex; pos++) {
2063                         if (src [pos] == value) {
2064                                 return(pos);
2065                         }
2066                 }
2067
2068                 return(-1);
2069         } else {
2070                 for (pos = sindex; pos > sindex - count; pos--) {
2071                         if (src [pos] == value)
2072                                 return(pos);
2073                 }
2074
2075                 return(-1);
2076         }
2077 }
2078