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