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