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