#include #include #include #include #include "setlocale.h" #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 } /* Otherwise load locale data. */ else 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, (void *) tmp_locale.lc_cat[i].ptr); _free_r (p, tmp_locale.lc_cat[i].buf); } #endif return NULL; } struct __locale_t * newlocale (int category_mask, const char *locale, struct __locale_t *base) { return _newlocale_r (_REENT, category_mask, locale, base); }