/*
* locales.c: Culture-sensitive handling
*
- * Author:
+ * Authors:
* Dick Porter (dick@ximian.com)
* Mohammad DAMT (mdamt@cdl2000.com)
+ * Marek Safar (marek.safar@gmail.com)
*
- * (C) 2003 Ximian, Inc.
+ * Copyright 2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
* (C) 2003 PT Cakram Datalingga Duaribu http://www.cdl2000.com
+ * Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
*/
#include <config.h>
#include <mono/metadata/locales.h>
#include <mono/metadata/culture-info.h>
#include <mono/metadata/culture-info-tables.h>
-#include <mono/metadata/normalization-tables.h>
+#include <mono/utils/bsearch.h>
+#ifndef DISABLE_NORMALIZATION
+#include <mono/metadata/normalization-tables.h>
+#endif
#include <locale.h>
+#if defined(__APPLE__)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
#undef DEBUG
static int
culture_lcid_locator (const void *a, const void *b)
-{
- const CultureInfoEntry *aa = a;
- const CultureInfoEntry *bb = b;
-
- return (aa->lcid - bb->lcid);
-}
-
-static int
-region_lcid_locator (const void *a, const void *b)
{
const int *lcid = a;
const CultureInfoEntry *bb = b;
len++;
}
- ret = mono_array_new (mono_domain_get (),
+ ret = mono_array_new_cached (mono_domain_get (),
mono_get_int32_class (), len);
for(i = 0; i < len; i++)
static MonoArray*
create_names_array_idx (const guint16 *names, int ml)
+{
+ MonoArray *ret;
+ MonoDomain *domain;
+ int i;
+
+ if (names == NULL)
+ return NULL;
+
+ domain = mono_domain_get ();
+
+ ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), ml);
+
+ for(i = 0; i < ml; i++)
+ mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
+
+ return ret;
+}
+
+static MonoArray*
+create_names_array_idx_dynamic (const guint16 *names, int ml)
{
MonoArray *ret;
MonoDomain *domain;
len++;
}
- ret = mono_array_new (mono_domain_get (), mono_get_string_class (), len);
+ ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), len);
for(i = 0; i < len; i++)
mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
domain = mono_domain_get ();
- MONO_OBJECT_SETREF (datetime, AbbreviatedDayNames, create_names_array_idx (dfe->abbreviated_day_names,
+ datetime->readOnly = this->is_read_only;
+ MONO_OBJECT_SETREF (datetime, AbbreviatedDayNames, create_names_array_idx (dfe->abbreviated_day_names,
NUM_DAYS));
MONO_OBJECT_SETREF (datetime, AbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_names,
NUM_MONTHS));
datetime->CalendarWeekRule = dfe->calendar_week_rule;
MONO_OBJECT_SETREF (datetime, DateSeparator, mono_string_new (domain, idx2string (dfe->date_separator)));
MONO_OBJECT_SETREF (datetime, DayNames, create_names_array_idx (dfe->day_names, NUM_DAYS));
+ MONO_OBJECT_SETREF (datetime, ShortestDayNames, create_names_array_idx (dfe->shortest_day_names, NUM_DAYS));
datetime->FirstDayOfWeek = dfe->first_day_of_week;
- MONO_OBJECT_SETREF (datetime, FullDateTimePattern, mono_string_new (domain, idx2string (dfe->full_date_time_pattern)));
MONO_OBJECT_SETREF (datetime, LongDatePattern, mono_string_new (domain, idx2string (dfe->long_date_pattern)));
MONO_OBJECT_SETREF (datetime, LongTimePattern, mono_string_new (domain, idx2string (dfe->long_time_pattern)));
MONO_OBJECT_SETREF (datetime, MonthDayPattern, mono_string_new (domain, idx2string (dfe->month_day_pattern)));
MONO_OBJECT_SETREF (datetime, ShortTimePattern, mono_string_new (domain, idx2string (dfe->short_time_pattern)));
MONO_OBJECT_SETREF (datetime, TimeSeparator, mono_string_new (domain, idx2string (dfe->time_separator)));
MONO_OBJECT_SETREF (datetime, YearMonthPattern, mono_string_new (domain, idx2string (dfe->year_month_pattern)));
- MONO_OBJECT_SETREF (datetime, ShortDatePatterns, create_names_array_idx (dfe->short_date_patterns,
+ MONO_OBJECT_SETREF (datetime, ShortDatePatterns, create_names_array_idx_dynamic (dfe->short_date_patterns,
NUM_SHORT_DATE_PATTERNS));
- MONO_OBJECT_SETREF (datetime, LongDatePatterns, create_names_array_idx (dfe->long_date_patterns,
+ MONO_OBJECT_SETREF (datetime, LongDatePatterns, create_names_array_idx_dynamic (dfe->long_date_patterns,
NUM_LONG_DATE_PATTERNS));
- MONO_OBJECT_SETREF (datetime, ShortTimePatterns, create_names_array_idx (dfe->short_time_patterns,
+ MONO_OBJECT_SETREF (datetime, ShortTimePatterns, create_names_array_idx_dynamic (dfe->short_time_patterns,
NUM_SHORT_TIME_PATTERNS));
- MONO_OBJECT_SETREF (datetime, LongTimePatterns, create_names_array_idx (dfe->long_time_patterns,
+ MONO_OBJECT_SETREF (datetime, LongTimePatterns, create_names_array_idx_dynamic (dfe->long_time_patterns,
NUM_LONG_TIME_PATTERNS));
-
+ MONO_OBJECT_SETREF (datetime, GenitiveMonthNames, create_names_array_idx (dfe->month_genitive_names, NUM_MONTHS));
+ MONO_OBJECT_SETREF (datetime, GenitiveAbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_genitive_names, NUM_MONTHS));
}
void
MONO_ARCH_SAVE_REGS;
g_assert (this->number_format != 0);
+ if (this->number_index < 0)
+ return;
number = this->number_format;
nfe = &number_format_entries [this->number_index];
domain = mono_domain_get ();
+ number->readOnly = this->is_read_only;
number->currencyDecimalDigits = nfe->currency_decimal_digits;
MONO_OBJECT_SETREF (number, currencyDecimalSeparator, mono_string_new (domain,
idx2string (nfe->currency_decimal_separator)));
this->lcid = ci->lcid;
MONO_OBJECT_SETREF (this, name, mono_string_new (domain, idx2string (ci->name)));
- MONO_OBJECT_SETREF (this, icu_name, mono_string_new (domain, idx2string (ci->icu_name)));
- MONO_OBJECT_SETREF (this, displayname, mono_string_new (domain, idx2string (ci->displayname)));
MONO_OBJECT_SETREF (this, englishname, mono_string_new (domain, idx2string (ci->englishname)));
MONO_OBJECT_SETREF (this, nativename, mono_string_new (domain, idx2string (ci->nativename)));
MONO_OBJECT_SETREF (this, win3lang, mono_string_new (domain, idx2string (ci->win3lang)));
MONO_OBJECT_SETREF (this, iso3lang, mono_string_new (domain, idx2string (ci->iso3lang)));
MONO_OBJECT_SETREF (this, iso2lang, mono_string_new (domain, idx2string (ci->iso2lang)));
+
+ // It's null for neutral cultures
+ if (ci->territory > 0)
+ MONO_OBJECT_SETREF (this, territory, mono_string_new (domain, idx2string (ci->territory)));
+ MONO_OBJECT_SETREF (this, native_calendar_names, create_names_array_idx (ci->native_calendar_names, NUM_CALENDARS));
this->parent_lcid = ci->parent_lcid;
- this->specific_lcid = ci->specific_lcid;
this->datetime_index = ci->datetime_format_index;
this->number_index = ci->number_format_index;
- this->calendar_data = ci->calendar_data;
+ this->calendar_type = ci->calendar_type;
this->text_info_data = &ci->text_info;
return TRUE;
{
MonoDomain *domain = mono_domain_get ();
- this->region_id = ri->region_id;
+ this->geo_id = ri->geo_id;
MONO_OBJECT_SETREF (this, iso2name, mono_string_new (domain, idx2string (ri->iso2name)));
MONO_OBJECT_SETREF (this, iso3name, mono_string_new (domain, idx2string (ri->iso3name)));
MONO_OBJECT_SETREF (this, win3name, mono_string_new (domain, idx2string (ri->win3name)));
MONO_OBJECT_SETREF (this, english_name, mono_string_new (domain, idx2string (ri->english_name)));
+ MONO_OBJECT_SETREF (this, native_name, mono_string_new (domain, idx2string (ri->native_name)));
MONO_OBJECT_SETREF (this, currency_symbol, mono_string_new (domain, idx2string (ri->currency_symbol)));
MONO_OBJECT_SETREF (this, iso_currency_symbol, mono_string_new (domain, idx2string (ri->iso_currency_symbol)));
MONO_OBJECT_SETREF (this, currency_english_name, mono_string_new (domain, idx2string (ri->currency_english_name)));
+ MONO_OBJECT_SETREF (this, currency_native_name, mono_string_new (domain, idx2string (ri->currency_native_name)));
return TRUE;
}
-static gboolean
-construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name)
-{
- const CultureInfoEntry *entry;
- const CultureInfoNameEntry *ne;
-
- MONO_ARCH_SAVE_REGS;
-
- ne = bsearch (name, culture_name_entries, NUM_CULTURE_ENTRIES,
- sizeof (CultureInfoNameEntry), culture_name_locator);
-
- if (ne == NULL)
- return FALSE;
-
- entry = &culture_entries [ne->culture_entry_index];
-
- /* try avoiding another lookup, often the culture is its own specific culture */
- if (entry->lcid != entry->specific_lcid)
- entry = culture_info_entry_from_lcid (entry->specific_lcid);
-
- return construct_culture (ci, entry);
-}
-
static const CultureInfoEntry*
culture_info_entry_from_lcid (int lcid)
{
const CultureInfoEntry *ci;
- CultureInfoEntry key;
- key.lcid = lcid;
- ci = bsearch (&key, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
+ ci = mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
return ci;
}
MONO_ARCH_SAVE_REGS;
- ne = bsearch (&lcid, culture_entries, NUM_CULTURE_ENTRIES,
- sizeof (CultureInfoEntry), region_lcid_locator);
+ ne = mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
if (ne == NULL)
return FALSE;
return entry;
}
-/*
- * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu)
- * Copyright (c) 1995-2003 International Business Machines Corporation and others
- * All rights reserved.
- */
+#if defined (__APPLE__)
static gchar*
-get_posix_locale (void)
-{
- const gchar* posix_locale = NULL;
+get_darwin_locale (void)
+{
+ static gchar *darwin_locale = NULL;
+ CFLocaleRef locale = NULL;
+ CFStringRef locale_language = NULL;
+ CFStringRef locale_country = NULL;
+ CFStringRef locale_script = NULL;
+ CFStringRef locale_cfstr = NULL;
+ CFIndex bytes_converted;
+ CFIndex bytes_written;
+ CFIndex len;
+ int i;
+
+ if (darwin_locale != NULL)
+ return g_strdup (darwin_locale);
+
+ locale = CFLocaleCopyCurrent ();
+
+ if (locale) {
+ locale_language = CFLocaleGetValue (locale, kCFLocaleLanguageCode);
+ if (locale_language != NULL && CFStringGetBytes(locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
+ len = bytes_converted + 1;
+
+ locale_country = CFLocaleGetValue (locale, kCFLocaleCountryCode);
+ if (locale_country != NULL && CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
+ len += bytes_converted + 1;
+
+ locale_script = CFLocaleGetValue (locale, kCFLocaleScriptCode);
+ if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
+ len += bytes_converted + 1;
+ }
+
+ darwin_locale = (char *) malloc (len + 1);
+ CFStringGetBytes (locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) darwin_locale, len, &bytes_converted);
- posix_locale = g_getenv("LC_ALL");
- if (posix_locale == 0) {
- posix_locale = g_getenv("LANG");
- if (posix_locale == 0) {
- posix_locale = setlocale(LC_ALL, NULL);
+ darwin_locale[bytes_converted] = '-';
+ bytes_written = bytes_converted + 1;
+ if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted) > 0) {
+ darwin_locale[bytes_written + bytes_converted] = '-';
+ bytes_written += bytes_converted + 1;
+ }
+
+ CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted);
+ darwin_locale[bytes_written + bytes_converted] = '\0';
+ }
}
- }
- if (posix_locale == NULL)
- return NULL;
+ if (darwin_locale == NULL) {
+ locale_cfstr = CFLocaleGetIdentifier (locale);
+
+ if (locale_cfstr) {
+ len = CFStringGetMaximumSizeForEncoding (CFStringGetLength (locale_cfstr), kCFStringEncodingMacRoman) + 1;
+ darwin_locale = (char *) malloc (len);
+ if (!CFStringGetCString (locale_cfstr, darwin_locale, len, kCFStringEncodingMacRoman)) {
+ free (darwin_locale);
+ CFRelease (locale);
+ darwin_locale = NULL;
+ return NULL;
+ }
- if ((strcmp ("C", posix_locale) == 0) || (strchr (posix_locale, ' ') != NULL)
- || (strchr (posix_locale, '/') != NULL)) {
- /*
- * HPUX returns 'C C C C C C C'
- * Solaris can return /en_US/C/C/C/C/C on the second try.
- * Maybe we got some garbage.
- */
- return NULL;
+ for (i = 0; i < strlen (darwin_locale); i++)
+ if (darwin_locale [i] == '_')
+ darwin_locale [i] = '-';
+ }
+ }
+
+ CFRelease (locale);
}
- return g_strdup (posix_locale);
+ return g_strdup (darwin_locale);
}
+#endif
-static gchar*
-get_current_locale_name (void)
+static char *
+get_posix_locale (void)
{
- gchar *locale;
- gchar *corrected = NULL;
- const gchar *p;
- gchar *c;
-
-#ifdef PLATFORM_WIN32
- locale = g_win32_getlocale ();
-#else
- locale = get_posix_locale ();
-#endif
+ const char *locale;
+ locale = g_getenv ("LC_ALL");
+ if (locale == NULL) {
+ locale = g_getenv ("LANG");
+ if (locale == NULL)
+ locale = setlocale (LC_ALL, NULL);
+ }
if (locale == NULL)
return NULL;
- if ((p = strchr (locale, '.')) != NULL) {
- /* assume new locale can't be larger than old one? */
- corrected = malloc (strlen (locale));
- strncpy (corrected, locale, p - locale);
- corrected [p - locale] = 0;
-
- /* do not copy after the @ */
- if ((p = strchr (corrected, '@')) != NULL)
- corrected [p - corrected] = 0;
- }
-
- /* Note that we scan the *uncorrected* ID. */
- if ((p = strrchr (locale, '@')) != NULL) {
+ /* Skip English-only locale 'C' */
+ if (strcmp (locale, "C") == 0)
+ return NULL;
- /*
- * In Mono we dont handle the '@' modifier because we do
- * not have any cultures that use it. We just trim it
- * off of the end of the name.
- */
+ return g_strdup (locale);
+}
- if (corrected == NULL) {
- corrected = malloc (strlen (locale));
- strncpy (corrected, locale, p - locale);
- corrected [p - locale] = 0;
- }
- }
- if (corrected == NULL)
- corrected = locale;
- else
- g_free (locale);
+static gchar *
+get_current_locale_name (void)
+{
+ char *locale;
+ char *p, *ret;
+
+#ifdef HOST_WIN32
+ locale = g_win32_getlocale ();
+#elif defined (__APPLE__)
+ locale = get_darwin_locale ();
+ if (!locale)
+ locale = get_posix_locale ();
+#else
+ locale = get_posix_locale ();
+#endif
- if ((c = strchr (corrected, '_')) != NULL)
- *c = '-';
+ if (locale == NULL)
+ return NULL;
- g_strdown (corrected);
+ p = strchr (locale, '.');
+ if (p != NULL)
+ *p = 0;
+ p = strchr (locale, '@');
+ if (p != NULL)
+ *p = 0;
+ p = strchr (locale, '_');
+ if (p != NULL)
+ *p = '-';
+
+ ret = g_ascii_strdown (locale, -1);
+ g_free (locale);
- return corrected;
-}
+ return ret;
+}
-MonoBoolean
-ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (MonoCultureInfo *ci)
+MonoString*
+ves_icall_System_Globalization_CultureInfo_get_current_locale_name (void)
{
gchar *locale;
- gboolean ret;
+ MonoString* ret;
+ MonoDomain *domain;
MONO_ARCH_SAVE_REGS;
locale = get_current_locale_name ();
if (locale == NULL)
- return FALSE;
+ return NULL;
- ret = construct_culture_from_specific_name (ci, locale);
+ domain = mono_domain_get ();
+ ret = mono_string_new (domain, locale);
g_free (locale);
return ret;
MONO_ARCH_SAVE_REGS;
n = mono_string_to_utf8 (name);
- ne = bsearch (n, culture_name_entries, NUM_CULTURE_ENTRIES,
+ ne = mono_binary_search (n, culture_name_entries, NUM_CULTURE_ENTRIES,
sizeof (CultureInfoNameEntry), culture_name_locator);
if (ne == NULL) {
return construct_culture (this, &culture_entries [ne->culture_entry_index]);
}
-
+/*
MonoBoolean
ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
MonoString *name)
return ret;
}
-
+*/
MonoBoolean
ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this,
gint lcid)
MONO_ARCH_SAVE_REGS;
n = mono_string_to_utf8 (name);
- ne = bsearch (n, region_name_entries, NUM_REGION_ENTRIES,
+ ne = mono_binary_search (n, region_name_entries, NUM_REGION_ENTRIES,
sizeof (RegionInfoNameEntry), region_name_locator);
if (ne == NULL) {
len = 0;
for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
ci = &culture_entries [i];
- is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
+ is_neutral = ci->territory == 0;
if ((neutral && is_neutral) || (specific && !is_neutral))
len++;
}
for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
ci = &culture_entries [i];
- is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
+ is_neutral = ci->territory == 0;
if ((neutral && is_neutral) || (specific && !is_neutral)) {
culture = (MonoCultureInfo *) mono_object_new (domain, class);
mono_runtime_object_init ((MonoObject *) culture);
construct_culture (culture, ci);
+ culture->use_user_override = TRUE;
mono_array_setref (ret, len++, culture);
}
}
return ret;
}
-/**
- * ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral:
- *
- * Set is_neutral and return TRUE if the culture is found. If it is not found return FALSE.
- */
-MonoBoolean
-ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral (gint lcid, MonoBoolean *is_neutral)
-{
- const CultureInfoEntry *entry;
-
- MONO_ARCH_SAVE_REGS;
-
- entry = culture_info_entry_from_lcid (lcid);
-
- if (entry == NULL)
- return FALSE;
-
- *is_neutral = (entry->specific_lcid == 0);
-
- return TRUE;
-}
-
void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
{
/* Nothing to do here */
gint32 options)
{
gint32 result;
- GUnicodeType c1type, c2type;
/* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
if (options & CompareOptions_Ordinal)
return (gint32) c1 - c2;
- c1type = g_unichar_type (c1);
- c2type = g_unichar_type (c2);
-
if (options & CompareOptions_IgnoreCase) {
+ GUnicodeType c1type, c2type;
+
+ c1type = g_unichar_type (c1);
+ c2type = g_unichar_type (c2);
+
result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
(c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
} else {
guint8 **argMapIdxToComposite,
guint8 **argCombiningClass)
{
+#ifdef DISABLE_NORMALIZATION
+ mono_raise_exception (mono_get_exception_not_supported ("This runtime has been compiled without string normalization support."));
+#else
*argProps = (guint8*)props;
*argMappedChars = (guint8*) mappedChars;
*argCharMapIndex = (guint8*) charMapIndex;
*argHelperIndex = (guint8*) helperIndex;
*argMapIdxToComposite = (guint8*) mapIdxToComposite;
*argCombiningClass = (guint8*)combiningClass;
+#endif
}