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