]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/locale/setlocale.c
Upgrade our copies of clang, llvm, lldb and compiler-rt to r312293 from
[FreeBSD/FreeBSD.git] / lib / libc / locale / setlocale.c
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  * This code is derived from software contributed to Berkeley by
7  * Paul Borman at Krystal Technologies.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93";
36 #endif /* LIBC_SCCS and not lint */
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <errno.h>
43 #include <limits.h>
44 #include <locale.h>
45 #include <paths.h>                      /* for _PATH_LOCALE */
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include "collate.h"
50 #include "lmonetary.h"                  /* for __monetary_load_locale() */
51 #include "lnumeric.h"                   /* for __numeric_load_locale() */
52 #include "lmessages.h"                  /* for __messages_load_locale() */
53 #include "setlocale.h"
54 #include "ldpart.h"
55 #include "../stdtime/timelocal.h"       /* for __time_load_locale() */
56
57 /*
58  * Category names for getenv()
59  */
60 static const char categories[_LC_LAST][12] = {
61         "LC_ALL",
62         "LC_COLLATE",
63         "LC_CTYPE",
64         "LC_MONETARY",
65         "LC_NUMERIC",
66         "LC_TIME",
67         "LC_MESSAGES",
68 };
69
70 /*
71  * Current locales for each category
72  */
73 static char current_categories[_LC_LAST][ENCODING_LEN + 1] = {
74         "C",
75         "C",
76         "C",
77         "C",
78         "C",
79         "C",
80         "C",
81 };
82
83 /*
84  * Path to locale storage directory
85  */
86 char   *_PathLocale;
87
88 /*
89  * The locales we are going to try and load
90  */
91 static char new_categories[_LC_LAST][ENCODING_LEN + 1];
92 static char saved_categories[_LC_LAST][ENCODING_LEN + 1];
93
94 static char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)];
95
96 static char *currentlocale(void);
97 static char *loadlocale(int);
98 const char *__get_locale_env(int);
99
100 char *
101 setlocale(int category, const char *locale)
102 {
103         int i, j, len, saverr;
104         const char *env, *r;
105
106         if (category < LC_ALL || category >= _LC_LAST) {
107                 errno = EINVAL;
108                 return (NULL);
109         }
110         if (locale == NULL)
111                 return (category != LC_ALL ?
112                     current_categories[category] : currentlocale());
113
114         /*
115          * Default to the current locale for everything.
116          */
117         for (i = 1; i < _LC_LAST; ++i)
118                 (void)strcpy(new_categories[i], current_categories[i]);
119
120         /*
121          * Now go fill up new_categories from the locale argument
122          */
123         if (!*locale) {
124                 if (category == LC_ALL) {
125                         for (i = 1; i < _LC_LAST; ++i) {
126                                 env = __get_locale_env(i);
127                                 if (strlen(env) > ENCODING_LEN) {
128                                         errno = EINVAL;
129                                         return (NULL);
130                                 }
131                                 (void)strcpy(new_categories[i], env);
132                         }
133                 } else {
134                         env = __get_locale_env(category);
135                         if (strlen(env) > ENCODING_LEN) {
136                                 errno = EINVAL;
137                                 return (NULL);
138                         }
139                         (void)strcpy(new_categories[category], env);
140                 }
141         } else if (category != LC_ALL) {
142                 if (strlen(locale) > ENCODING_LEN) {
143                         errno = EINVAL;
144                         return (NULL);
145                 }
146                 (void)strcpy(new_categories[category], locale);
147         } else {
148                 if ((r = strchr(locale, '/')) == NULL) {
149                         if (strlen(locale) > ENCODING_LEN) {
150                                 errno = EINVAL;
151                                 return (NULL);
152                         }
153                         for (i = 1; i < _LC_LAST; ++i)
154                                 (void)strcpy(new_categories[i], locale);
155                 } else {
156                         for (i = 1; r[1] == '/'; ++r)
157                                 ;
158                         if (!r[1]) {
159                                 errno = EINVAL;
160                                 return (NULL);  /* Hmm, just slashes... */
161                         }
162                         do {
163                                 if (i == _LC_LAST)
164                                         break;  /* Too many slashes... */
165                                 if ((len = r - locale) > ENCODING_LEN) {
166                                         errno = EINVAL;
167                                         return (NULL);
168                                 }
169                                 (void)strlcpy(new_categories[i], locale,
170                                     len + 1);
171                                 i++;
172                                 while (*r == '/')
173                                         r++;
174                                 locale = r;
175                                 while (*r && *r != '/')
176                                         r++;
177                         } while (*locale);
178                         while (i < _LC_LAST) {
179                                 (void)strcpy(new_categories[i],
180                                     new_categories[i - 1]);
181                                 i++;
182                         }
183                 }
184         }
185
186         if (category != LC_ALL)
187                 return (loadlocale(category));
188
189         for (i = 1; i < _LC_LAST; ++i) {
190                 (void)strcpy(saved_categories[i], current_categories[i]);
191                 if (loadlocale(i) == NULL) {
192                         saverr = errno;
193                         for (j = 1; j < i; j++) {
194                                 (void)strcpy(new_categories[j],
195                                     saved_categories[j]);
196                                 if (loadlocale(j) == NULL) {
197                                         (void)strcpy(new_categories[j], "C");
198                                         (void)loadlocale(j);
199                                 }
200                         }
201                         errno = saverr;
202                         return (NULL);
203                 }
204         }
205         return (currentlocale());
206 }
207
208 static char *
209 currentlocale(void)
210 {
211         int i;
212
213         (void)strcpy(current_locale_string, current_categories[1]);
214
215         for (i = 2; i < _LC_LAST; ++i)
216                 if (strcmp(current_categories[1], current_categories[i])) {
217                         for (i = 2; i < _LC_LAST; ++i) {
218                                 (void)strcat(current_locale_string, "/");
219                                 (void)strcat(current_locale_string,
220                                     current_categories[i]);
221                         }
222                         break;
223                 }
224         return (current_locale_string);
225 }
226
227 static char *
228 loadlocale(int category)
229 {
230         char *new = new_categories[category];
231         char *old = current_categories[category];
232         int (*func) (const char *);
233         int saved_errno;
234
235         if ((new[0] == '.' &&
236             (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) ||
237             strchr(new, '/') != NULL) {
238                 errno = EINVAL;
239                 return (NULL);
240         }
241         saved_errno = errno;
242         errno = __detect_path_locale();
243         if (errno != 0)
244                 return (NULL);
245         errno = saved_errno;
246
247         switch (category) {
248         case LC_CTYPE:
249                 func = __wrap_setrunelocale;
250                 break;
251         case LC_COLLATE:
252                 func = __collate_load_tables;
253                 break;
254         case LC_TIME:
255                 func = __time_load_locale;
256                 break;
257         case LC_NUMERIC:
258                 func = __numeric_load_locale;
259                 break;
260         case LC_MONETARY:
261                 func = __monetary_load_locale;
262                 break;
263         case LC_MESSAGES:
264                 func = __messages_load_locale;
265                 break;
266         default:
267                 errno = EINVAL;
268                 return (NULL);
269         }
270
271         if (strcmp(new, old) == 0)
272                 return (old);
273
274         if (func(new) != _LDP_ERROR) {
275                 (void)strcpy(old, new);
276                 (void)strcpy(__xlocale_global_locale.components[category-1]->locale, new);
277                 return (old);
278         }
279
280         return (NULL);
281 }
282
283 const char *
284 __get_locale_env(int category)
285 {
286         const char *env;
287
288         /* 1. check LC_ALL. */
289         env = getenv(categories[0]);
290
291         /* 2. check LC_* */
292         if (env == NULL || !*env)
293                 env = getenv(categories[category]);
294
295         /* 3. check LANG */
296         if (env == NULL || !*env)
297                 env = getenv("LANG");
298
299         /* 4. if none is set, fall to "C" */
300         if (env == NULL || !*env)
301                 env = "C";
302
303         return (env);
304 }
305
306 /*
307  * Detect locale storage location and store its value to _PathLocale variable
308  */
309 int
310 __detect_path_locale(void)
311 {
312         if (_PathLocale == NULL) {
313                 char *p = getenv("PATH_LOCALE");
314
315                 if (p != NULL && !issetugid()) {
316                         if (strlen(p) + 1/*"/"*/ + ENCODING_LEN +
317                             1/*"/"*/ + CATEGORY_LEN >= PATH_MAX)
318                                 return (ENAMETOOLONG);
319                         _PathLocale = strdup(p);
320                         if (_PathLocale == NULL)
321                                 return (errno == 0 ? ENOMEM : errno);
322                 } else
323                         _PathLocale = _PATH_LOCALE;
324         }
325         return (0);
326 }