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 _GNU_SOURCE
106 #include "setlocale.h"
107
108 struct __locale_t *
newlocale(int category_mask,const char * locale,struct __locale_t * base)109 newlocale (int category_mask, const char *locale,
110 struct __locale_t *base)
111 {
112 (void) category_mask;
113 (void) locale;
114 (void) base;
115
116 /* Check for invalid mask values and valid locale ptr. */
117 if ((category_mask & ~LC_ALL_MASK) || !locale)
118 {
119 _REENT_ERRNO(p) = EINVAL;
120 return NULL;
121 }
122 /* If the new locale is supposed to be all default locale, just return
123 a pointer to the default locale. */
124 if ((!base && category_mask == 0)
125 || (category_mask == LC_ALL_MASK
126 && (!strcmp (locale, "C") || !strcmp (locale, "POSIX"))))
127 return __get_C_locale ();
128
129 #ifndef _MB_CAPABLE
130 _REENT_ERRNO(p) = EINVAL;
131 return NULL;
132 #else /* _MB_CAPABLE */
133 char new_categories[_LC_LAST][ENCODING_LEN + 1];
134 struct __locale_t tmp_locale, *new_locale;
135 int i;
136
137 /* Start with setting all values to the default locale values. */
138 tmp_locale = *__get_C_locale ();
139 /* Fill out new category strings. */
140 for (i = 1; i < _LC_LAST; ++i)
141 {
142 if (((1 << i) & category_mask) != 0)
143 {
144 /* If locale is "", fetch from environment. Otherwise use locale
145 name verbatim. */
146 const char *cat = (locale[0] == '\0') ? __get_locale_env (i)
147 : locale;
148 if (strlen (cat) > ENCODING_LEN)
149 {
150 _REENT_ERRNO(p) = EINVAL;
151 return NULL;
152 }
153 strcpy (new_categories[i], cat);
154 }
155 else
156 strcpy (new_categories[i], base ? base->categories[i] : "C");
157 }
158 /* Now go over all categories and set them. */
159 for (i = 1; i < _LC_LAST; ++i)
160 {
161 /* If we have a base locale, and the category is not in category_mask
162 or the new category is the base categroy, just copy over. */
163 if (base && (((1 << i) & category_mask) == 0
164 || !strcmp (base->categories[i], new_categories[i])))
165 {
166 strcpy (tmp_locale.categories[i], new_categories[i]);
167 if (i == LC_CTYPE)
168 {
169 tmp_locale.wctomb = base->wctomb;
170 tmp_locale.mbtowc = base->mbtowc;
171 tmp_locale.cjk_lang = base->cjk_lang;
172 tmp_locale.ctype_ptr = base->ctype_ptr;
173 }
174 #ifdef __HAVE_LOCALE_INFO__
175 /* Mark the values as "has still to be copied". We do this in
176 two steps to simplify freeing new locale types in case of a
177 subsequent error. */
178 tmp_locale.lc_cat[i].ptr = base->lc_cat[i].ptr;
179 tmp_locale.lc_cat[i].buf = (void *) -1;
180 #else /* !__HAVE_LOCALE_INFO__ */
181 if (i == LC_CTYPE)
182 strcpy (tmp_locale.ctype_codeset, base->ctype_codeset);
183 else if (i == LC_MESSAGES)
184 strcpy (tmp_locale.message_codeset, base->message_codeset);
185 #endif /* !__HAVE_LOCALE_INFO__ */
186 }
187 /* Otherwise, if the category is in category_mask, create entry. */
188 else if (((1 << i) & category_mask) != 0)
189 {
190 /* Nothing to do for "C"/"POSIX" locale. */
191 if (!strcmp (new_categories[i], "C")
192 || !strcmp (new_categories[i], "POSIX"))
193 continue;
194 /* Otherwise load locale data. */
195 else if (!__loadlocale (&tmp_locale, i, new_categories[i]))
196 {
197 _REENT_ERRNO(p) = ENOENT;
198 goto error;
199 }
200 }
201 }
202 /* Allocate new locale_t. */
203 new_locale = (struct __locale_t *) calloc (1, sizeof *new_locale);
204 if (!new_locale)
205 goto error;
206 if (base)
207 {
208 #ifdef __HAVE_LOCALE_INFO__
209 /* Step 2 of copying over.. Make sure to invalidate the copied buffer
210 pointers in base, so the subsequent _freelocale_r (base) doesn't free
211 the buffers now used in the new locale. */
212 for (i = 1; i < _LC_LAST; ++i)
213 if (tmp_locale.lc_cat[i].buf == (const void *) -1)
214 {
215 tmp_locale.lc_cat[i].buf = base->lc_cat[i].buf;
216 if (base != __get_C_locale ())
217 base->lc_cat[i].ptr = base->lc_cat[i].buf = NULL;
218 }
219 #endif /* __HAVE_LOCALE_INFO__ */
220 freelocale (base);
221 }
222
223 *new_locale = tmp_locale;
224 return new_locale;
225
226 error:
227 /* An error occured while we had already (potentially) allocated memory.
228 Free memory and return NULL. errno is supposed to be set already. */
229 #ifdef __HAVE_LOCALE_INFO__
230 for (i = 1; i < _LC_LAST; ++i)
231 if (((1 << i) & category_mask) != 0
232 && tmp_locale.lc_cat[i].buf
233 && tmp_locale.lc_cat[i].buf != (const void *) -1)
234 {
235 free ((void *) tmp_locale.lc_cat[i].ptr);
236 free (tmp_locale.lc_cat[i].buf);
237 }
238 #endif /* __HAVE_LOCALE_INFO__ */
239
240 return NULL;
241 #endif /* _MB_CAPABLE */
242 }
243