11573Srgrimes/*
289739Sphantom * Copyright (c) 1996 - 2002 FreeBSD Project
31573Srgrimes * Copyright (c) 1991, 1993
41573Srgrimes *	The Regents of the University of California.  All rights reserved.
51573Srgrimes *
61573Srgrimes * This code is derived from software contributed to Berkeley by
71573Srgrimes * Paul Borman at Krystal Technologies.
81573Srgrimes *
91573Srgrimes * Redistribution and use in source and binary forms, with or without
101573Srgrimes * modification, are permitted provided that the following conditions
111573Srgrimes * are met:
121573Srgrimes * 1. Redistributions of source code must retain the above copyright
131573Srgrimes *    notice, this list of conditions and the following disclaimer.
141573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151573Srgrimes *    notice, this list of conditions and the following disclaimer in the
161573Srgrimes *    documentation and/or other materials provided with the distribution.
171573Srgrimes * 4. Neither the name of the University nor the names of its contributors
181573Srgrimes *    may be used to endorse or promote products derived from this software
191573Srgrimes *    without specific prior written permission.
201573Srgrimes *
211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311573Srgrimes * SUCH DAMAGE.
321573Srgrimes */
331573Srgrimes
341573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
351573Srgrimesstatic char sccsid[] = "@(#)setlocale.c	8.1 (Berkeley) 7/4/93";
361573Srgrimes#endif /* LIBC_SCCS and not lint */
3792986Sobrien#include <sys/cdefs.h>
3892986Sobrien__FBSDID("$FreeBSD$");
391573Srgrimes
4019988Sache#include <sys/types.h>
4119988Sache#include <sys/stat.h>
42101259Sache#include <errno.h>
431573Srgrimes#include <limits.h>
441573Srgrimes#include <locale.h>
45116845Sphantom#include <paths.h>	/* for _PATH_LOCALE */
461573Srgrimes#include <stdlib.h>
471573Srgrimes#include <string.h>
4819988Sache#include <unistd.h>
4911695Sache#include "collate.h"
5072165Sphantom#include "lmonetary.h"	/* for __monetary_load_locale() */
5172165Sphantom#include "lnumeric.h"	/* for __numeric_load_locale() */
5272165Sphantom#include "lmessages.h"	/* for __messages_load_locale() */
5322330Sache#include "setlocale.h"
54101498Sache#include "ldpart.h"
5589739Sphantom#include "../stdtime/timelocal.h" /* for __time_load_locale() */
561573Srgrimes
5711695Sache/*
5811695Sache * Category names for getenv()
5911695Sache */
60228921Sjillesstatic const char categories[_LC_LAST][12] = {
6111695Sache    "LC_ALL",
6211695Sache    "LC_COLLATE",
6311695Sache    "LC_CTYPE",
6411695Sache    "LC_MONETARY",
6511695Sache    "LC_NUMERIC",
6611695Sache    "LC_TIME",
6735523Sache    "LC_MESSAGES",
6811695Sache};
6911695Sache
7011695Sache/*
7111695Sache * Current locales for each category
7211695Sache */
7322330Sachestatic char current_categories[_LC_LAST][ENCODING_LEN + 1] = {
7411695Sache    "C",
7511695Sache    "C",
7611695Sache    "C",
7711695Sache    "C",
7811695Sache    "C",
7911695Sache    "C",
8035523Sache    "C",
8111695Sache};
8211695Sache
8311695Sache/*
84116846Sphantom * Path to locale storage directory
85116846Sphantom */
86116846Sphantomchar	*_PathLocale;
87116846Sphantom
88116846Sphantom/*
8911695Sache * The locales we are going to try and load
9011695Sache */
9122330Sachestatic char new_categories[_LC_LAST][ENCODING_LEN + 1];
9222330Sachestatic char saved_categories[_LC_LAST][ENCODING_LEN + 1];
9311695Sache
9422330Sachestatic char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)];
951573Srgrimes
9692905Sobrienstatic char	*currentlocale(void);
9792905Sobrienstatic char	*loadlocale(int);
98227753Stheravenconst char *__get_locale_env(int);
991573Srgrimes
1001573Srgrimeschar *
1011573Srgrimessetlocale(category, locale)
1021573Srgrimes	int category;
1031573Srgrimes	const char *locale;
1041573Srgrimes{
105101366Sache	int i, j, len, saverr;
106125274Sache        const char *env, *r;
1071573Srgrimes
108101259Sache	if (category < LC_ALL || category >= _LC_LAST) {
109101259Sache		errno = EINVAL;
1101573Srgrimes		return (NULL);
111101259Sache	}
1121573Srgrimes
113101366Sache	if (locale == NULL)
11419964Sache		return (category != LC_ALL ?
1151573Srgrimes		    current_categories[category] : currentlocale());
1161573Srgrimes
1171573Srgrimes	/*
1181573Srgrimes	 * Default to the current locale for everything.
1191573Srgrimes	 */
12019971Sache	for (i = 1; i < _LC_LAST; ++i)
1211573Srgrimes		(void)strcpy(new_categories[i], current_categories[i]);
1221573Srgrimes
1231573Srgrimes	/*
1241573Srgrimes	 * Now go fill up new_categories from the locale argument
1251573Srgrimes	 */
1261573Srgrimes	if (!*locale) {
12719964Sache		if (category == LC_ALL) {
1281573Srgrimes			for (i = 1; i < _LC_LAST; ++i) {
129125274Sache				env = __get_locale_env(i);
130125274Sache				if (strlen(env) > ENCODING_LEN) {
131101366Sache					errno = EINVAL;
132101366Sache					return (NULL);
133101366Sache				}
134101366Sache				(void)strcpy(new_categories[i], env);
1351573Srgrimes			}
136125274Sache		} else {
137125274Sache			env = __get_locale_env(category);
138125274Sache			if (strlen(env) > ENCODING_LEN) {
139125274Sache				errno = EINVAL;
140125274Sache				return (NULL);
141125274Sache			}
142125274Sache			(void)strcpy(new_categories[category], env);
1431573Srgrimes		}
144101366Sache	} else if (category != LC_ALL) {
145101366Sache		if (strlen(locale) > ENCODING_LEN) {
146101366Sache			errno = EINVAL;
147101366Sache			return (NULL);
148101366Sache		}
149101366Sache		(void)strcpy(new_categories[category], locale);
150101366Sache	} else {
15122330Sache		if ((r = strchr(locale, '/')) == NULL) {
152101366Sache			if (strlen(locale) > ENCODING_LEN) {
153101366Sache				errno = EINVAL;
154101366Sache				return (NULL);
155101366Sache			}
156101193Sache			for (i = 1; i < _LC_LAST; ++i)
157101366Sache				(void)strcpy(new_categories[i], locale);
1581573Srgrimes		} else {
159101259Sache			for (i = 1; r[1] == '/'; ++r)
160101259Sache				;
161101259Sache			if (!r[1]) {
162101259Sache				errno = EINVAL;
1631573Srgrimes				return (NULL);	/* Hmm, just slashes... */
164101259Sache			}
1651573Srgrimes			do {
166101193Sache				if (i == _LC_LAST)
167101223Sache					break;  /* Too many slashes... */
168101366Sache				if ((len = r - locale) > ENCODING_LEN) {
169101366Sache					errno = EINVAL;
170101366Sache					return (NULL);
171101366Sache				}
172114443Snectar				(void)strlcpy(new_categories[i], locale,
173101259Sache					      len + 1);
17422330Sache				i++;
175123801Sache				while (*r == '/')
176123801Sache					r++;
1771573Srgrimes				locale = r;
178123801Sache				while (*r && *r != '/')
179123801Sache					r++;
1801573Srgrimes			} while (*locale);
18153050Sache			while (i < _LC_LAST) {
1821573Srgrimes				(void)strcpy(new_categories[i],
183101259Sache					     new_categories[i-1]);
18453050Sache				i++;
18553050Sache			}
1861573Srgrimes		}
1871573Srgrimes	}
1881573Srgrimes
18965420Simp	if (category != LC_ALL)
19019971Sache		return (loadlocale(category));
1911573Srgrimes
19219971Sache	for (i = 1; i < _LC_LAST; ++i) {
19319971Sache		(void)strcpy(saved_categories[i], current_categories[i]);
19419964Sache		if (loadlocale(i) == NULL) {
195101366Sache			saverr = errno;
19619964Sache			for (j = 1; j < i; j++) {
19719964Sache				(void)strcpy(new_categories[j],
198101259Sache					     saved_categories[j]);
199101498Sache				if (loadlocale(j) == NULL) {
200101498Sache					(void)strcpy(new_categories[j], "C");
201101498Sache					(void)loadlocale(j);
202101498Sache				}
20319964Sache			}
204101259Sache			errno = saverr;
20519964Sache			return (NULL);
20619964Sache		}
20719971Sache	}
20819964Sache	return (currentlocale());
2091573Srgrimes}
2101573Srgrimes
2111573Srgrimesstatic char *
21211695Sachecurrentlocale()
21311695Sache{
21419964Sache	int i;
21511695Sache
21611695Sache	(void)strcpy(current_locale_string, current_categories[1]);
21711695Sache
21811695Sache	for (i = 2; i < _LC_LAST; ++i)
21911695Sache		if (strcmp(current_categories[1], current_categories[i])) {
22035523Sache			for (i = 2; i < _LC_LAST; ++i) {
221101259Sache				(void)strcat(current_locale_string, "/");
222101259Sache				(void)strcat(current_locale_string,
223101259Sache					     current_categories[i]);
22435523Sache			}
22511695Sache			break;
22611695Sache		}
22711695Sache	return (current_locale_string);
22811695Sache}
22911695Sache
23011695Sachestatic char *
2311573Srgrimesloadlocale(category)
2321573Srgrimes	int category;
2331573Srgrimes{
23419971Sache	char *new = new_categories[category];
23519971Sache	char *old = current_categories[category];
236101498Sache	int (*func)(const char *);
237116847Sphantom	int saved_errno;
23819964Sache
239101259Sache	if ((new[0] == '.' &&
240101259Sache	     (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) ||
241101259Sache	    strchr(new, '/') != NULL) {
242101259Sache		errno = EINVAL;
243101259Sache		return (NULL);
244101259Sache	}
245101259Sache
246116847Sphantom	saved_errno = errno;
247116847Sphantom	errno = __detect_path_locale();
248116847Sphantom	if (errno != 0)
249116847Sphantom		return (NULL);
250116847Sphantom	errno = saved_errno;
25119964Sache
252101259Sache	switch (category) {
253101259Sache	case LC_CTYPE:
254117270Sache		func = __wrap_setrunelocale;
255101292Sache		break;
256101259Sache	case LC_COLLATE:
257101292Sache		func = __collate_load_tables;
258101292Sache		break;
259101259Sache	case LC_TIME:
260101292Sache		func = __time_load_locale;
261101292Sache		break;
262101259Sache	case LC_NUMERIC:
263101292Sache		func = __numeric_load_locale;
264101292Sache		break;
265101259Sache	case LC_MONETARY:
266101292Sache		func = __monetary_load_locale;
267101292Sache		break;
268101259Sache	case LC_MESSAGES:
269101292Sache		func = __messages_load_locale;
270101292Sache		break;
271101259Sache	default:
272101259Sache		errno = EINVAL;
273101259Sache		return (NULL);
2746485Sache	}
275101292Sache
276101292Sache	if (strcmp(new, old) == 0)
277101292Sache		return (old);
278101292Sache
279101498Sache	if (func(new) != _LDP_ERROR) {
280101292Sache		(void)strcpy(old, new);
281227753Stheraven		(void)strcpy(__xlocale_global_locale.components[category-1]->locale, new);
282101498Sache		return (old);
283101498Sache	}
284101292Sache
285101498Sache	return (NULL);
28619964Sache}
28719964Sache
288227753Stheravenconst char *
289125274Sache__get_locale_env(category)
290125274Sache        int category;
291125274Sache{
292125274Sache        const char *env;
293125274Sache
294125274Sache        /* 1. check LC_ALL. */
295125274Sache        env = getenv(categories[0]);
296125274Sache
297125274Sache        /* 2. check LC_* */
298125274Sache	if (env == NULL || !*env)
299125274Sache                env = getenv(categories[category]);
300125274Sache
301125274Sache        /* 3. check LANG */
302125274Sache	if (env == NULL || !*env)
303125274Sache                env = getenv("LANG");
304125274Sache
305125274Sache        /* 4. if none is set, fall to "C" */
306125274Sache	if (env == NULL || !*env)
307125274Sache                env = "C";
308125274Sache
309125274Sache	return (env);
310125274Sache}
311125274Sache
312116847Sphantom/*
313116847Sphantom * Detect locale storage location and store its value to _PathLocale variable
314116847Sphantom */
315116847Sphantomint
316116847Sphantom__detect_path_locale(void)
317116847Sphantom{
318116847Sphantom	if (_PathLocale == NULL) {
319116847Sphantom		char *p = getenv("PATH_LOCALE");
320116847Sphantom
321121667Stjr		if (p != NULL && !issetugid()) {
322116847Sphantom			if (strlen(p) + 1/*"/"*/ + ENCODING_LEN +
323116847Sphantom			    1/*"/"*/ + CATEGORY_LEN >= PATH_MAX)
324116847Sphantom				return (ENAMETOOLONG);
325116847Sphantom			_PathLocale = strdup(p);
326116847Sphantom			if (_PathLocale == NULL)
327116847Sphantom				return (errno == 0 ? ENOMEM : errno);
328116847Sphantom		} else
329116847Sphantom			_PathLocale = _PATH_LOCALE;
330116847Sphantom	}
331116847Sphantom	return (0);
332116847Sphantom}
333116847Sphantom
334