]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/locale/setlocale.c
zfs: merge openzfs/zfs@e0bd8118d
[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 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <locale.h>
41 #include <paths.h>                      /* for _PATH_LOCALE */
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include "collate.h"
46 #include "lmonetary.h"                  /* for __monetary_load_locale() */
47 #include "lnumeric.h"                   /* for __numeric_load_locale() */
48 #include "lmessages.h"                  /* for __messages_load_locale() */
49 #include "setlocale.h"
50 #include "ldpart.h"
51 #include "../stdtime/timelocal.h"       /* for __time_load_locale() */
52
53 /*
54  * Category names for getenv()
55  */
56 static const char categories[_LC_LAST][12] = {
57         "LC_ALL",
58         "LC_COLLATE",
59         "LC_CTYPE",
60         "LC_MONETARY",
61         "LC_NUMERIC",
62         "LC_TIME",
63         "LC_MESSAGES",
64 };
65
66 /*
67  * Current locales for each category
68  */
69 static char current_categories[_LC_LAST][ENCODING_LEN + 1] = {
70         "C",
71         "C",
72         "C",
73         "C",
74         "C",
75         "C",
76         "C",
77 };
78
79 /*
80  * Path to locale storage directory
81  */
82 char   *_PathLocale;
83
84 /*
85  * The locales we are going to try and load
86  */
87 static char new_categories[_LC_LAST][ENCODING_LEN + 1];
88 static char saved_categories[_LC_LAST][ENCODING_LEN + 1];
89
90 static char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)];
91
92 static char *currentlocale(void);
93 static char *loadlocale(int);
94 const char *__get_locale_env(int);
95
96 char *
97 setlocale(int category, const char *locale)
98 {
99         int i, j, len, saverr;
100         const char *env, *r;
101
102         if (category < LC_ALL || category >= _LC_LAST) {
103                 errno = EINVAL;
104                 return (NULL);
105         }
106         if (locale == NULL)
107                 return (category != LC_ALL ?
108                     current_categories[category] : currentlocale());
109
110         /*
111          * Default to the current locale for everything.
112          */
113         for (i = 1; i < _LC_LAST; ++i)
114                 (void)strcpy(new_categories[i], current_categories[i]);
115
116         /*
117          * Now go fill up new_categories from the locale argument
118          */
119         if (!*locale) {
120                 if (category == LC_ALL) {
121                         for (i = 1; i < _LC_LAST; ++i) {
122                                 env = __get_locale_env(i);
123                                 if (strlen(env) > ENCODING_LEN) {
124                                         errno = EINVAL;
125                                         return (NULL);
126                                 }
127                                 (void)strcpy(new_categories[i], env);
128                         }
129                 } else {
130                         env = __get_locale_env(category);
131                         if (strlen(env) > ENCODING_LEN) {
132                                 errno = EINVAL;
133                                 return (NULL);
134                         }
135                         (void)strcpy(new_categories[category], env);
136                 }
137         } else if (category != LC_ALL) {
138                 if (strlen(locale) > ENCODING_LEN) {
139                         errno = EINVAL;
140                         return (NULL);
141                 }
142                 (void)strcpy(new_categories[category], locale);
143         } else {
144                 if ((r = strchr(locale, '/')) == NULL) {
145                         if (strlen(locale) > ENCODING_LEN) {
146                                 errno = EINVAL;
147                                 return (NULL);
148                         }
149                         for (i = 1; i < _LC_LAST; ++i)
150                                 (void)strcpy(new_categories[i], locale);
151                 } else {
152                         for (i = 1; r[1] == '/'; ++r)
153                                 ;
154                         if (!r[1]) {
155                                 errno = EINVAL;
156                                 return (NULL);  /* Hmm, just slashes... */
157                         }
158                         do {
159                                 if (i == _LC_LAST)
160                                         break;  /* Too many slashes... */
161                                 if ((len = r - locale) > ENCODING_LEN) {
162                                         errno = EINVAL;
163                                         return (NULL);
164                                 }
165                                 (void)strlcpy(new_categories[i], locale,
166                                     len + 1);
167                                 i++;
168                                 while (*r == '/')
169                                         r++;
170                                 locale = r;
171                                 while (*r && *r != '/')
172                                         r++;
173                         } while (*locale);
174                         while (i < _LC_LAST) {
175                                 (void)strcpy(new_categories[i],
176                                     new_categories[i - 1]);
177                                 i++;
178                         }
179                 }
180         }
181
182         if (category != LC_ALL)
183                 return (loadlocale(category));
184
185         for (i = 1; i < _LC_LAST; ++i) {
186                 (void)strcpy(saved_categories[i], current_categories[i]);
187                 if (loadlocale(i) == NULL) {
188                         saverr = errno;
189                         for (j = 1; j < i; j++) {
190                                 (void)strcpy(new_categories[j],
191                                     saved_categories[j]);
192                                 if (loadlocale(j) == NULL) {
193                                         (void)strcpy(new_categories[j], "C");
194                                         (void)loadlocale(j);
195                                 }
196                         }
197                         errno = saverr;
198                         return (NULL);
199                 }
200         }
201         return (currentlocale());
202 }
203
204 static char *
205 currentlocale(void)
206 {
207         int i;
208
209         (void)strcpy(current_locale_string, current_categories[1]);
210
211         for (i = 2; i < _LC_LAST; ++i)
212                 if (strcmp(current_categories[1], current_categories[i])) {
213                         for (i = 2; i < _LC_LAST; ++i) {
214                                 (void)strcat(current_locale_string, "/");
215                                 (void)strcat(current_locale_string,
216                                     current_categories[i]);
217                         }
218                         break;
219                 }
220         return (current_locale_string);
221 }
222
223 static char *
224 loadlocale(int category)
225 {
226         char *new = new_categories[category];
227         char *old = current_categories[category];
228         int (*func) (const char *);
229         int saved_errno;
230
231         if ((new[0] == '.' &&
232             (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) ||
233             strchr(new, '/') != NULL) {
234                 errno = EINVAL;
235                 return (NULL);
236         }
237         saved_errno = errno;
238         errno = __detect_path_locale();
239         if (errno != 0)
240                 return (NULL);
241         errno = saved_errno;
242
243         switch (category) {
244         case LC_CTYPE:
245                 func = __wrap_setrunelocale;
246                 break;
247         case LC_COLLATE:
248                 func = __collate_load_tables;
249                 break;
250         case LC_TIME:
251                 func = __time_load_locale;
252                 break;
253         case LC_NUMERIC:
254                 func = __numeric_load_locale;
255                 break;
256         case LC_MONETARY:
257                 func = __monetary_load_locale;
258                 break;
259         case LC_MESSAGES:
260                 func = __messages_load_locale;
261                 break;
262         default:
263                 errno = EINVAL;
264                 return (NULL);
265         }
266
267         if (strcmp(new, old) == 0)
268                 return (old);
269
270         if (func(new) != _LDP_ERROR) {
271                 (void)strcpy(old, new);
272                 (void)strcpy(__xlocale_global_locale.components[category-1]->locale, new);
273                 return (old);
274         }
275
276         return (NULL);
277 }
278
279 const char *
280 __get_locale_env(int category)
281 {
282         const char *env;
283
284         /* 1. check LC_ALL. */
285         env = getenv(categories[0]);
286
287         /* 2. check LC_* */
288         if (env == NULL || !*env)
289                 env = getenv(categories[category]);
290
291         /* 3. check LANG */
292         if (env == NULL || !*env)
293                 env = getenv("LANG");
294
295         /* 4. if none is set, fall to "C" */
296         if (env == NULL || !*env)
297                 env = "C";
298
299         return (env);
300 }
301
302 /*
303  * Detect locale storage location and store its value to _PathLocale variable
304  */
305 int
306 __detect_path_locale(void)
307 {
308         if (_PathLocale == NULL) {
309                 char *p = secure_getenv("PATH_LOCALE");
310
311                 if (p != NULL) {
312                         if (strlen(p) + 1/*"/"*/ + ENCODING_LEN +
313                             1/*"/"*/ + CATEGORY_LEN >= PATH_MAX)
314                                 return (ENAMETOOLONG);
315                         _PathLocale = strdup(p);
316                         if (_PathLocale == NULL)
317                                 return (errno == 0 ? ENOMEM : errno);
318                 } else
319                         _PathLocale = _PATH_LOCALE;
320         }
321         return (0);
322 }