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