setlocale.c revision 19971
1193326Sed/*
2193326Sed * Copyright (c) 1991, 1993
3193326Sed *	The Regents of the University of California.  All rights reserved.
4193326Sed *
5193326Sed * This code is derived from software contributed to Berkeley by
6193326Sed * Paul Borman at Krystal Technologies.
7193326Sed *
8193326Sed * Redistribution and use in source and binary forms, with or without
9193326Sed * modification, are permitted provided that the following conditions
10193326Sed * are met:
11193326Sed * 1. Redistributions of source code must retain the above copyright
12193326Sed *    notice, this list of conditions and the following disclaimer.
13193326Sed * 2. Redistributions in binary form must reproduce the above copyright
14193326Sed *    notice, this list of conditions and the following disclaimer in the
15193326Sed *    documentation and/or other materials provided with the distribution.
16193326Sed * 3. All advertising materials mentioning features or use of this software
17193326Sed *    must display the following acknowledgement:
18252723Sdim *	This product includes software developed by the University of
19245431Sdim *	California, Berkeley and its contributors.
20207619Srdivacky * 4. Neither the name of the University nor the names of its contributors
21212904Sdim *    may be used to endorse or promote products derived from this software
22252723Sdim *    without specific prior written permission.
23226890Sdim *
24252723Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25252723Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26221345Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27252723Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28193326Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29193326Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30198092Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31235633Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32193326Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33193326Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34252723Sdim * SUCH DAMAGE.
35193326Sed *
36252723Sdim * $Id$
37252723Sdim */
38252723Sdim
39252723Sdim#if defined(LIBC_SCCS) && !defined(lint)
40245431Sdimstatic char sccsid[] = "@(#)setlocale.c	8.1 (Berkeley) 7/4/93";
41193326Sed#endif /* LIBC_SCCS and not lint */
42193326Sed
43252723Sdim#include <limits.h>
44193326Sed#include <locale.h>
45218893Sdim#include <rune.h>
46218893Sdim#include <stdlib.h>
47252723Sdim#include <string.h>
48252723Sdim#include <sys/stat.h>
49252723Sdim#include "collate.h"
50193326Sed
51207619Srdivacky/*
52226890Sdim * Category names for getenv()
53207619Srdivacky */
54245431Sdimstatic char *categories[_LC_LAST] = {
55245431Sdim    "LC_ALL",
56245431Sdim    "LC_COLLATE",
57245431Sdim    "LC_CTYPE",
58245431Sdim    "LC_MONETARY",
59245431Sdim    "LC_NUMERIC",
60245431Sdim    "LC_TIME",
61245431Sdim};
62245431Sdim
63245431Sdim/*
64252723Sdim * Current locales for each category
65252723Sdim */
66252723Sdimstatic char current_categories[_LC_LAST][32] = {
67252723Sdim    "C",
68245431Sdim    "C",
69252723Sdim    "C",
70252723Sdim    "C",
71252723Sdim    "C",
72245431Sdim    "C",
73245431Sdim};
74252723Sdim
75252723Sdim/*
76252723Sdim * The locales we are going to try and load
77252723Sdim */
78252723Sdimstatic char new_categories[_LC_LAST][32];
79252723Sdimstatic char saved_categories[_LC_LAST][32];
80245431Sdim
81245431Sdimstatic char current_locale_string[_LC_LAST * 33];
82245431Sdimchar *_PathLocale;
83245431Sdim
84245431Sdimstatic char	*currentlocale __P((void));
85245431Sdimstatic char	*loadlocale __P((int));
86245431Sdimstatic int      stub_load_locale __P((const char *));
87245431Sdim
88245431Sdimextern int __time_load_locale __P((const char *)); /* strftime.c */
89245431Sdim
90245431Sdim#ifdef XPG4
91245431Sdimextern int _xpg4_setrunelocale __P((char *));
92245431Sdim#endif
93245431Sdim
94245431Sdimchar *
95245431Sdimsetlocale(category, locale)
96245431Sdim	int category;
97245431Sdim	const char *locale;
98245431Sdim{
99193326Sed	int i, j, len;
100193326Sed	char *env, *r;
101193326Sed
102193326Sed	if (category < LC_ALL || category >= _LC_LAST)
103193326Sed		return (NULL);
104193326Sed
105193326Sed	if (!locale)
106193326Sed		return (category != LC_ALL ?
107218893Sdim		    current_categories[category] : currentlocale());
108235633Sdim
109235633Sdim	/*
110224145Sdim	 * Default to the current locale for everything.
111218893Sdim	 */
112218893Sdim	for (i = 1; i < _LC_LAST; ++i)
113224145Sdim		(void)strcpy(new_categories[i], current_categories[i]);
114218893Sdim
115218893Sdim	/*
116218893Sdim	 * Now go fill up new_categories from the locale argument
117193326Sed	 */
118193326Sed	if (!*locale) {
119193326Sed		env = getenv(categories[category]);
120193326Sed
121193326Sed		if (category != LC_ALL && (!env || !*env))
122193326Sed			env = getenv(categories[LC_ALL]);
123198092Srdivacky
124193326Sed		if (!env || !*env)
125198092Srdivacky			env = getenv("LANG");
126193326Sed
127193326Sed		if (!env || !*env)
128193326Sed			env = "C";
129193326Sed
130193326Sed		(void) strncpy(new_categories[category], env, 31);
131193326Sed		new_categories[category][31] = 0;
132193326Sed		if (category == LC_ALL) {
133199482Srdivacky			for (i = 1; i < _LC_LAST; ++i) {
134193326Sed				if (!(env = getenv(categories[i])) || !*env)
135193326Sed					env = new_categories[LC_ALL];
136198092Srdivacky				(void)strncpy(new_categories[i], env, 31);
137193326Sed				new_categories[i][31] = 0;
138193326Sed			}
139193326Sed		}
140193326Sed	} else if (category != LC_ALL)  {
141193326Sed		(void)strncpy(new_categories[category], locale, 31);
142198092Srdivacky		new_categories[category][31] = 0;
143193326Sed	} else {
144193326Sed		if ((r = strchr(locale, '/')) == 0) {
145193326Sed			for (i = 1; i < _LC_LAST; ++i) {
146218893Sdim				(void)strncpy(new_categories[i], locale, 31);
147193326Sed				new_categories[i][31] = 0;
148193326Sed			}
149235633Sdim		} else {
150235633Sdim			for (i = 1; r[1] == '/'; ++r);
151224145Sdim			if (!r[1])
152224145Sdim				return (NULL);	/* Hmm, just slashes... */
153224145Sdim			do {
154193326Sed				len = r - locale > 31 ? 31 : r - locale;
155193326Sed				(void)strncpy(new_categories[i++], locale, len);
156193326Sed				new_categories[i++][len] = 0;
157193326Sed				locale = r;
158193326Sed				while (*locale == '/')
159193326Sed				    ++locale;
160193326Sed				while (*++r && *r != '/');
161198092Srdivacky			} while (*locale);
162193326Sed			while (i < _LC_LAST)
163193326Sed				(void)strcpy(new_categories[i],
164193326Sed				    new_categories[i-1]);
165193326Sed		}
166218893Sdim	}
167193326Sed
168193326Sed	if (category)
169235633Sdim		return (loadlocale(category));
170235633Sdim
171224145Sdim	for (i = 1; i < _LC_LAST; ++i) {
172224145Sdim		(void)strcpy(saved_categories[i], current_categories[i]);
173224145Sdim		if (loadlocale(i) == NULL) {
174193326Sed			for (j = 1; j < i; j++) {
175224145Sdim				(void)strcpy(new_categories[j],
176224145Sdim				     saved_categories[j]);
177224145Sdim				/* XXX can fail too */
178224145Sdim				(void)loadlocale(j);
179224145Sdim			}
180224145Sdim			return (NULL);
181224145Sdim		}
182224145Sdim	}
183224145Sdim	return (currentlocale());
184224145Sdim}
185224145Sdim
186224145Sdim/* To be compatible with crt0 hack */
187224145Sdimvoid
188224145Sdim_startup_setlocale(category, locale)
189224145Sdim	int category;
190224145Sdim	const char *locale;
191224145Sdim{
192235633Sdim#ifndef XPG4
193235633Sdim	(void) setlocale(category, locale);
194224145Sdim#endif
195235633Sdim}
196224145Sdim
197235633Sdimstatic char *
198224145Sdimcurrentlocale()
199224145Sdim{
200224145Sdim	int i;
201218893Sdim
202252723Sdim	(void)strcpy(current_locale_string, current_categories[1]);
203218893Sdim
204218893Sdim	for (i = 2; i < _LC_LAST; ++i)
205218893Sdim		if (strcmp(current_categories[1], current_categories[i])) {
206218893Sdim			(void) strcpy(current_locale_string, current_categories[1]);
207218893Sdim			(void) strcat(current_locale_string, "/");
208218893Sdim			(void) strcat(current_locale_string, current_categories[2]);
209218893Sdim			(void) strcat(current_locale_string, "/");
210218893Sdim			(void) strcat(current_locale_string, current_categories[3]);
211218893Sdim			(void) strcat(current_locale_string, "/");
212218893Sdim			(void) strcat(current_locale_string, current_categories[4]);
213218893Sdim			(void) strcat(current_locale_string, "/");
214218893Sdim			(void) strcat(current_locale_string, current_categories[5]);
215235633Sdim			break;
216235633Sdim		}
217218893Sdim	return (current_locale_string);
218193326Sed}
219218893Sdim
220218893Sdimstatic char *
221218893Sdimloadlocale(category)
222218893Sdim	int category;
223218893Sdim{
224218893Sdim	char *ret;
225193326Sed	char *new = new_categories[category];
226193326Sed	char *old = current_categories[category];
227235633Sdim
228198092Srdivacky	if (strcmp(new, old) == 0)
229193326Sed		return (old);
230245431Sdim
231245431Sdim	if (   !_PathLocale
232245431Sdim	    && strcmp(new, "C") && strcmp(new, "POSIX")
233245431Sdim	   ) {
234245431Sdim		char *pl = getenv("PATH_LOCALE");
235245431Sdim
236198092Srdivacky		if (!pl)
237218893Sdim			_PathLocale = _PATH_LOCALE;
238218893Sdim		else if (   strlen(pl) + 45 > PATH_MAX
239218893Sdim			 || !(_PathLocale = strdup(pl))
240218893Sdim			)
241218893Sdim			return (NULL);
242218893Sdim	}
243193326Sed
244252723Sdim	if (category == LC_CTYPE) {
245218893Sdim#ifdef XPG4
246218893Sdim		ret = _xpg4_setrunelocale(new) ? NULL : new;
247218893Sdim#else
248218893Sdim		ret = setrunelocale(new) ? NULL : new;
249218893Sdim#endif
250218893Sdim		if (!ret) {
251218893Sdim#ifdef XPG4
252218893Sdim			(void)_xpg4_setrunelocale(old);
253218893Sdim#else
254218893Sdim			(void)setrunelocale(old);
255218893Sdim#endif
256193326Sed		} else
257193326Sed			(void)strcpy(old, new);
258193326Sed		return (ret);
259193326Sed	}
260193326Sed
261221345Sdim	if (category == LC_COLLATE) {
262201361Srdivacky		ret = (__collate_load_tables(new) < 0) ? NULL : new;
263203955Srdivacky		if (!ret)
264245431Sdim			(void)__collate_load_tables(old);
265245431Sdim		else
266193326Sed			(void)strcpy(old, new);
267218893Sdim		return (ret);
268218893Sdim	}
269193326Sed
270193326Sed	if (category == LC_TIME) {
271193326Sed		ret = (__time_load_locale(new) < 0) ? NULL : new;
272193326Sed		if (!ret)
273193326Sed			(void)__time_load_locale(old);
274193326Sed		else
275193326Sed			(void)strcpy(old, new);
276193326Sed		return (ret);
277193326Sed	}
278193326Sed
279193326Sed	if (category == LC_MONETARY || category == LC_NUMERIC) {
280193326Sed		ret = stub_load_locale(new) ? NULL : new;
281201361Srdivacky		if (!ret)
282203955Srdivacky			(void)stub_load_locale(old);
283221345Sdim		else
284245431Sdim			(void)strcpy(old, new);
285245431Sdim		return (ret);
286193326Sed	}
287245431Sdim
288245431Sdim	/* Just in case...*/
289245431Sdim	return (NULL);
290245431Sdim}
291245431Sdim
292245431Sdimstatic int
293245431Sdimstub_load_locale(encoding)
294245431Sdimconst char *encoding;
295245431Sdim{
296193326Sed	char name[PATH_MAX];
297193326Sed	struct stat st;
298198092Srdivacky
299252723Sdim	if (!encoding)
300210299Sed		return(1);
301210299Sed	/*
302210299Sed	 * The "C" and "POSIX" locale are always here.
303210299Sed	 */
304210299Sed	if (!strcmp(encoding, "C") || !strcmp(encoding, "POSIX"))
305210299Sed		return(0);
306210299Sed	if (!_PathLocale)
307210299Sed		return(1);
308210299Sed	strcpy(name, _PathLocale);
309221345Sdim	strcat(name, "/");
310210299Sed	strcat(name, encoding);
311210299Sed#if 0
312210299Sed	/*
313245431Sdim	 * Some day we will actually look at this file.
314245431Sdim	 */
315221345Sdim#endif
316210299Sed	return (stat(name, &st) != 0 || !S_ISDIR(st.st_mode));
317210299Sed}
318210299Sed