diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2016-07-22 23:54:07 +0300 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2016-08-15 11:56:57 +0300 |
commit | aefd8b5b518b958f64506a6f74aeffb4f47bde8a (patch) | |
tree | 2eb483eb3ab883d1426e7783d8eb322bb66c7952 /newlib/libc/locale | |
parent | d4f6cae9cd526290bfa2a0be7d3dc9fd20f31dba (diff) |
Implement newlocale, freelocale, duplocale, uselocale
Add global const __C_locale for reference purposes.
Bump Cygwin API minor number and DLL major version number to 2.6.0.
Signed-off by: Corinna Vinschen <corinna@vinschen.de>
Diffstat (limited to 'newlib/libc/locale')
-rw-r--r-- | newlib/libc/locale/locale.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/newlib/libc/locale/locale.c b/newlib/libc/locale/locale.c index 35c5e6c6f..4f2d6d271 100644 --- a/newlib/libc/locale/locale.c +++ b/newlib/libc/locale/locale.c @@ -226,6 +226,34 @@ static char *categories[_LC_LAST] = { */ char __default_locale[ENCODING_LEN + 1] = DEFAULT_LOCALE; +const struct __locale_t __C_locale = +{ + { "C", "C", "C", "C", "C", "C", "C", }, + __ascii_wctomb, + __ascii_mbtowc, + 0, + NULL, +#ifndef __HAVE_LOCALE_INFO__ + "\1", + "ASCII", + "ASCII", +#else + { + { NULL, NULL }, /* LC_ALL */ +#ifdef __CYGWIN__ + { &_C_collate_locale, NULL }, /* LC_COLLATE */ +#else + { NULL, NULL }, /* LC_COLLATE */ +#endif + { &_C_ctype_locale, NULL }, /* LC_CTYPE */ + { &_C_monetary_locale, NULL }, /* LC_MONETARY */ + { &_C_numeric_locale, NULL }, /* LC_NUMERIC */ + { &_C_time_locale, NULL }, /* LC_TIME */ + { &_C_messages_locale, NULL }, /* LC_MESSAGES */ + }, +#endif +}; + struct __locale_t __global_locale = { { "C", "C", DEFAULT_LOCALE, "C", "C", "C", "C", }, @@ -1008,6 +1036,205 @@ _DEFUN (_localeconv_r, (data), return (struct lconv *) &lconv; } +#define LC_VALID_MASK (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MONETARY_MASK \ + | LC_NUMERIC_MASK | LC_TIME_MASK | LC_MESSAGES_MASK) + +struct __locale_t * +_newlocale_r (struct _reent *p, int category_mask, const char *locale, + struct __locale_t *base) +{ + struct __locale_t tmp_locale, *new_locale; + int i; + + /* Convert LC_ALL_MASK to a mask containing all valid MASK values. + This simplifies the code below. */ + if (category_mask & LC_ALL_MASK) + { + category_mask &= ~LC_ALL_MASK; + category_mask |= LC_VALID_MASK; + } + /* Check for invalid mask values and valid locale ptr. */ + if (category_mask & ~LC_VALID_MASK || !locale) + { + p->_errno = EINVAL; + return NULL; + } + /* If the new locale is supposed to be all default locale, just return + a pointer to the default locale. */ + if ((!base && category_mask == 0) + || (category_mask == LC_VALID_MASK + && (!strcmp (locale, "C") || !strcmp (locale, "POSIX")))) + return (struct __locale_t *) &__C_locale; + /* Start with setting all values to the default locale values. */ + tmp_locale = __C_locale; + /* Fill out category strings. */ + if (!*locale) + { + for (i = 1; i < _LC_LAST; ++i) + if (((1 << i) & category_mask) != 0) + { + const char *env = __get_locale_env (p, i); + if (strlen (env) > ENCODING_LEN) + { + p->_errno = EINVAL; + return NULL; + } + strcpy (tmp_locale.categories[i], env); + } + } + else + { + for (i = 1; i < _LC_LAST; ++i) + if (((1 << i) & category_mask) != 0) + strcpy (tmp_locale.categories[i], locale); + } + /* Now go over all categories and set them. */ + for (i = 1; i < _LC_LAST; ++i) + { + if (((1 << i) & category_mask) != 0) + { + /* Nothing to do for "C"/"POSIX" locale. */ + if (!strcmp (tmp_locale.categories[i], "C") + || !strcmp (tmp_locale.categories[i], "POSIX")) + continue; + /* If the new locale is the old locale, just copy it over. */ + if (base && !strcmp (base->categories[i], tmp_locale.categories[i])) + { + if (i == LC_CTYPE) + { + tmp_locale.wctomb = base->wctomb; + tmp_locale.mbtowc = base->mbtowc; + tmp_locale.cjk_lang = base->cjk_lang; + tmp_locale.ctype_ptr - base->ctype_ptr; + } +#ifdef __HAVE_LOCALE_INFO__ + tmp_locale.lc_cat[i].ptr = base->lc_cat[i].ptr; + /* Mark the value as "has still to be copied". We do this in + two steps to simplify freeing new locale types in case of a + subsequent error. */ + tmp_locale.lc_cat[i].buf = (void *) -1; +#else + if (i == LC_CTYPE) + strcpy (tmp_locale.ctype_codeset, base->ctype_codeset); + else if (i == LC_MESSAGES) + strcpy (tmp_locale.message_codeset, base->message_codeset); +#endif + continue; + } + /* Otherwise load locale data. */ + if (!loadlocale (&tmp_locale, i, tmp_locale.categories[i])) + goto error; + } + } + /* Allocate new locale_t. */ + new_locale = (struct __locale_t *) _calloc_r (p, 1, sizeof *new_locale); + if (!new_locale) + goto error; +#ifdef __HAVE_LOCALE_INFO__ + /* Second step of copying over. At this point we can safely copy. Make + sure to invalidate the copied buffer pointers in base, so a subsequent + freelocale (base) doesn't free the buffers now used in the new locale. */ + for (i = 1; i < _LC_LAST; ++i) + if (tmp_locale.lc_cat[i].buf == (const void *) -1) + { + tmp_locale.lc_cat[i].buf = base->lc_cat[i].buf; + base->lc_cat[i].buf = NULL; + } +#endif + + *new_locale = tmp_locale; + return new_locale; + +error: + /* An error occured while we had already (potentially) allocated memory. + Free memory and return NULL. errno is supposed to be set already. */ +#ifdef __HAVE_LOCALE_INFO__ + for (i = 1; i < _LC_LAST; ++i) + if (tmp_locale.lc_cat[i].buf + && tmp_locale.lc_cat[i].buf != (const void *) -1) + _free_r (p, tmp_locale.lc_cat[i].buf); +#endif + + return NULL; +} + +void +_freelocale_r (struct _reent *p, struct __locale_t *locobj) +{ + /* Sanity check. The "C" locale is static, don't try to free it. */ + if (!locobj || locobj == &__C_locale || locobj == LC_GLOBAL_LOCALE) + return; +#ifdef __HAVE_LOCALE_INFO__ + for (int i = 1; i < _LC_LAST; ++i) + if (locobj->lc_cat[i].buf) + _free_r (p, locobj->lc_cat[i].buf); +#endif + _free_r (p, locobj); +} + +struct __locale_t * +_duplocale_r (struct _reent *p, struct __locale_t *locobj) +{ + struct __locale_t tmp_locale, *new_locale; + int i; + + /* LC_GLOBAL_LOCALE denotes the global locale. */ + if (locobj == LC_GLOBAL_LOCALE) + locobj = __get_global_locale (); + /* The "C" locale is used statically, never copied. */ + else if (locobj == &__C_locale) + return (struct __locale_t *) &__C_locale; + /* Copy locale content. */ + tmp_locale = *locobj; +#ifdef __HAVE_LOCALE_INFO__ + for (i = 1; i < _LC_LAST; ++i) + if (locobj->lc_cat[i].buf) + { + /* If the object is not a "C" locale category, copy it. Just call + loadlocale. It knows what to do to replicate the category. */ + tmp_locale.lc_cat[i].ptr = NULL; + tmp_locale.lc_cat[i].buf = NULL; + if (!loadlocale (&tmp_locale, i, tmp_locale.categories[i])) + goto error; + } +#endif + /* Allocate new locale_t. */ + new_locale = (struct __locale_t *) _calloc_r (p, 1, sizeof *new_locale); + if (!new_locale) + goto error; + + *new_locale = tmp_locale; + return new_locale; + +error: + /* An error occured while we had already (potentially) allocated memory. + Free memory and return NULL. errno is supposed to be set already. */ +#ifdef __HAVE_LOCALE_INFO__ + while (--i > 0) + if (tmp_locale.lc_cat[i].buf) + _free_r (p, tmp_locale.lc_cat[i].buf); +#endif + + return NULL; +} + +struct __locale_t * +_uselocale_r (struct _reent *p, struct __locale_t *newloc) +{ + struct __locale_t *current_locale; + + current_locale = __get_locale_r (p); + if (!current_locale) + current_locale = LC_GLOBAL_LOCALE; + + if (newloc == LC_GLOBAL_LOCALE) + p->_locale = NULL; + else if (newloc) + p->_locale = newloc; + + return current_locale; +} + #ifndef _REENT_ONLY char * @@ -1024,4 +1251,27 @@ _DEFUN_VOID (localeconv) return _localeconv_r (_REENT); } +struct __locale_t * +newlocale (int category_mask, const char *locale, struct __locale_t *base) +{ + return _newlocale_r (_REENT, category_mask, locale, base); +} + +void +freelocale (struct __locale_t *locobj) +{ + _freelocale_r (_REENT, locobj); +} + +struct __locale_t * +duplocale (struct __locale_t *locobj) +{ + return _duplocale_r (_REENT, locobj); +} + +struct __locale_t * +uselocale (struct __locale_t *newloc) +{ + return _uselocale_r (_REENT, newloc); +} #endif |