1 /*
2 Copyright (c) 1996 - 2002 FreeBSD Project
3 Copyright (c) 1991, 1993
4 The Regents of the University of California. All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 4. Neither the name of the University nor the names of its contributors
15 may be used to endorse or promote products derived from this software
16 without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 SUCH DAMAGE.
29 */
30 /*
31 FUNCTION
32 <<newlocale>>---create or modify a locale object
33
34 INDEX
35 newlocale
36
37 INDEX
38 _newlocale_r
39
40 SYNOPSIS
41 #include <locale.h>
42 locale_t newlocale(int <[category_mask]>, const char *<[locale]>,
43 locale_t <[locobj]>);
44
45 locale_t _newlocale_r(void *<[reent]>, int <[category_mask]>,
46 const char *<[locale]>, locale_t <[locobj]>);
47
48 DESCRIPTION
49 The <<newlocale>> function shall create a new locale object or modify an
50 existing one. If the base argument is (locale_t) <<0>>, a new locale
51 object shall be created. It is unspecified whether the locale object
52 pointed to by base shall be modified, or freed and a new locale object
53 created.
54
55 The category_mask argument specifies the locale categories to be set or
56 modified. Values for category_mask shall be constructed by a
57 bitwise-inclusive OR of the symbolic constants LC_CTYPE_MASK,
58 LC_NUMERIC_MASK, LC_TIME_MASK, LC_COLLATE_MASK, LC_MONETARY_MASK, and
59 LC_MESSAGES_MASK, or any of the other implementation-defined LC_*_MASK
60 values defined in <locale.h>.
61
62 For each category with the corresponding bit set in category_mask the
63 data from the locale named by locale shall be used. In the case of
64 modifying an existing locale object, the data from the locale named by
65 locale shall replace the existing data within the locale object. If a
66 completely new locale object is created, the data for all sections not
67 requested by category_mask shall be taken from the default locale.
68
69 The following preset values of locale are defined for all settings of
70 category_mask:
71
72 "POSIX" Specifies the minimal environment for C-language translation
73 called the POSIX locale.
74
75 "C" Equivalent to "POSIX".
76
77 "" Specifies an implementation-defined native environment. This
78 corresponds to the value of the associated environment variables,
79 LC_* and LANG; see the Base Definitions volume of POSIX.1‐2008,
80 Chapter 7, Locale and Chapter 8, Environment Variables.
81
82 If the base argument is not (locale_t) <<0>> and the <<newlocale>>
83 function call succeeds, the contents of base are unspecified.
84 Applications shall ensure that they stop using base as a locale object
85 before calling <<newlocale>>. If the function call fails and the base
86 argument is not (locale_t) <<0>>, the contents of base shall remain
87 valid and unchanged.
88
89 The behavior is undefined if the base argument is the special locale
90 object LC_GLOBAL_LOCALE, or is not a valid locale object handle and is
91 not (locale_t) <<0>>.
92
93 RETURNS
94 Upon successful completion, the <<newlocale>> function shall return a
95 handle which the caller may use on subsequent calls to <<duplocale>>,
96 <<freelocale>>, and other functions taking a locale_t argument.
97
98 Upon failure, the <<newlocale>> function shall return (locale_t) <<0>>
99 and set errno to indicate the error.
100
101 PORTABILITY
102 <<newlocale>> is POSIX-1.2008.
103 */
104
105 #define _DEFAULT_SOURCE
106 #include <newlib.h>
107 #include <errno.h>
108 #include <stdlib.h>
109 #include "setlocale.h"
110
111 #define LC_VALID_MASK (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MONETARY_MASK \
112 | LC_NUMERIC_MASK | LC_TIME_MASK | LC_MESSAGES_MASK)
113
114 struct __locale_t *
newlocale(int category_mask,const char * locale,struct __locale_t * base)115 newlocale (int category_mask, const char *locale,
116 struct __locale_t *base)
117 {
118 (void) category_mask;
119 (void) locale;
120 (void) base;
121 #ifndef _MB_CAPABLE
122 return __get_C_locale ();
123 #else /* _MB_CAPABLE */
124 char new_categories[_LC_LAST][ENCODING_LEN + 1];
125 struct __locale_t tmp_locale, *new_locale;
126 int i;
127
128 /* Convert LC_ALL_MASK to a mask containing all valid MASK values.
129 This simplifies the code below. */
130 if (category_mask & LC_ALL_MASK)
131 {
132 category_mask &= ~LC_ALL_MASK;
133 category_mask |= LC_VALID_MASK;
134 }
135 /* Check for invalid mask values and valid locale ptr. */
136 if ((category_mask & ~LC_VALID_MASK) || !locale)
137 {
138 _REENT_ERRNO(p) = EINVAL;
139 return NULL;
140 }
141 /* If the new locale is supposed to be all default locale, just return
142 a pointer to the default locale. */
143 if ((!base && category_mask == 0)
144 || (category_mask == LC_VALID_MASK
145 && (!strcmp (locale, "C") || !strcmp (locale, "POSIX"))))
146 return __get_C_locale ();
147 /* Start with setting all values to the default locale values. */
148 tmp_locale = *__get_C_locale ();
149 /* Fill out new category strings. */
150 for (i = 1; i < _LC_LAST; ++i)
151 {
152 if (((1 << i) & category_mask) != 0)
153 {
154 /* If locale is "", fetch from environment. Otherwise use locale
155 name verbatim. */
156 const char *cat = (locale[0] == '\0') ? __get_locale_env (i)
157 : locale;
158 if (strlen (cat) > ENCODING_LEN)
159 {
160 _REENT_ERRNO(p) = EINVAL;
161 return NULL;
162 }
163 strcpy (new_categories[i], cat);
164 }
165 else
166 strcpy (new_categories[i], base ? base->categories[i] : "C");
167 }
168 /* Now go over all categories and set them. */
169 for (i = 1; i < _LC_LAST; ++i)
170 {
171 /* If we have a base locale, and the category is not in category_mask
172 or the new category is the base categroy, just copy over. */
173 if (base && (((1 << i) & category_mask) == 0
174 || !strcmp (base->categories[i], new_categories[i])))
175 {
176 strcpy (tmp_locale.categories[i], new_categories[i]);
177 if (i == LC_CTYPE)
178 {
179 tmp_locale.wctomb = base->wctomb;
180 tmp_locale.mbtowc = base->mbtowc;
181 tmp_locale.cjk_lang = base->cjk_lang;
182 tmp_locale.ctype_ptr = base->ctype_ptr;
183 }
184 #ifdef __HAVE_LOCALE_INFO__
185 /* Mark the values as "has still to be copied". We do this in
186 two steps to simplify freeing new locale types in case of a
187 subsequent error. */
188 tmp_locale.lc_cat[i].ptr = base->lc_cat[i].ptr;
189 tmp_locale.lc_cat[i].buf = (void *) -1;
190 #else /* !__HAVE_LOCALE_INFO__ */
191 if (i == LC_CTYPE)
192 strcpy (tmp_locale.ctype_codeset, base->ctype_codeset);
193 else if (i == LC_MESSAGES)
194 strcpy (tmp_locale.message_codeset, base->message_codeset);
195 #endif /* !__HAVE_LOCALE_INFO__ */
196 }
197 /* Otherwise, if the category is in category_mask, create entry. */
198 else if (((1 << i) & category_mask) != 0)
199 {
200 /* Nothing to do for "C"/"POSIX" locale. */
201 if (!strcmp (new_categories[i], "C")
202 || !strcmp (new_categories[i], "POSIX"))
203 continue;
204 /* Otherwise load locale data. */
205 else if (!__loadlocale (&tmp_locale, i, new_categories[i]))
206 {
207 _REENT_ERRNO(p) = ENOENT;
208 goto error;
209 }
210 }
211 }
212 /* Allocate new locale_t. */
213 new_locale = (struct __locale_t *) calloc (1, sizeof *new_locale);
214 if (!new_locale)
215 goto error;
216 if (base)
217 {
218 #ifdef __HAVE_LOCALE_INFO__
219 /* Step 2 of copying over.. Make sure to invalidate the copied buffer
220 pointers in base, so the subsequent _freelocale_r (base) doesn't free
221 the buffers now used in the new locale. */
222 for (i = 1; i < _LC_LAST; ++i)
223 if (tmp_locale.lc_cat[i].buf == (const void *) -1)
224 {
225 tmp_locale.lc_cat[i].buf = base->lc_cat[i].buf;
226 if (base != __get_C_locale ())
227 base->lc_cat[i].ptr = base->lc_cat[i].buf = NULL;
228 }
229 #endif /* __HAVE_LOCALE_INFO__ */
230 freelocale (base);
231 }
232
233 *new_locale = tmp_locale;
234 return new_locale;
235
236 error:
237 /* An error occured while we had already (potentially) allocated memory.
238 Free memory and return NULL. errno is supposed to be set already. */
239 #ifdef __HAVE_LOCALE_INFO__
240 for (i = 1; i < _LC_LAST; ++i)
241 if (((1 << i) & category_mask) != 0
242 && tmp_locale.lc_cat[i].buf
243 && tmp_locale.lc_cat[i].buf != (const void *) -1)
244 {
245 free ((void *) tmp_locale.lc_cat[i].ptr);
246 free (tmp_locale.lc_cat[i].buf);
247 }
248 #endif /* __HAVE_LOCALE_INFO__ */
249
250 return NULL;
251 #endif /* _MB_CAPABLE */
252 }
253