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