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