xref: /freebsd/lib/libc/locale/setlocale.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*
2  * Copyright (c) 1996 - 2001 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. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifdef LIBC_RCS
39 static const char rcsid[] =
40   "$FreeBSD$";
41 #endif
42 
43 #if defined(LIBC_SCCS) && !defined(lint)
44 static char sccsid[] = "@(#)setlocale.c	8.1 (Berkeley) 7/4/93";
45 #endif /* LIBC_SCCS and not lint */
46 
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <limits.h>
50 #include <locale.h>
51 #include <rune.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include "collate.h"
56 #include "lmonetary.h"	/* for __monetary_load_locale() */
57 #include "lnumeric.h"	/* for __numeric_load_locale() */
58 #include "lmessages.h"	/* for __messages_load_locale() */
59 #include "setlocale.h"
60 
61 /*
62  * Category names for getenv()
63  */
64 static char *categories[_LC_LAST] = {
65     "LC_ALL",
66     "LC_COLLATE",
67     "LC_CTYPE",
68     "LC_MONETARY",
69     "LC_NUMERIC",
70     "LC_TIME",
71     "LC_MESSAGES",
72 };
73 
74 /*
75  * Current locales for each category
76  */
77 static char current_categories[_LC_LAST][ENCODING_LEN + 1] = {
78     "C",
79     "C",
80     "C",
81     "C",
82     "C",
83     "C",
84     "C",
85 };
86 
87 /*
88  * The locales we are going to try and load
89  */
90 static char new_categories[_LC_LAST][ENCODING_LEN + 1];
91 static char saved_categories[_LC_LAST][ENCODING_LEN + 1];
92 
93 static char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)];
94 
95 static char	*currentlocale __P((void));
96 static char	*loadlocale __P((int));
97 
98 extern int __time_load_locale __P((const char *)); /* strftime.c */
99 
100 char *
101 setlocale(category, locale)
102 	int category;
103 	const char *locale;
104 {
105 	int i, j, len;
106 	char *env, *r;
107 
108 	if (category < LC_ALL || category >= _LC_LAST)
109 		return (NULL);
110 
111 	if (!locale)
112 		return (category != LC_ALL ?
113 		    current_categories[category] : currentlocale());
114 
115 	/*
116 	 * Default to the current locale for everything.
117 	 */
118 	for (i = 1; i < _LC_LAST; ++i)
119 		(void)strcpy(new_categories[i], current_categories[i]);
120 
121 	/*
122 	 * Now go fill up new_categories from the locale argument
123 	 */
124 	if (!*locale) {
125 		env = getenv("LC_ALL");
126 
127 		if (category != LC_ALL && (!env || !*env))
128 			env = getenv(categories[category]);
129 
130 		if (!env || !*env)
131 			env = getenv("LANG");
132 
133 		if (!env || !*env || strchr(env, '/'))
134 			env = "C";
135 
136 		(void) strncpy(new_categories[category], env, ENCODING_LEN);
137 		new_categories[category][ENCODING_LEN] = '\0';
138 		if (category == LC_ALL) {
139 			for (i = 1; i < _LC_LAST; ++i) {
140 				if (!(env = getenv(categories[i])) || !*env)
141 					env = new_categories[LC_ALL];
142 				(void)strncpy(new_categories[i], env, ENCODING_LEN);
143 				new_categories[i][ENCODING_LEN] = '\0';
144 			}
145 		}
146 	} else if (category != LC_ALL)  {
147 		(void)strncpy(new_categories[category], locale, ENCODING_LEN);
148 		new_categories[category][ENCODING_LEN] = '\0';
149 	} else {
150 		if ((r = strchr(locale, '/')) == NULL) {
151 			for (i = 1; i < _LC_LAST; ++i) {
152 				(void)strncpy(new_categories[i], locale, ENCODING_LEN);
153 				new_categories[i][ENCODING_LEN] = '\0';
154 			}
155 		} else {
156 			for (i = 1; r[1] == '/'; ++r);
157 			if (!r[1])
158 				return (NULL);	/* Hmm, just slashes... */
159 			do {
160 				len = r - locale > ENCODING_LEN ? ENCODING_LEN : r - locale;
161 				(void)strncpy(new_categories[i], locale, len);
162 				new_categories[i][len] = '\0';
163 				i++;
164 				locale = r;
165 				while (*locale == '/')
166 				    ++locale;
167 				while (*++r && *r != '/');
168 			} while (*locale);
169 			while (i < _LC_LAST) {
170 				(void)strcpy(new_categories[i],
171 				    new_categories[i-1]);
172 				i++;
173 			}
174 		}
175 	}
176 
177 	if (category != LC_ALL)
178 		return (loadlocale(category));
179 
180 	for (i = 1; i < _LC_LAST; ++i) {
181 		(void)strcpy(saved_categories[i], current_categories[i]);
182 		if (loadlocale(i) == NULL) {
183 			for (j = 1; j < i; j++) {
184 				(void)strcpy(new_categories[j],
185 				     saved_categories[j]);
186 				/* XXX can fail too */
187 				(void)loadlocale(j);
188 			}
189 			return (NULL);
190 		}
191 	}
192 	return (currentlocale());
193 }
194 
195 static char *
196 currentlocale()
197 {
198 	int i;
199 
200 	(void)strcpy(current_locale_string, current_categories[1]);
201 
202 	for (i = 2; i < _LC_LAST; ++i)
203 		if (strcmp(current_categories[1], current_categories[i])) {
204 			for (i = 2; i < _LC_LAST; ++i) {
205 				(void) strcat(current_locale_string, "/");
206 				(void) strcat(current_locale_string, current_categories[i]);
207 			}
208 			break;
209 		}
210 	return (current_locale_string);
211 }
212 
213 static char *
214 loadlocale(category)
215 	int category;
216 {
217 	char *ret;
218 	char *new = new_categories[category];
219 	char *old = current_categories[category];
220 
221 	if (_PathLocale == NULL) {
222 		char *p = getenv("PATH_LOCALE");
223 
224 		if (p != NULL
225 #ifndef __NETBSD_SYSCALLS
226 			&& !issetugid()
227 #endif
228 			) {
229 			if (strlen(p) + 1/*"/"*/ + ENCODING_LEN +
230 			    1/*"/"*/ + CATEGORY_LEN >= PATH_MAX)
231 				return (NULL);
232 			_PathLocale = strdup(p);
233 			if (_PathLocale == NULL)
234 				return (NULL);
235 		} else
236 			_PathLocale = _PATH_LOCALE;
237 	}
238 
239 	if (strcmp(new, old) == 0)
240 		return (old);
241 
242 	if (category == LC_CTYPE) {
243 		ret = setrunelocale(new) ? NULL : new;
244 		if (!ret)
245 			(void)setrunelocale(old);
246 		else
247 			(void)strcpy(old, new);
248 		return (ret);
249 	}
250 
251 #define LOAD_CATEGORY(CAT, FUNC)			\
252 	if (category == CAT) {				\
253 		ret = (FUNC(new) < 0) ? NULL : new;	\
254 		if (!ret)				\
255 			(void)FUNC(old);		\
256 		else					\
257 			(void)strcpy(old, new);		\
258 		return (ret);				\
259 	}
260 
261 	LOAD_CATEGORY(LC_COLLATE, __collate_load_tables);
262 	LOAD_CATEGORY(LC_TIME, __time_load_locale);
263 	LOAD_CATEGORY(LC_NUMERIC, __numeric_load_locale);
264 	LOAD_CATEGORY(LC_MONETARY, __monetary_load_locale);
265 	LOAD_CATEGORY(LC_MESSAGES, __messages_load_locale);
266 
267 	/* Just in case...*/
268 	return (NULL);
269 }
270 
271