setlocale.c revision 32524
11573Srgrimes/*
223661Speter * Copyright (c) 1991, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Paul Borman at Krystal Technologies.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
161573Srgrimes * 3. All advertising materials mentioning features or use of this software
171573Srgrimes *    must display the following acknowledgement:
181573Srgrimes *	This product includes software developed by the University of
191573Srgrimes *	California, Berkeley and its contributors.
201573Srgrimes * 4. Neither the name of the University nor the names of its contributors
211573Srgrimes *    may be used to endorse or promote products derived from this software
221573Srgrimes *    without specific prior written permission.
231573Srgrimes *
241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3123661Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3390041Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3490041Sobrien * SUCH DAMAGE.
351573Srgrimes *
3671579Sdeischen * $Id: setlocale.c,v 1.21 1997/04/07 08:54:35 ache Exp $
371573Srgrimes */
381573Srgrimes
391573Srgrimes#ifdef LIBC_RCS
4023661Speterstatic const char rcsid[] =
411573Srgrimes	"$Id: setlocale.c,v 1.21 1997/04/07 08:54:35 ache Exp $";
4223661Speter#endif
431573Srgrimes
441573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
451573Srgrimesstatic char sccsid[] = "@(#)setlocale.c	8.1 (Berkeley) 7/4/93";
461573Srgrimes#endif /* LIBC_SCCS and not lint */
4771579Sdeischen
481573Srgrimes#include <sys/types.h>
491573Srgrimes#include <sys/stat.h>
501573Srgrimes#include <limits.h>
5117141Sjkh#include <locale.h>
521573Srgrimes#include <rune.h>
53109039Stjr#include <stdlib.h>
54109039Stjr#include <string.h>
551573Srgrimes#include <unistd.h>
561573Srgrimes#include "collate.h"
571573Srgrimes#include "setlocale.h"
581573Srgrimes
591573Srgrimes/*
6090041Sobrien * Category names for getenv()
6190041Sobrien */
6290041Sobrienstatic char *categories[_LC_LAST] = {
6390041Sobrien    "LC_ALL",
6490041Sobrien    "LC_COLLATE",
65198053Sjilles    "LC_CTYPE",
661573Srgrimes    "LC_MONETARY",
671573Srgrimes    "LC_NUMERIC",
681573Srgrimes    "LC_TIME",
69198053Sjilles};
701573Srgrimes
71198053Sjilles/*
72198053Sjilles * Current locales for each category
731573Srgrimes */
741573Srgrimesstatic char current_categories[_LC_LAST][ENCODING_LEN + 1] = {
751573Srgrimes    "C",
761573Srgrimes    "C",
771573Srgrimes    "C",
781573Srgrimes    "C",
791573Srgrimes    "C",
801573Srgrimes    "C",
811573Srgrimes};
821573Srgrimes
831573Srgrimes/*
841573Srgrimes * The locales we are going to try and load
855072Sbde */
865072Sbdestatic char new_categories[_LC_LAST][ENCODING_LEN + 1];
875072Sbdestatic char saved_categories[_LC_LAST][ENCODING_LEN + 1];
885072Sbde
891573Srgrimesstatic char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)];
901573Srgrimes
91150172Sachestatic char	*currentlocale __P((void));
921573Srgrimesstatic char	*loadlocale __P((int));
931573Srgrimesstatic int      stub_load_locale __P((const char *));
941573Srgrimes
9565468Speterextern int __time_load_locale __P((const char *)); /* strftime.c */
9665468Speter
9765468Speter#ifdef XPG4
9865468Speterextern int _xpg4_setrunelocale __P((char *));
9965468Speter#endif
10065468Speter
10165468Speterchar *
10265468Spetersetlocale(category, locale)
10329476Sphk	int category;
10429392Sphk	const char *locale;
10565468Speter{
10629392Sphk	int i, j, len;
1071573Srgrimes	char *env, *r;
1081573Srgrimes
1091573Srgrimes	if (category < LC_ALL || category >= _LC_LAST)
1101573Srgrimes		return (NULL);
1111573Srgrimes
1121573Srgrimes	if (!locale)
1131573Srgrimes		return (category != LC_ALL ?
1141573Srgrimes		    current_categories[category] : currentlocale());
1151573Srgrimes
1161573Srgrimes	/*
1171573Srgrimes	 * Default to the current locale for everything.
1181573Srgrimes	 */
1191573Srgrimes	for (i = 1; i < _LC_LAST; ++i)
120198053Sjilles		(void)strcpy(new_categories[i], current_categories[i]);
1211573Srgrimes
1221573Srgrimes	/*
1231573Srgrimes	 * Now go fill up new_categories from the locale argument
1241573Srgrimes	 */
1251573Srgrimes	if (!*locale) {
1261573Srgrimes		env = getenv(categories[category]);
1271573Srgrimes
1281573Srgrimes		if (category != LC_ALL && (!env || !*env))
1291573Srgrimes			env = getenv(categories[LC_ALL]);
1301573Srgrimes
1311573Srgrimes		if (!env || !*env)
1321573Srgrimes			env = getenv("LANG");
1331573Srgrimes
1341573Srgrimes		if (!env || !*env)
1359272Shsu			env = "C";
136198053Sjilles
137198053Sjilles		(void) strncpy(new_categories[category], env, ENCODING_LEN);
1381573Srgrimes		new_categories[category][ENCODING_LEN] = '\0';
1391573Srgrimes		if (category == LC_ALL) {
1401573Srgrimes			for (i = 1; i < _LC_LAST; ++i) {
1411573Srgrimes				if (!(env = getenv(categories[i])) || !*env)
142198053Sjilles					env = new_categories[LC_ALL];
143198053Sjilles				(void)strncpy(new_categories[i], env, ENCODING_LEN);
144198053Sjilles				new_categories[i][ENCODING_LEN] = '\0';
1451573Srgrimes			}
146198053Sjilles		}
147198053Sjilles	} else if (category != LC_ALL)  {
148198053Sjilles		(void)strncpy(new_categories[category], locale, ENCODING_LEN);
149198053Sjilles		new_categories[category][ENCODING_LEN] = '\0';
150198053Sjilles	} else {
151198053Sjilles		if ((r = strchr(locale, '/')) == NULL) {
1521573Srgrimes			for (i = 1; i < _LC_LAST; ++i) {
1531573Srgrimes				(void)strncpy(new_categories[i], locale, ENCODING_LEN);
1541573Srgrimes				new_categories[i][ENCODING_LEN] = '\0';
1551573Srgrimes			}
1561573Srgrimes		} else {
1571573Srgrimes			for (i = 1; r[1] == '/'; ++r);
1581573Srgrimes			if (!r[1])
1591573Srgrimes				return (NULL);	/* Hmm, just slashes... */
1601573Srgrimes			do {
1611573Srgrimes				len = r - locale > ENCODING_LEN ? ENCODING_LEN : r - locale;
1621573Srgrimes				(void)strncpy(new_categories[i], locale, len);
1631573Srgrimes				new_categories[i][len] = '\0';
1641573Srgrimes				i++;
1651573Srgrimes				locale = r;
1661573Srgrimes				while (*locale == '/')
1671573Srgrimes				    ++locale;
1681573Srgrimes				while (*++r && *r != '/');
1691573Srgrimes			} while (*locale);
1701573Srgrimes			while (i < _LC_LAST)
1711573Srgrimes				(void)strcpy(new_categories[i],
1721573Srgrimes				    new_categories[i-1]);
1731573Srgrimes		}
174198053Sjilles	}
175198053Sjilles
1761573Srgrimes	if (category)
1771573Srgrimes		return (loadlocale(category));
1781573Srgrimes
1791573Srgrimes	for (i = 1; i < _LC_LAST; ++i) {
1801573Srgrimes		(void)strcpy(saved_categories[i], current_categories[i]);
1811573Srgrimes		if (loadlocale(i) == NULL) {
1821573Srgrimes			for (j = 1; j < i; j++) {
1831573Srgrimes				(void)strcpy(new_categories[j],
1841573Srgrimes				     saved_categories[j]);
1851573Srgrimes				/* XXX can fail too */
1861573Srgrimes				(void)loadlocale(j);
1871573Srgrimes			}
1881573Srgrimes			return (NULL);
189150297Sache		}
1901573Srgrimes	}
1911573Srgrimes	return (currentlocale());
1921573Srgrimes}
1931573Srgrimes
1941573Srgrimesstatic char *
1951573Srgrimescurrentlocale()
1961573Srgrimes{
1971573Srgrimes	int i;
198109040Stjr
1991573Srgrimes	(void)strcpy(current_locale_string, current_categories[1]);
2001573Srgrimes
2011573Srgrimes	for (i = 2; i < _LC_LAST; ++i)
2029272Shsu		if (strcmp(current_categories[1], current_categories[i])) {
2031573Srgrimes			(void) strcpy(current_locale_string, current_categories[1]);
2041573Srgrimes			(void) strcat(current_locale_string, "/");
2051573Srgrimes			(void) strcat(current_locale_string, current_categories[2]);
2061573Srgrimes			(void) strcat(current_locale_string, "/");
2071573Srgrimes			(void) strcat(current_locale_string, current_categories[3]);
2081573Srgrimes			(void) strcat(current_locale_string, "/");
2091573Srgrimes			(void) strcat(current_locale_string, current_categories[4]);
2101573Srgrimes			(void) strcat(current_locale_string, "/");
2111573Srgrimes			(void) strcat(current_locale_string, current_categories[5]);
2121573Srgrimes			break;
2131573Srgrimes		}
2141573Srgrimes	return (current_locale_string);
2151573Srgrimes}
2161573Srgrimes
2171573Srgrimesstatic char *
2181573Srgrimesloadlocale(category)
2191573Srgrimes	int category;
2201573Srgrimes{
22132530Smckay	char *ret;
22232530Smckay	char *new = new_categories[category];
2231573Srgrimes	char *old = current_categories[category];
2241573Srgrimes
22528235Sdg	if (_PathLocale == NULL) {
22628235Sdg		char *p = getenv("PATH_LOCALE");
22732530Smckay
22832530Smckay		if (p != NULL
2291573Srgrimes#ifndef __NETBSD_SYSCALLS
2301573Srgrimes			&& !issetugid()
231#endif
232			) {
233			if (strlen(p) + 1/*"/"*/ + ENCODING_LEN +
234			    1/*"/"*/ + CATEGORY_LEN >= PATH_MAX)
235				return (NULL);
236			_PathLocale = strdup(p);
237			if (_PathLocale == NULL)
238				return (NULL);
239		} else
240			_PathLocale = _PATH_LOCALE;
241	}
242
243	if (strcmp(new, old) == 0)
244		return (old);
245
246	if (category == LC_CTYPE) {
247#ifdef XPG4
248		ret = _xpg4_setrunelocale(new) ? NULL : new;
249#else
250		ret = setrunelocale(new) ? NULL : new;
251#endif
252		if (!ret) {
253#ifdef XPG4
254			(void)_xpg4_setrunelocale(old);
255#else
256			(void)setrunelocale(old);
257#endif
258		} else
259			(void)strcpy(old, new);
260		return (ret);
261	}
262
263	if (category == LC_COLLATE) {
264		ret = (__collate_load_tables(new) < 0) ? NULL : new;
265		if (!ret)
266			(void)__collate_load_tables(old);
267		else
268			(void)strcpy(old, new);
269		return (ret);
270	}
271
272	if (category == LC_TIME) {
273		ret = (__time_load_locale(new) < 0) ? NULL : new;
274		if (!ret)
275			(void)__time_load_locale(old);
276		else
277			(void)strcpy(old, new);
278		return (ret);
279	}
280
281	if (category == LC_MONETARY || category == LC_NUMERIC) {
282		ret = stub_load_locale(new) ? NULL : new;
283		if (!ret)
284			(void)stub_load_locale(old);
285		else
286			(void)strcpy(old, new);
287		return (ret);
288	}
289
290	/* Just in case...*/
291	return (NULL);
292}
293
294static int
295stub_load_locale(encoding)
296const char *encoding;
297{
298	char name[PATH_MAX];
299	struct stat st;
300
301	if (!encoding)
302		return(1);
303	/*
304	 * The "C" and "POSIX" locale are always here.
305	 */
306	if (!strcmp(encoding, "C") || !strcmp(encoding, "POSIX"))
307		return(0);
308	if (!_PathLocale)
309		return(1);
310	/* Range checking not needed, encoding has fixed size */
311	strcpy(name, _PathLocale);
312	strcat(name, "/");
313	strcat(name, encoding);
314#if 0
315	/*
316	 * Some day we will actually look at this file.
317	 */
318#endif
319	return (stat(name, &st) != 0 || !S_ISDIR(st.st_mode));
320}
321