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