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