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