Cygwin: mmap: use 64K pages for bookkeeping, second attempt
[newlib-cygwin.git] / newlib / libc / locale / newlocale.c
blob92f9e0a9c4e415073920e3488feb5de27865ceb3
1 /*
2 FUNCTION
3 <<newlocale>>---create or modify a locale object
5 INDEX
6 newlocale
8 INDEX
9 _newlocale_r
11 SYNOPSIS
12 #include <locale.h>
13 locale_t newlocale(int <[category_mask]>, const char *<[locale]>,
14 locale_t <[locobj]>);
16 locale_t _newlocale_r(void *<[reent]>, int <[category_mask]>,
17 const char *<[locale]>, locale_t <[locobj]>);
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.
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>.
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.
40 The following preset values of locale are defined for all settings of
41 category_mask:
43 "POSIX" Specifies the minimal environment for C-language translation
44 called the POSIX locale.
46 "C" Equivalent to "POSIX".
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.
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.
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>>.
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.
69 Upon failure, the <<newlocale>> function shall return (locale_t) <<0>>
70 and set errno to indicate the error.
72 PORTABILITY
73 <<newlocale>> is POSIX-1.2008.
76 #include <newlib.h>
77 #include <errno.h>
78 #include <reent.h>
79 #include <stdlib.h>
80 #include "setlocale.h"
82 #define LC_VALID_MASK (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MONETARY_MASK \
83 | LC_NUMERIC_MASK | LC_TIME_MASK | LC_MESSAGES_MASK)
85 struct __locale_t *
86 _newlocale_r (struct _reent *p, int category_mask, const char *locale,
87 struct __locale_t *base)
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;
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)
100 category_mask &= ~LC_ALL_MASK;
101 category_mask |= LC_VALID_MASK;
103 /* Check for invalid mask values and valid locale ptr. */
104 if ((category_mask & ~LC_VALID_MASK) || !locale)
106 _REENT_ERRNO(p) = EINVAL;
107 return NULL;
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)
120 if (((1 << i) & category_mask) != 0)
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)
128 _REENT_ERRNO(p) = EINVAL;
129 return NULL;
131 strcpy (new_categories[i], cat);
133 else
134 strcpy (new_categories[i], base ? base->categories[i] : "C");
136 /* Now go over all categories and set them. */
137 for (i = 1; i < _LC_LAST; ++i)
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])))
144 strcpy (tmp_locale.categories[i], new_categories[i]);
145 if (i == LC_CTYPE)
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;
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__ */
165 /* Otherwise, if the category is in category_mask, create entry. */
166 else if (((1 << i) & category_mask) != 0)
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]))
175 _REENT_ERRNO(p) = ENOENT;
176 goto error;
180 /* Allocate new locale_t. */
181 new_locale = (struct __locale_t *) _calloc_r (p, 1, sizeof *new_locale);
182 if (!new_locale)
183 goto error;
184 if (base)
186 #ifdef __HAVE_LOCALE_INFO__
187 /* Step 2 of copying over.. Make sure to invalidate the copied buffer
188 pointers in base, so the subsequent _freelocale_r (base) doesn't free
189 the buffers now used in the new locale. */
190 for (i = 1; i < _LC_LAST; ++i)
191 if (tmp_locale.lc_cat[i].buf == (const void *) -1)
193 tmp_locale.lc_cat[i].buf = base->lc_cat[i].buf;
194 if (base != __get_C_locale ())
195 base->lc_cat[i].ptr = base->lc_cat[i].buf = NULL;
197 #endif /* __HAVE_LOCALE_INFO__ */
198 _freelocale_r (p, base);
201 *new_locale = tmp_locale;
202 return new_locale;
204 error:
205 /* An error occured while we had already (potentially) allocated memory.
206 Free memory and return NULL. errno is supposed to be set already. */
207 #ifdef __HAVE_LOCALE_INFO__
208 for (i = 1; i < _LC_LAST; ++i)
209 if (((1 << i) & category_mask) != 0
210 && tmp_locale.lc_cat[i].buf
211 && tmp_locale.lc_cat[i].buf != (const void *) -1)
213 _free_r (p, (void *) tmp_locale.lc_cat[i].ptr);
214 _free_r (p, tmp_locale.lc_cat[i].buf);
216 #endif /* __HAVE_LOCALE_INFO__ */
218 return NULL;
219 #endif /* _MB_CAPABLE */
222 struct __locale_t *
223 newlocale (int category_mask, const char *locale, struct __locale_t *base)
225 return _newlocale_r (_REENT, category_mask, locale, base);