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       __errno_r(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 	      __errno_r(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               __errno_r(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