xref: /netbsd-src/lib/libc/locale/setlocale.c (revision 481fca6e59249d8ffcf24fef7cfbe7b131bfb080)
1 /*	$NetBSD: setlocale.c,v 1.17 1999/10/15 17:17:07 jdolecek Exp $	*/
2 
3 /*
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Paul Borman at Krystal Technologies.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #if defined(LIBC_SCCS) && !defined(lint)
41 #if 0
42 static char sccsid[] = "@(#)setlocale.c	8.1 (Berkeley) 7/4/93";
43 #else
44 __RCSID("$NetBSD: setlocale.c,v 1.17 1999/10/15 17:17:07 jdolecek Exp $");
45 #endif
46 #endif /* LIBC_SCCS and not lint */
47 
48 #define _CTYPE_PRIVATE
49 
50 #include "namespace.h"
51 #include <sys/localedef.h>
52 #include <ctype.h>
53 #include <limits.h>
54 #include <locale.h>
55 #include <paths.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include "ctypeio.h"
60 
61 /*
62  * Category names for getenv()
63  */
64 static const char *const 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][32] = {
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][32];
91 
92 static char current_locale_string[_LC_LAST * 33];
93 static char *PathLocale;
94 
95 static char	*currentlocale __P((void));
96 static char	*loadlocale __P((int));
97 
98 char *
99 setlocale(category, locale)
100 	int category;
101 	const char *locale;
102 {
103 	int i;
104 	size_t len;
105 	char *env, *r;
106 
107 	/*
108 	 * XXX potential security problem here with set-id programs
109 	 * being able to read files the user can not normally read.
110 	 */
111 	if (!PathLocale && !(PathLocale = getenv("PATH_LOCALE")))
112 		PathLocale = _PATH_LOCALE;
113 
114 	if (category < 0 || category >= _LC_LAST)
115 		return (NULL);
116 
117 	if (!locale)
118 		return (category ?
119 		    current_categories[category] : currentlocale());
120 
121 	/*
122 	 * Default to the current locale for everything.
123 	 */
124 	for (i = 1; i < _LC_LAST; ++i)
125 		(void)strncpy(new_categories[i], current_categories[i],
126 		    sizeof(new_categories[i]) - 1);
127 
128 	/*
129 	 * Now go fill up new_categories from the locale argument
130 	 */
131 	if (!*locale) {
132 		env = getenv(categories[category]);
133 
134 		if (!env || !*env)
135 			env = getenv(categories[0]);
136 
137 		if (!env || !*env)
138 			env = getenv("LANG");
139 
140 		if (!env || !*env)
141 			env = "C";
142 
143 		(void)strncpy(new_categories[category], env, 31);
144 		new_categories[category][31] = 0;
145 		if (!category) {
146 			for (i = 1; i < _LC_LAST; ++i) {
147 				if (!(env = getenv(categories[i])) || !*env)
148 					env = new_categories[0];
149 				(void)strncpy(new_categories[i], env, 31);
150 				new_categories[i][31] = 0;
151 			}
152 		}
153 	} else if (category) {
154 		(void)strncpy(new_categories[category], locale, 31);
155 		new_categories[category][31] = 0;
156 	} else {
157 		if ((r = strchr(locale, '/')) == 0) {
158 			for (i = 1; i < _LC_LAST; ++i) {
159 				(void)strncpy(new_categories[i], locale, 31);
160 				new_categories[i][31] = 0;
161 			}
162 		} else {
163 			for (i = 1; r[1] == '/'; ++r);
164 			if (!r[1])
165 				return (NULL);	/* Hmm, just slashes... */
166 			do {
167 				len = r - locale > 31 ? 31 : r - locale;
168 				(void)strncpy(new_categories[i++], locale, len);
169 				new_categories[i++][len] = 0;
170 				locale = r;
171 				while (*locale == '/')
172 				    ++locale;
173 				while (*++r && *r != '/');
174 			} while (*locale);
175 			while (i < _LC_LAST)
176 				(void)strncpy(new_categories[i],
177 				    new_categories[i-1],
178 				    sizeof(new_categories[i]) - 1);
179 		}
180 	}
181 
182 	if (category)
183 		return (loadlocale(category));
184 
185 	for (i = 1; i < _LC_LAST; ++i)
186 		(void) loadlocale(i);
187 
188 	return (currentlocale());
189 }
190 
191 static char *
192 currentlocale()
193 {
194 	int i;
195 
196 	(void)strncpy(current_locale_string, current_categories[1],
197 	    sizeof(current_locale_string) - 1);
198 
199 	for (i = 2; i < _LC_LAST; ++i)
200 		if (strcmp(current_categories[1], current_categories[i])) {
201 			(void)snprintf(current_locale_string,
202 			    sizeof(current_locale_string), "%s/%s/%s/%s/%s",
203 			    current_categories[1], current_categories[2],
204 			    current_categories[3], current_categories[4],
205 			    current_categories[5]);
206 			break;
207 		}
208 	return (current_locale_string);
209 }
210 
211 static char *
212 loadlocale(category)
213 	int category;
214 {
215 	char name[PATH_MAX];
216 
217 	if (strcmp(new_categories[category], current_categories[category]) == 0)
218 		return (current_categories[category]);
219 
220 	if (!strcmp(new_categories[category], "C") ||
221 	    !strcmp(new_categories[category], "POSIX")) {
222 
223 		switch (category) {
224 		case LC_CTYPE:
225 			if (_ctype_ != _C_ctype_) {
226 				/* LINTED const castaway */
227 				free((void *)_ctype_);
228 				_ctype_ = _C_ctype_;
229 			}
230 			if (_toupper_tab_ != _C_toupper_) {
231 				/* LINTED const castaway */
232 				free((void *)_toupper_tab_);
233 				_toupper_tab_ = _C_toupper_;
234 			}
235 			if (_tolower_tab_ != _C_tolower_) {
236 				/* LINTED const castaway */
237 				free((void *)_tolower_tab_);
238 				_tolower_tab_ = _C_tolower_;
239 			}
240 		}
241 
242 		(void)strncpy(current_categories[category],
243 		    new_categories[category],
244 		    sizeof(current_categories[category]) - 1);
245 		return current_categories[category];
246 	}
247 
248 	/*
249 	 * Some day we will actually look at this file.
250 	 */
251 	(void)snprintf(name, sizeof(name), "%s/%s/%s",
252 	    PathLocale, new_categories[category], categories[category]);
253 
254 	switch (category) {
255 	case LC_CTYPE:
256 		if (__loadctype(name)) {
257 			(void)strncpy(current_categories[category],
258 			    new_categories[category],
259 			    sizeof(current_categories[category]) - 1);
260 			return current_categories[category];
261 		}
262 		return NULL;
263 
264 	case LC_COLLATE:
265 	case LC_MESSAGES:
266 	case LC_MONETARY:
267 	case LC_NUMERIC:
268 	case LC_TIME:
269 		return NULL;
270 	}
271 
272 	return NULL;
273 }
274