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