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