136 lines
4.1 KiB
C
136 lines
4.1 KiB
C
|
#include <newlib.h>
|
||
|
#include <errno.h>
|
||
|
#include <reent.h>
|
||
|
#include <stdlib.h>
|
||
|
#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);
|
||
|
}
|