[444] | 1 | /* |
---|
| 2 | FUNCTION |
---|
| 3 | <<newlocale>>---create or modify a locale object |
---|
| 4 | |
---|
| 5 | INDEX |
---|
| 6 | newlocale |
---|
| 7 | |
---|
| 8 | INDEX |
---|
| 9 | _newlocale_r |
---|
| 10 | |
---|
| 11 | SYNOPSIS |
---|
| 12 | #include <locale.h> |
---|
| 13 | locale_t newlocale(int <[category_mask]>, const char *<[locale]>, |
---|
| 14 | locale_t <[locobj]>); |
---|
| 15 | |
---|
| 16 | locale_t _newlocale_r(void *<[reent]>, int <[category_mask]>, |
---|
| 17 | const char *<[locale]>, locale_t <[locobj]>); |
---|
| 18 | |
---|
| 19 | DESCRIPTION |
---|
| 20 | The <<newlocale>> function shall create a new locale object or modify an |
---|
| 21 | existing one. If the base argument is (locale_t) <<0>>, a new locale |
---|
| 22 | object shall be created. It is unspecified whether the locale object |
---|
| 23 | pointed to by base shall be modified, or freed and a new locale object |
---|
| 24 | created. |
---|
| 25 | |
---|
| 26 | The category_mask argument specifies the locale categories to be set or |
---|
| 27 | modified. Values for category_mask shall be constructed by a |
---|
| 28 | bitwise-inclusive OR of the symbolic constants LC_CTYPE_MASK, |
---|
| 29 | LC_NUMERIC_MASK, LC_TIME_MASK, LC_COLLATE_MASK, LC_MONETARY_MASK, and |
---|
| 30 | LC_MESSAGES_MASK, or any of the other implementation-defined LC_*_MASK |
---|
| 31 | values defined in <locale.h>. |
---|
| 32 | |
---|
| 33 | For each category with the corresponding bit set in category_mask the |
---|
| 34 | data from the locale named by locale shall be used. In the case of |
---|
| 35 | modifying an existing locale object, the data from the locale named by |
---|
| 36 | locale shall replace the existing data within the locale object. If a |
---|
| 37 | completely new locale object is created, the data for all sections not |
---|
| 38 | requested by category_mask shall be taken from the default locale. |
---|
| 39 | |
---|
| 40 | The following preset values of locale are defined for all settings of |
---|
| 41 | category_mask: |
---|
| 42 | |
---|
| 43 | "POSIX" Specifies the minimal environment for C-language translation |
---|
| 44 | called the POSIX locale. |
---|
| 45 | |
---|
| 46 | "C" Equivalent to "POSIX". |
---|
| 47 | |
---|
| 48 | "" Specifies an implementation-defined native environment. This |
---|
| 49 | corresponds to the value of the associated environment variables, |
---|
| 50 | LC_* and LANG; see the Base Definitions volume of POSIX.1‐2008, |
---|
| 51 | Chapter 7, Locale and Chapter 8, Environment Variables. |
---|
| 52 | |
---|
| 53 | If the base argument is not (locale_t) <<0>> and the <<newlocale>> |
---|
| 54 | function call succeeds, the contents of base are unspecified. |
---|
| 55 | Applications shall ensure that they stop using base as a locale object |
---|
| 56 | before calling <<newlocale>>. If the function call fails and the base |
---|
| 57 | argument is not (locale_t) <<0>>, the contents of base shall remain |
---|
| 58 | valid and unchanged. |
---|
| 59 | |
---|
| 60 | The behavior is undefined if the base argument is the special locale |
---|
| 61 | object LC_GLOBAL_LOCALE, or is not a valid locale object handle and is |
---|
| 62 | not (locale_t) <<0>>. |
---|
| 63 | |
---|
| 64 | RETURNS |
---|
| 65 | Upon successful completion, the <<newlocale>> function shall return a |
---|
| 66 | handle which the caller may use on subsequent calls to <<duplocale>>, |
---|
| 67 | <<freelocale>>, and other functions taking a locale_t argument. |
---|
| 68 | |
---|
| 69 | Upon failure, the <<newlocale>> function shall return (locale_t) <<0>> |
---|
| 70 | and set errno to indicate the error. |
---|
| 71 | |
---|
| 72 | PORTABILITY |
---|
| 73 | <<newlocale>> is POSIX-1.2008. |
---|
| 74 | */ |
---|
| 75 | |
---|
| 76 | #include <newlib.h> |
---|
| 77 | #include <errno.h> |
---|
| 78 | #include <reent.h> |
---|
| 79 | #include <stdlib.h> |
---|
| 80 | #include "setlocale.h" |
---|
| 81 | |
---|
| 82 | #define LC_VALID_MASK (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MONETARY_MASK \ |
---|
| 83 | | LC_NUMERIC_MASK | LC_TIME_MASK | LC_MESSAGES_MASK) |
---|
| 84 | |
---|
| 85 | struct __locale_t * |
---|
| 86 | _newlocale_r (struct _reent *p, int category_mask, const char *locale, |
---|
| 87 | struct __locale_t *base) |
---|
| 88 | { |
---|
| 89 | #ifndef _MB_CAPABLE |
---|
| 90 | return __get_C_locale (); |
---|
| 91 | #else /* _MB_CAPABLE */ |
---|
| 92 | char new_categories[_LC_LAST][ENCODING_LEN + 1]; |
---|
| 93 | struct __locale_t tmp_locale, *new_locale; |
---|
| 94 | int i; |
---|
| 95 | |
---|
| 96 | /* Convert LC_ALL_MASK to a mask containing all valid MASK values. |
---|
| 97 | This simplifies the code below. */ |
---|
| 98 | if (category_mask & LC_ALL_MASK) |
---|
| 99 | { |
---|
| 100 | category_mask &= ~LC_ALL_MASK; |
---|
| 101 | category_mask |= LC_VALID_MASK; |
---|
| 102 | } |
---|
| 103 | /* Check for invalid mask values and valid locale ptr. */ |
---|
| 104 | if ((category_mask & ~LC_VALID_MASK) || !locale) |
---|
| 105 | { |
---|
| 106 | p->_errno = EINVAL; |
---|
| 107 | return NULL; |
---|
| 108 | } |
---|
| 109 | /* If the new locale is supposed to be all default locale, just return |
---|
| 110 | a pointer to the default locale. */ |
---|
| 111 | if ((!base && category_mask == 0) |
---|
| 112 | || (category_mask == LC_VALID_MASK |
---|
| 113 | && (!strcmp (locale, "C") || !strcmp (locale, "POSIX")))) |
---|
| 114 | return __get_C_locale (); |
---|
| 115 | /* Start with setting all values to the default locale values. */ |
---|
| 116 | tmp_locale = *__get_C_locale (); |
---|
| 117 | /* Fill out new category strings. */ |
---|
| 118 | for (i = 1; i < _LC_LAST; ++i) |
---|
| 119 | { |
---|
| 120 | if (((1 << i) & category_mask) != 0) |
---|
| 121 | { |
---|
| 122 | /* If locale is "", fetch from environment. Otherwise use locale |
---|
| 123 | name verbatim. */ |
---|
| 124 | const char *cat = (locale[0] == '\0') ? __get_locale_env (p, i) |
---|
| 125 | : locale; |
---|
| 126 | if (strlen (cat) > ENCODING_LEN) |
---|
| 127 | { |
---|
| 128 | p->_errno = EINVAL; |
---|
| 129 | return NULL; |
---|
| 130 | } |
---|
| 131 | strcpy (new_categories[i], cat); |
---|
| 132 | } |
---|
| 133 | else |
---|
| 134 | strcpy (new_categories[i], base ? base->categories[i] : "C"); |
---|
| 135 | } |
---|
| 136 | /* Now go over all categories and set them. */ |
---|
| 137 | for (i = 1; i < _LC_LAST; ++i) |
---|
| 138 | { |
---|
| 139 | /* If we have a base locale, and the category is not in category_mask |
---|
| 140 | or the new category is the base categroy, just copy over. */ |
---|
| 141 | if (base && (((1 << i) & category_mask) == 0 |
---|
| 142 | || !strcmp (base->categories[i], new_categories[i]))) |
---|
| 143 | { |
---|
| 144 | strcpy (tmp_locale.categories[i], new_categories[i]); |
---|
| 145 | if (i == LC_CTYPE) |
---|
| 146 | { |
---|
| 147 | tmp_locale.wctomb = base->wctomb; |
---|
| 148 | tmp_locale.mbtowc = base->mbtowc; |
---|
| 149 | tmp_locale.cjk_lang = base->cjk_lang; |
---|
| 150 | tmp_locale.ctype_ptr - base->ctype_ptr; |
---|
| 151 | } |
---|
| 152 | #ifdef __HAVE_LOCALE_INFO__ |
---|
| 153 | /* Mark the values as "has still to be copied". We do this in |
---|
| 154 | two steps to simplify freeing new locale types in case of a |
---|
| 155 | subsequent error. */ |
---|
| 156 | tmp_locale.lc_cat[i].ptr = base->lc_cat[i].ptr; |
---|
| 157 | tmp_locale.lc_cat[i].buf = (void *) -1; |
---|
| 158 | #else /* !__HAVE_LOCALE_INFO__ */ |
---|
| 159 | if (i == LC_CTYPE) |
---|
| 160 | strcpy (tmp_locale.ctype_codeset, base->ctype_codeset); |
---|
| 161 | else if (i == LC_MESSAGES) |
---|
| 162 | strcpy (tmp_locale.message_codeset, base->message_codeset); |
---|
| 163 | #endif /* !__HAVE_LOCALE_INFO__ */ |
---|
| 164 | } |
---|
| 165 | /* Otherwise, if the category is in category_mask, create entry. */ |
---|
| 166 | else if (((1 << i) & category_mask) != 0) |
---|
| 167 | { |
---|
| 168 | /* Nothing to do for "C"/"POSIX" locale. */ |
---|
| 169 | if (!strcmp (new_categories[i], "C") |
---|
| 170 | || !strcmp (new_categories[i], "POSIX")) |
---|
| 171 | continue; |
---|
| 172 | /* Otherwise load locale data. */ |
---|
| 173 | else if (!__loadlocale (&tmp_locale, i, new_categories[i])) |
---|
| 174 | goto error; |
---|
| 175 | } |
---|
| 176 | } |
---|
| 177 | /* Allocate new locale_t. */ |
---|
| 178 | new_locale = (struct __locale_t *) _calloc_r (p, 1, sizeof *new_locale); |
---|
| 179 | if (!new_locale) |
---|
| 180 | goto error; |
---|
| 181 | if (base) |
---|
| 182 | { |
---|
| 183 | #ifdef __HAVE_LOCALE_INFO__ |
---|
| 184 | /* Step 2 of copying over.. Make sure to invalidate the copied buffer |
---|
| 185 | pointers in base, so the subsequent _freelocale_r (base) doesn't free |
---|
| 186 | the buffers now used in the new locale. */ |
---|
| 187 | for (i = 1; i < _LC_LAST; ++i) |
---|
| 188 | if (tmp_locale.lc_cat[i].buf == (const void *) -1) |
---|
| 189 | { |
---|
| 190 | tmp_locale.lc_cat[i].buf = base->lc_cat[i].buf; |
---|
| 191 | base->lc_cat[i].ptr = base->lc_cat[i].buf = NULL; |
---|
| 192 | } |
---|
| 193 | #endif /* __HAVE_LOCALE_INFO__ */ |
---|
| 194 | _freelocale_r (p, base); |
---|
| 195 | } |
---|
| 196 | |
---|
| 197 | *new_locale = tmp_locale; |
---|
| 198 | return new_locale; |
---|
| 199 | |
---|
| 200 | error: |
---|
| 201 | /* An error occured while we had already (potentially) allocated memory. |
---|
| 202 | Free memory and return NULL. errno is supposed to be set already. */ |
---|
| 203 | #ifdef __HAVE_LOCALE_INFO__ |
---|
| 204 | for (i = 1; i < _LC_LAST; ++i) |
---|
| 205 | if (((1 << i) & category_mask) != 0 |
---|
| 206 | && tmp_locale.lc_cat[i].buf |
---|
| 207 | && tmp_locale.lc_cat[i].buf != (const void *) -1) |
---|
| 208 | { |
---|
| 209 | _free_r (p, (void *) tmp_locale.lc_cat[i].ptr); |
---|
| 210 | _free_r (p, tmp_locale.lc_cat[i].buf); |
---|
| 211 | } |
---|
| 212 | #endif /* __HAVE_LOCALE_INFO__ */ |
---|
| 213 | |
---|
| 214 | return NULL; |
---|
| 215 | #endif /* _MB_CAPABLE */ |
---|
| 216 | } |
---|
| 217 | |
---|
| 218 | struct __locale_t * |
---|
| 219 | newlocale (int category_mask, const char *locale, struct __locale_t *base) |
---|
| 220 | { |
---|
| 221 | return _newlocale_r (_REENT, category_mask, locale, base); |
---|
| 222 | } |
---|