2007-05-15 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 #include <mono/metadata/normalization-tables.h>
25
26
27 #include <locale.h>
28
29 #undef DEBUG
30
31 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
32                                              gint32 options);
33 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
34                                         gint32 len1, MonoString *str2,
35                                         gint32 off2, gint32 len2,
36                                         gint32 options);
37 static MonoString *string_invariant_replace (MonoString *me,
38                                              MonoString *oldValue,
39                                              MonoString *newValue);
40 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
41                                         gint32 count, MonoString *value,
42                                         MonoBoolean first);
43 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
44                                              gint32 count, gunichar2 value,
45                                              MonoBoolean first);
46
47 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
48
49 static const RegionInfoEntry* region_info_entry_from_lcid (int lcid);
50
51 static int
52 culture_lcid_locator (const void *a, const void *b)
53 {
54         const CultureInfoEntry *aa = a;
55         const CultureInfoEntry *bb = b;
56
57         return (aa->lcid - bb->lcid);
58 }
59
60 static int
61 region_lcid_locator (const void *a, const void *b)
62 {
63         const int *lcid = a;
64         const CultureInfoEntry *bb = b;
65
66         return *lcid - bb->lcid;
67 }
68
69 static int
70 culture_name_locator (const void *a, const void *b)
71 {
72         const char *aa = a;
73         const CultureInfoNameEntry *bb = b;
74         int ret;
75         
76         ret = strcmp (aa, idx2string (bb->name));
77
78         return ret;
79 }
80
81 static int
82 region_name_locator (const void *a, const void *b)
83 {
84         const char *aa = a;
85         const RegionInfoNameEntry *bb = b;
86         int ret;
87         
88         ret = strcmp (aa, idx2string (bb->name));
89
90         return ret;
91 }
92
93 static MonoArray*
94 create_group_sizes_array (const gint *gs, gint ml)
95 {
96         MonoArray *ret;
97         int i, len = 0;
98
99         for (i = 0; i < ml; i++) {
100                 if (gs [i] == -1)
101                         break;
102                 len++;
103         }
104         
105         ret = mono_array_new (mono_domain_get (),
106                         mono_get_int32_class (), len);
107
108         for(i = 0; i < len; i++)
109                 mono_array_set (ret, gint32, i, gs [i]);
110
111         return ret;
112 }
113
114 static MonoArray*
115 create_names_array_idx (const guint16 *names, int ml)
116 {
117         MonoArray *ret;
118         MonoDomain *domain;
119         int i, len = 0;
120
121         if (names == NULL)
122                 return NULL;
123
124         domain = mono_domain_get ();
125
126         for (i = 0; i < ml; i++) {
127                 if (names [i] == 0)
128                         break;
129                 len++;
130         }
131
132         ret = mono_array_new (mono_domain_get (), mono_get_string_class (), len);
133
134         for(i = 0; i < len; i++)
135                 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
136
137         return ret;
138 }
139
140 void
141 ves_icall_System_Globalization_CultureInfo_construct_datetime_format (MonoCultureInfo *this)
142 {
143         MonoDomain *domain;
144         MonoDateTimeFormatInfo *datetime;
145         const DateTimeFormatEntry *dfe;
146
147         MONO_ARCH_SAVE_REGS;
148
149         g_assert (this->datetime_index >= 0);
150
151         datetime = this->datetime_format;
152         dfe = &datetime_format_entries [this->datetime_index];
153
154         domain = mono_domain_get ();
155
156         MONO_OBJECT_SETREF (datetime, AbbreviatedDayNames, create_names_array_idx (dfe->abbreviated_day_names,
157                         NUM_DAYS));
158         MONO_OBJECT_SETREF (datetime, AbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_names,
159                         NUM_MONTHS));
160         MONO_OBJECT_SETREF (datetime, AMDesignator, mono_string_new (domain, idx2string (dfe->am_designator)));
161         datetime->CalendarWeekRule = dfe->calendar_week_rule;
162         MONO_OBJECT_SETREF (datetime, DateSeparator, mono_string_new (domain, idx2string (dfe->date_separator)));
163         MONO_OBJECT_SETREF (datetime, DayNames, create_names_array_idx (dfe->day_names, NUM_DAYS));
164         datetime->FirstDayOfWeek = dfe->first_day_of_week;
165         MONO_OBJECT_SETREF (datetime, FullDateTimePattern, mono_string_new (domain, idx2string (dfe->full_date_time_pattern)));
166         MONO_OBJECT_SETREF (datetime, LongDatePattern, mono_string_new (domain, idx2string (dfe->long_date_pattern)));
167         MONO_OBJECT_SETREF (datetime, LongTimePattern, mono_string_new (domain, idx2string (dfe->long_time_pattern)));
168         MONO_OBJECT_SETREF (datetime, MonthDayPattern, mono_string_new (domain, idx2string (dfe->month_day_pattern)));
169         MONO_OBJECT_SETREF (datetime, MonthNames, create_names_array_idx (dfe->month_names, NUM_MONTHS));
170         MONO_OBJECT_SETREF (datetime, PMDesignator, mono_string_new (domain, idx2string (dfe->pm_designator)));
171         MONO_OBJECT_SETREF (datetime, ShortDatePattern, mono_string_new (domain, idx2string (dfe->short_date_pattern)));
172         MONO_OBJECT_SETREF (datetime, ShortTimePattern, mono_string_new (domain, idx2string (dfe->short_time_pattern)));
173         MONO_OBJECT_SETREF (datetime, TimeSeparator, mono_string_new (domain, idx2string (dfe->time_separator)));
174         MONO_OBJECT_SETREF (datetime, YearMonthPattern, mono_string_new (domain, idx2string (dfe->year_month_pattern)));
175         MONO_OBJECT_SETREF (datetime, ShortDatePatterns, create_names_array_idx (dfe->short_date_patterns,
176                         NUM_SHORT_DATE_PATTERNS));
177         MONO_OBJECT_SETREF (datetime, LongDatePatterns, create_names_array_idx (dfe->long_date_patterns,
178                         NUM_LONG_DATE_PATTERNS));
179         MONO_OBJECT_SETREF (datetime, ShortTimePatterns, create_names_array_idx (dfe->short_time_patterns,
180                         NUM_SHORT_TIME_PATTERNS));
181         MONO_OBJECT_SETREF (datetime, LongTimePatterns, create_names_array_idx (dfe->long_time_patterns,
182                         NUM_LONG_TIME_PATTERNS));
183
184 }
185
186 void
187 ves_icall_System_Globalization_CultureInfo_construct_number_format (MonoCultureInfo *this)
188 {
189         MonoDomain *domain;
190         MonoNumberFormatInfo *number;
191         const NumberFormatEntry *nfe;
192
193         MONO_ARCH_SAVE_REGS;
194
195         g_assert (this->number_format != 0);
196
197         number = this->number_format;
198         nfe = &number_format_entries [this->number_index];
199
200         domain = mono_domain_get ();
201
202         number->currencyDecimalDigits = nfe->currency_decimal_digits;
203         MONO_OBJECT_SETREF (number, currencyDecimalSeparator, mono_string_new (domain,
204                         idx2string (nfe->currency_decimal_separator)));
205         MONO_OBJECT_SETREF (number, currencyGroupSeparator, mono_string_new (domain,
206                         idx2string (nfe->currency_group_separator)));
207         MONO_OBJECT_SETREF (number, currencyGroupSizes, create_group_sizes_array (nfe->currency_group_sizes,
208                         GROUP_SIZE));
209         number->currencyNegativePattern = nfe->currency_negative_pattern;
210         number->currencyPositivePattern = nfe->currency_positive_pattern;
211         MONO_OBJECT_SETREF (number, currencySymbol, mono_string_new (domain, idx2string (nfe->currency_symbol)));
212         MONO_OBJECT_SETREF (number, naNSymbol, mono_string_new (domain, idx2string (nfe->nan_symbol)));
213         MONO_OBJECT_SETREF (number, negativeInfinitySymbol, mono_string_new (domain,
214                         idx2string (nfe->negative_infinity_symbol)));
215         MONO_OBJECT_SETREF (number, negativeSign, mono_string_new (domain, idx2string (nfe->negative_sign)));
216         number->numberDecimalDigits = nfe->number_decimal_digits;
217         MONO_OBJECT_SETREF (number, numberDecimalSeparator, mono_string_new (domain,
218                         idx2string (nfe->number_decimal_separator)));
219         MONO_OBJECT_SETREF (number, numberGroupSeparator, mono_string_new (domain, idx2string (nfe->number_group_separator)));
220         MONO_OBJECT_SETREF (number, numberGroupSizes, create_group_sizes_array (nfe->number_group_sizes,
221                         GROUP_SIZE));
222         number->numberNegativePattern = nfe->number_negative_pattern;
223         number->percentDecimalDigits = nfe->percent_decimal_digits;
224         MONO_OBJECT_SETREF (number, percentDecimalSeparator, mono_string_new (domain,
225                         idx2string (nfe->percent_decimal_separator)));
226         MONO_OBJECT_SETREF (number, percentGroupSeparator, mono_string_new (domain,
227                         idx2string (nfe->percent_group_separator)));
228         MONO_OBJECT_SETREF (number, percentGroupSizes, create_group_sizes_array (nfe->percent_group_sizes,
229                         GROUP_SIZE));
230         number->percentNegativePattern = nfe->percent_negative_pattern;
231         number->percentPositivePattern = nfe->percent_positive_pattern;
232         MONO_OBJECT_SETREF (number, percentSymbol, mono_string_new (domain, idx2string (nfe->percent_symbol)));
233         MONO_OBJECT_SETREF (number, perMilleSymbol, mono_string_new (domain, idx2string (nfe->per_mille_symbol)));
234         MONO_OBJECT_SETREF (number, positiveInfinitySymbol, mono_string_new (domain,
235                         idx2string (nfe->positive_infinity_symbol)));
236         MONO_OBJECT_SETREF (number, positiveSign, mono_string_new (domain, idx2string (nfe->positive_sign)));
237 }
238
239 static MonoBoolean
240 construct_culture (MonoCultureInfo *this, const CultureInfoEntry *ci)
241 {
242         MonoDomain *domain = mono_domain_get ();
243
244         this->lcid = ci->lcid;
245         MONO_OBJECT_SETREF (this, name, mono_string_new (domain, idx2string (ci->name)));
246         MONO_OBJECT_SETREF (this, icu_name, mono_string_new (domain, idx2string (ci->icu_name)));
247         MONO_OBJECT_SETREF (this, displayname, mono_string_new (domain, idx2string (ci->displayname)));
248         MONO_OBJECT_SETREF (this, englishname, mono_string_new (domain, idx2string (ci->englishname)));
249         MONO_OBJECT_SETREF (this, nativename, mono_string_new (domain, idx2string (ci->nativename)));
250         MONO_OBJECT_SETREF (this, win3lang, mono_string_new (domain, idx2string (ci->win3lang)));
251         MONO_OBJECT_SETREF (this, iso3lang, mono_string_new (domain, idx2string (ci->iso3lang)));
252         MONO_OBJECT_SETREF (this, iso2lang, mono_string_new (domain, idx2string (ci->iso2lang)));
253         MONO_OBJECT_SETREF (this, territory, mono_string_new (domain, idx2string (ci->territory)));
254         this->parent_lcid = ci->parent_lcid;
255         this->specific_lcid = ci->specific_lcid;
256         this->datetime_index = ci->datetime_format_index;
257         this->number_index = ci->number_format_index;
258         this->calendar_data = ci->calendar_data;
259         this->text_info_data = &ci->text_info;
260         
261         return TRUE;
262 }
263
264 static MonoBoolean
265 construct_region (MonoRegionInfo *this, const RegionInfoEntry *ri)
266 {
267         MonoDomain *domain = mono_domain_get ();
268
269         this->region_id = ri->region_id;
270         MONO_OBJECT_SETREF (this, iso2name, mono_string_new (domain, idx2string (ri->iso2name)));
271         MONO_OBJECT_SETREF (this, iso3name, mono_string_new (domain, idx2string (ri->iso3name)));
272         MONO_OBJECT_SETREF (this, win3name, mono_string_new (domain, idx2string (ri->win3name)));
273         MONO_OBJECT_SETREF (this, english_name, mono_string_new (domain, idx2string (ri->english_name)));
274         MONO_OBJECT_SETREF (this, currency_symbol, mono_string_new (domain, idx2string (ri->currency_symbol)));
275         MONO_OBJECT_SETREF (this, iso_currency_symbol, mono_string_new (domain, idx2string (ri->iso_currency_symbol)));
276         MONO_OBJECT_SETREF (this, currency_english_name, mono_string_new (domain, idx2string (ri->currency_english_name)));
277         
278         return TRUE;
279 }
280
281 static gboolean
282 construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name)
283 {
284         const CultureInfoEntry *entry;
285         const CultureInfoNameEntry *ne;
286
287         MONO_ARCH_SAVE_REGS;
288
289         ne = bsearch (name, culture_name_entries, NUM_CULTURE_ENTRIES,
290                         sizeof (CultureInfoNameEntry), culture_name_locator);
291
292         if (ne == NULL)
293                 return FALSE;
294
295         entry = &culture_entries [ne->culture_entry_index];
296
297         /* try avoiding another lookup, often the culture is its own specific culture */
298         if (entry->lcid != entry->specific_lcid)
299                 entry = culture_info_entry_from_lcid (entry->specific_lcid);
300
301         return construct_culture (ci, entry);
302 }
303
304 static const CultureInfoEntry*
305 culture_info_entry_from_lcid (int lcid)
306 {
307         const CultureInfoEntry *ci;
308         CultureInfoEntry key;
309
310         key.lcid = lcid;
311         ci = bsearch (&key, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
312
313         return ci;
314 }
315
316 static const RegionInfoEntry*
317 region_info_entry_from_lcid (int lcid)
318 {
319         const RegionInfoEntry *entry;
320         const CultureInfoEntry *ne;
321
322         MONO_ARCH_SAVE_REGS;
323
324         ne = bsearch (&lcid, culture_entries, NUM_CULTURE_ENTRIES,
325                         sizeof (CultureInfoEntry), region_lcid_locator);
326
327         if (ne == NULL)
328                 return FALSE;
329
330         entry = &region_entries [ne->region_entry_index];
331
332         return entry;
333 }
334
335 /*
336  * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu)
337  * Copyright (c) 1995-2003 International Business Machines Corporation and others
338  * All rights reserved.
339  */
340 static gchar*
341 get_posix_locale (void)
342 {
343         const gchar* posix_locale = NULL;
344
345         posix_locale = g_getenv("LC_ALL");
346         if (posix_locale == 0) {
347                 posix_locale = g_getenv("LANG");
348                 if (posix_locale == 0) {
349                         posix_locale = setlocale(LC_ALL, NULL);
350                 }
351         }
352
353         if (posix_locale == NULL)
354                 return NULL;
355
356         if ((strcmp ("C", posix_locale) == 0) || (strchr (posix_locale, ' ') != NULL)
357                         || (strchr (posix_locale, '/') != NULL)) {
358                 /*
359                  * HPUX returns 'C C C C C C C'
360                  * Solaris can return /en_US/C/C/C/C/C on the second try.
361                  * Maybe we got some garbage.
362                  */
363                 return NULL;
364         }
365
366         return g_strdup (posix_locale);
367 }
368
369 static gchar*
370 get_current_locale_name (void)
371 {
372         gchar *locale;
373         gchar *corrected = NULL;
374         const gchar *p;
375         gchar *c;
376
377 #ifdef PLATFORM_WIN32
378         locale = g_win32_getlocale ();
379 #else   
380         locale = get_posix_locale ();
381 #endif  
382
383         if (locale == NULL)
384                 return NULL;
385
386         if ((p = strchr (locale, '.')) != NULL) {
387                 /* assume new locale can't be larger than old one? */
388                 corrected = malloc (strlen (locale));
389                 strncpy (corrected, locale, p - locale);
390                 corrected [p - locale] = 0;
391
392                 /* do not copy after the @ */
393                 if ((p = strchr (corrected, '@')) != NULL)
394                         corrected [p - corrected] = 0;
395         }
396
397         /* Note that we scan the *uncorrected* ID. */
398         if ((p = strrchr (locale, '@')) != NULL) {
399
400                 /*
401                  * In Mono we dont handle the '@' modifier because we do
402                  * not have any cultures that use it. We just trim it
403                  * off of the end of the name.
404                  */
405
406                 if (corrected == NULL) {
407                         corrected = malloc (strlen (locale));
408                         strncpy (corrected, locale, p - locale);
409                         corrected [p - locale] = 0;
410                 }
411         }
412
413         if (corrected == NULL)
414                 corrected = locale;
415         else
416                 g_free (locale);
417
418         if ((c = strchr (corrected, '_')) != NULL)
419                 *c = '-';
420
421         g_strdown (corrected);
422
423         return corrected;
424 }        
425
426 MonoBoolean
427 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (MonoCultureInfo *ci)
428 {
429         gchar *locale;
430         gboolean ret;
431
432         MONO_ARCH_SAVE_REGS;
433
434         locale = get_current_locale_name ();
435         if (locale == NULL)
436                 return FALSE;
437
438         ret = construct_culture_from_specific_name (ci, locale);
439         g_free (locale);
440
441         return ret;
442 }
443
444 MonoBoolean
445 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this,
446                 gint lcid)
447 {
448         const CultureInfoEntry *ci;
449         
450         MONO_ARCH_SAVE_REGS;
451
452         ci = culture_info_entry_from_lcid (lcid);
453         if(ci == NULL)
454                 return FALSE;
455
456         return construct_culture (this, ci);
457 }
458
459 MonoBoolean
460 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this,
461                 MonoString *name)
462 {
463         const CultureInfoNameEntry *ne;
464         char *n;
465         
466         MONO_ARCH_SAVE_REGS;
467
468         n = mono_string_to_utf8 (name);
469         ne = bsearch (n, culture_name_entries, NUM_CULTURE_ENTRIES,
470                         sizeof (CultureInfoNameEntry), culture_name_locator);
471
472         if (ne == NULL) {
473                 /*g_print ("ne (%s) is null\n", n);*/
474                 g_free (n);
475                 return FALSE;
476         }
477         g_free (n);
478
479         return construct_culture (this, &culture_entries [ne->culture_entry_index]);
480 }
481
482 MonoBoolean
483 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
484                 MonoString *name)
485 {
486         gchar *locale;
487         gboolean ret;
488
489         MONO_ARCH_SAVE_REGS;
490
491         locale = mono_string_to_utf8 (name);
492         ret = construct_culture_from_specific_name (ci, locale);
493         g_free (locale);
494
495         return ret;
496 }
497
498 MonoBoolean
499 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this,
500                 gint lcid)
501 {
502         const RegionInfoEntry *ri;
503         
504         MONO_ARCH_SAVE_REGS;
505
506         ri = region_info_entry_from_lcid (lcid);
507         if(ri == NULL)
508                 return FALSE;
509
510         return construct_region (this, ri);
511 }
512
513 MonoBoolean
514 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this,
515                 MonoString *name)
516 {
517         const RegionInfoNameEntry *ne;
518         char *n;
519         
520         MONO_ARCH_SAVE_REGS;
521
522         n = mono_string_to_utf8 (name);
523         ne = bsearch (n, region_name_entries, NUM_REGION_ENTRIES,
524                 sizeof (RegionInfoNameEntry), region_name_locator);
525
526         if (ne == NULL) {
527                 /*g_print ("ne (%s) is null\n", n);*/
528                 g_free (n);
529                 return FALSE;
530         }
531         g_free (n);
532
533         return construct_region (this, &region_entries [ne->region_entry_index]);
534 }
535
536 MonoArray*
537 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
538                 MonoBoolean specific, MonoBoolean installed)
539 {
540         MonoArray *ret;
541         MonoClass *class;
542         MonoCultureInfo *culture;
543         MonoDomain *domain;
544         const CultureInfoEntry *ci;
545         gint i, len;
546         gboolean is_neutral;
547
548         MONO_ARCH_SAVE_REGS;
549
550         domain = mono_domain_get ();
551
552         len = 0;
553         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
554                 ci = &culture_entries [i];
555                 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
556                 if ((neutral && is_neutral) || (specific && !is_neutral))
557                         len++;
558         }
559
560         class = mono_class_from_name (mono_get_corlib (),
561                         "System.Globalization", "CultureInfo");
562
563         /* The InvariantCulture is not in culture_entries */
564         /* We reserve the first slot in the array for it */
565         if (neutral)
566                 len++;
567
568         ret = mono_array_new (domain, class, len);
569
570         if (len == 0)
571                 return ret;
572
573         len = 0;
574         if (neutral)
575                 mono_array_setref (ret, len++, NULL);
576
577         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
578                 ci = &culture_entries [i];
579                 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
580                 if ((neutral && is_neutral) || (specific && !is_neutral)) {
581                         culture = (MonoCultureInfo *) mono_object_new (domain, class);
582                         mono_runtime_object_init ((MonoObject *) culture);
583                         construct_culture (culture, ci);
584                         mono_array_setref (ret, len++, culture);
585                 }
586         }
587
588         return ret;
589 }
590
591 /**
592  * ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral:
593  * 
594  * Set is_neutral and return TRUE if the culture is found. If it is not found return FALSE.
595  */
596 MonoBoolean
597 ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral (gint lcid, MonoBoolean *is_neutral)
598 {
599         const CultureInfoEntry *entry;
600
601         MONO_ARCH_SAVE_REGS;
602
603         entry = culture_info_entry_from_lcid (lcid);
604
605         if (entry == NULL)
606                 return FALSE;
607
608         *is_neutral = (entry->specific_lcid == 0);
609
610         return TRUE;
611 }
612
613 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
614 {
615         /* Nothing to do here */
616 }
617
618 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
619 {
620         MONO_ARCH_SAVE_REGS;
621         
622         /* Do a normal ascii string compare, as we only know the
623          * invariant locale if we dont have ICU
624          */
625         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
626                                          options));
627 }
628
629 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
630 {
631         /* Nothing to do here */
632 }
633
634 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
635 {
636         MonoArray *arr;
637         gint32 keylen, i;
638
639         MONO_ARCH_SAVE_REGS;
640         
641         keylen=mono_string_length (source);
642         
643         arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
644                             keylen);
645         for(i=0; i<keylen; i++) {
646                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
647         }
648         
649         MONO_OBJECT_SETREF (key, key, arr);
650 }
651
652 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
653 {
654         MONO_ARCH_SAVE_REGS;
655         
656         return(string_invariant_indexof (source, sindex, count, value, first));
657 }
658
659 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
660 {
661         MONO_ARCH_SAVE_REGS;
662         
663         return(string_invariant_indexof_char (source, sindex, count, value,
664                                               first));
665 }
666
667 int ves_icall_System_Threading_Thread_current_lcid (void)
668 {
669         MONO_ARCH_SAVE_REGS;
670         
671         /* Invariant */
672         return(0x007F);
673 }
674
675 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
676 {
677         MONO_ARCH_SAVE_REGS;
678         
679         /* Do a normal ascii string compare and replace, as we only
680          * know the invariant locale if we dont have ICU
681          */
682         return(string_invariant_replace (this, old, new));
683 }
684
685 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
686                                              gint32 options)
687 {
688         gint32 result;
689         GUnicodeType c1type, c2type;
690
691         /* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
692         if (options & CompareOptions_Ordinal) 
693                 return (gint32) c1 - c2;
694         
695         c1type = g_unichar_type (c1);
696         c2type = g_unichar_type (c2);
697         
698         if (options & CompareOptions_IgnoreCase) {
699                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
700                         (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
701         } else {
702                 /*
703                  * No options. Kana, symbol and spacing options don't
704                  * apply to the invariant culture.
705                  */
706
707                 /*
708                  * FIXME: here we must use the information from c1type and c2type
709                  * to find out the proper collation, even on the InvariantCulture, the
710                  * sorting is not done by computing the unicode values, but their
711                  * actual sort order.
712                  */
713                 result = (gint32) c1 - c2;
714         }
715
716         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
717 }
718
719 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
720                                         gint32 len1, MonoString *str2,
721                                         gint32 off2, gint32 len2,
722                                         gint32 options)
723 {
724         /* c translation of C# code from old string.cs.. :) */
725         gint32 length;
726         gint32 charcmp;
727         gunichar2 *ustr1;
728         gunichar2 *ustr2;
729         gint32 pos;
730
731         if(len1 >= len2) {
732                 length=len1;
733         } else {
734                 length=len2;
735         }
736
737         ustr1 = mono_string_chars(str1)+off1;
738         ustr2 = mono_string_chars(str2)+off2;
739
740         pos = 0;
741
742         for (pos = 0; pos != length; pos++) {
743                 if (pos >= len1 || pos >= len2)
744                         break;
745
746                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
747                                                         options);
748                 if (charcmp != 0) {
749                         return(charcmp);
750                 }
751         }
752
753         /* the lesser wins, so if we have looped until length we just
754          * need to check the last char
755          */
756         if (pos == length) {
757                 return(string_invariant_compare_char(ustr1[pos - 1],
758                                                      ustr2[pos - 1], options));
759         }
760
761         /* Test if one of the strings has been compared to the end */
762         if (pos >= len1) {
763                 if (pos >= len2) {
764                         return(0);
765                 } else {
766                         return(-1);
767                 }
768         } else if (pos >= len2) {
769                 return(1);
770         }
771
772         /* if not, check our last char only.. (can this happen?) */
773         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
774 }
775
776 static MonoString *string_invariant_replace (MonoString *me,
777                                              MonoString *oldValue,
778                                              MonoString *newValue)
779 {
780         MonoString *ret;
781         gunichar2 *src;
782         gunichar2 *dest=NULL; /* shut gcc up */
783         gunichar2 *oldstr;
784         gunichar2 *newstr=NULL; /* shut gcc up here too */
785         gint32 i, destpos;
786         gint32 occurr;
787         gint32 newsize;
788         gint32 oldstrlen;
789         gint32 newstrlen;
790         gint32 srclen;
791
792         occurr = 0;
793         destpos = 0;
794
795         oldstr = mono_string_chars(oldValue);
796         oldstrlen = mono_string_length(oldValue);
797
798         if (NULL != newValue) {
799                 newstr = mono_string_chars(newValue);
800                 newstrlen = mono_string_length(newValue);
801         } else
802                 newstrlen = 0;
803
804         src = mono_string_chars(me);
805         srclen = mono_string_length(me);
806
807         if (oldstrlen != newstrlen) {
808                 i = 0;
809                 while (i <= srclen - oldstrlen) {
810                         if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
811                                 occurr++;
812                                 i += oldstrlen;
813                         }
814                         else
815                                 i ++;
816                 }
817                 if (occurr == 0)
818                         return me;
819                 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
820         } else
821                 newsize = srclen;
822
823         ret = NULL;
824         i = 0;
825         while (i < srclen) {
826                 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
827                         if (ret == NULL) {
828                                 ret = mono_string_new_size( mono_domain_get (), newsize);
829                                 dest = mono_string_chars(ret);
830                                 memcpy (dest, src, i * sizeof(gunichar2));
831                         }
832                         if (newstrlen > 0) {
833                                 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
834                                 destpos += newstrlen;
835                         }
836                         i += oldstrlen;
837                         continue;
838                 } else if (ret != NULL) {
839                         dest[destpos] = src[i];
840                 }
841                 destpos++;
842                 i++;
843         }
844         
845         if (ret == NULL)
846                 return me;
847
848         return ret;
849 }
850
851 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
852                                         gint32 count, MonoString *value,
853                                         MonoBoolean first)
854 {
855         gint32 lencmpstr;
856         gunichar2 *src;
857         gunichar2 *cmpstr;
858         gint32 pos,i;
859         
860         lencmpstr = mono_string_length(value);
861         
862         src = mono_string_chars(source);
863         cmpstr = mono_string_chars(value);
864
865         if(first) {
866                 count -= lencmpstr;
867                 for(pos=sindex;pos <= sindex+count;pos++) {
868                         for(i=0;src[pos+i]==cmpstr[i];) {
869                                 if(++i==lencmpstr) {
870                                         return(pos);
871                                 }
872                         }
873                 }
874                 
875                 return(-1);
876         } else {
877                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
878                         if(memcmp (src+pos, cmpstr,
879                                    lencmpstr*sizeof(gunichar2))==0) {
880                                 return(pos);
881                         }
882                 }
883                 
884                 return(-1);
885         }
886 }
887
888 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
889                                              gint32 count, gunichar2 value,
890                                              MonoBoolean first)
891 {
892         gint32 pos;
893         gunichar2 *src;
894
895         src = mono_string_chars(source);
896         if(first) {
897                 for (pos = sindex; pos != count + sindex; pos++) {
898                         if (src [pos] == value) {
899                                 return(pos);
900                         }
901                 }
902
903                 return(-1);
904         } else {
905                 for (pos = sindex; pos > sindex - count; pos--) {
906                         if (src [pos] == value)
907                                 return(pos);
908                 }
909
910                 return(-1);
911         }
912 }
913
914 void load_normalization_resource (guint8 **argProps,
915                                   guint8 **argMappedChars,
916                                   guint8 **argCharMapIndex,
917                                   guint8 **argHelperIndex,
918                                   guint8 **argMapIdxToComposite,
919                                   guint8 **argCombiningClass)
920 {
921         *argProps = (guint8*)props;
922         *argMappedChars = (guint8*) mappedChars;
923         *argCharMapIndex = (guint8*) charMapIndex;
924         *argHelperIndex = (guint8*) helperIndex;
925         *argMapIdxToComposite = (guint8*) mapIdxToComposite;
926         *argCombiningClass = (guint8*)combiningClass;
927 }
928
929