]> git.ipfire.org Git - thirdparty/glibc.git/blob - locale/setlocale.c
Sun Jul 28 23:46:14 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[thirdparty/glibc.git] / locale / setlocale.c
1 /* Copyright (C) 1991, 1992, 1995, 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
18
19 #include <alloca.h>
20 #include <argz.h>
21 #include <errno.h>
22 #include <locale.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "localeinfo.h"
28
29 /* For each category declare two external variables (with weak references):
30 extern const struct locale_data *_nl_current_CATEGORY;
31 This points to the current locale's in-core data for CATEGORY.
32 extern const struct locale_data _nl_C_CATEGORY;
33 This contains the built-in "C"/"POSIX" locale's data for CATEGORY.
34 Both are weak references; if &_nl_current_CATEGORY is zero,
35 then nothing is using the locale data. */
36 #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \
37 extern const struct locale_data *_nl_current_##category; \
38 extern const struct locale_data _nl_C_##category; \
39 weak_extern (_nl_current_##category) weak_extern (_nl_C_##category)
40 #include "categories.def"
41 #undef DEFINE_CATEGORY
42
43 /* Array indexed by category of pointers to _nl_current_CATEGORY slots.
44 Elements are zero for categories whose data is never used. */
45 static const struct locale_data * *const _nl_current[] =
46 {
47 #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \
48 [category] = &_nl_current_##category,
49 #include "categories.def"
50 #undef DEFINE_CATEGORY
51 };
52
53 /* Array indexed by category of pointers to _nl_C_CATEGORY slots.
54 Elements are zero for categories whose data is never used. */
55 const struct locale_data *const _nl_C[] =
56 {
57 #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \
58 [category] = &_nl_C_##category,
59 #include "categories.def"
60 #undef DEFINE_CATEGORY
61 };
62
63
64 /* Define an array of category names (also the environment variable names),
65 indexed by integral category. */
66 const char *const _nl_category_names[] =
67 {
68 #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \
69 [category] = category_name,
70 #include "categories.def"
71 #undef DEFINE_CATEGORY
72 [LC_ALL] = "LC_ALL"
73 };
74 /* An array of their lengths, for convenience. */
75 const size_t _nl_category_name_sizes[] =
76 {
77 #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \
78 [category] = sizeof (category_name) - 1,
79 #include "categories.def"
80 #undef DEFINE_CATEGORY
81 [LC_ALL] = sizeof ("LC_ALL") - 1
82 };
83
84
85 /* Declare the postload functions used below. */
86 #undef NO_POSTLOAD
87 #define NO_POSTLOAD _nl_postload_ctype /* Harmless thing known to exist. */
88 #define DEFINE_CATEGORY(category, category_name, items, postload, b, c, d) \
89 extern void postload (void);
90 #include "categories.def"
91 #undef DEFINE_CATEGORY
92 #undef NO_POSTLOAD
93
94 /* Define an array indexed by category of postload functions to call after
95 loading and installing that category's data. */
96 void (*const _nl_category_postload[]) (void) =
97 {
98 #define DEFINE_CATEGORY(category, category_name, items, postload, b, c, d) \
99 [category] = postload,
100 #include "categories.def"
101 #undef DEFINE_CATEGORY
102 };
103
104
105 /* Name of current locale for each individual category.
106 Each is malloc'd unless it is nl_C_name. */
107 static const char *_nl_current_names[] =
108 {
109 #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \
110 [category] = _nl_C_name,
111 #include "categories.def"
112 #undef DEFINE_CATEGORY
113 [LC_ALL] = _nl_C_name /* For LC_ALL. */
114 };
115
116
117
118 /* Use this when we come along an error. */
119 #define ERROR_RETURN \
120 do { \
121 errno = EINVAL; \
122 return NULL; \
123 } while (0)
124
125
126 static inline char *
127 clever_copy (const char *string)
128 {
129 size_t len;
130 char *new;
131
132 if (strcmp (string, "C") == 0 || strcmp (string, "POSIX") == 0)
133 /* This return is dangerous because the returned string might be
134 placed in read-only memory. But everything should be set up to
135 handle this case. */
136 return (char *) _nl_C_name;
137
138 len = strlen (string) + 1;
139 new = (char *) malloc (len);
140 return new != NULL ? memcpy (new, string, len) : NULL;
141 }
142
143
144 /* Construct a new composite name. */
145 static inline char *
146 new_composite_name (int category, char *newnames[LC_ALL])
147 {
148 size_t last_len;
149 size_t cumlen = 0;
150 int i;
151 char *new, *p;
152 int same = 1;
153
154 for (i = 0; i < LC_ALL; ++i)
155 {
156 char *name = (category == LC_ALL ? newnames[i] :
157 category == i ? newnames[0] :
158 (char *) _nl_current_names[i]);
159 last_len = strlen (name);
160 cumlen += _nl_category_name_sizes[i] + 1 + last_len + 1;
161 if (i > 0 && same && strcmp (name, newnames[0]) != 0)
162 same = 0;
163 }
164
165 if (same)
166 {
167 /* All the categories use the same name. */
168 if (strcmp (newnames[0], "C") == 0 || strcmp (newnames[0], "POSIX") == 0)
169 return (char *) _nl_C_name;
170
171 new = malloc (last_len + 1);
172
173 return new == NULL ? NULL : memcpy (new, newnames[0], last_len + 1);
174 }
175
176 new = malloc (cumlen);
177 if (new == NULL)
178 return NULL;
179 p = new;
180 for (i = 0; i < LC_ALL; ++i)
181 {
182 /* Add "CATEGORY=NAME;" to the string. */
183 char *name = (category == LC_ALL ? newnames[i] :
184 category == i ? newnames[0] :
185 (char *) _nl_current_names[i]);
186 p = __stpcpy (p, _nl_category_names[i]);
187 *p++ = '=';
188 p = __stpcpy (p, name);
189 *p++ = ';';
190 }
191 p[-1] = '\0'; /* Clobber the last ';'. */
192 return new;
193 }
194
195
196 /* Put NAME in _nl_current_names. */
197 static inline void
198 setname (int category, const char *name)
199 {
200 if (_nl_current[category] == NULL
201 && _nl_current_names[category] != _nl_C_name)
202 free ((void *) _nl_current_names[category]);
203
204 _nl_current_names[category] = name;
205 }
206
207
208 /* Put DATA in *_nl_current[CATEGORY]. */
209 static inline void
210 setdata (int category, const struct locale_data *data)
211 {
212 if (_nl_current[category] != NULL)
213 {
214 *_nl_current[category] = data;
215 if (_nl_category_postload[category])
216 (*_nl_category_postload[category]) ();
217 }
218 }
219
220
221 char *
222 setlocale (int category, const char *locale)
223 {
224 char *locpath_var;
225 char *locale_path;
226 size_t locale_path_len;
227 char *composite;
228
229 /* Sanity check for CATEGORY argument. */
230 if (category < 0 || category > LC_ALL)
231 ERROR_RETURN;
232
233 /* Does user want name of current locale? */
234 if (locale == NULL)
235 return (char *) _nl_current_names[category];
236
237 if (strcmp (locale, _nl_current_names[category]) == 0)
238 /* Changing to the same thing. */
239 return (char *) _nl_current_names[category];
240
241 /* We perhaps really have to load some data. So we determine the
242 path in which to look for the data now. But this environment
243 variable must only be used when the binary has no SUID or SGID
244 bit set. */
245 locale_path = NULL;
246 locale_path_len = 0;
247
248 locpath_var = getenv ("LOCPATH");
249 if (locpath_var != NULL && locpath_var[0] != '\0'
250 && __getuid () == __geteuid () && __getgid () == __getegid ())
251 if (__argz_create_sep (locpath_var, ':',
252 &locale_path, &locale_path_len) != 0)
253 return NULL;
254
255 if (__argz_append (&locale_path, &locale_path_len,
256 LOCALE_PATH, sizeof (LOCALE_PATH)) != 0)
257 return NULL;
258
259 if (category == LC_ALL)
260 {
261 /* The user wants to set all categories. The desired locales
262 for the individual categories can be selected by using a
263 composite locale name. This is a semi-colon separated list
264 of entries of the form `CATEGORY=VALUE'. */
265 char *newnames[LC_ALL];
266 const struct locale_data *newdata[LC_ALL];
267
268 /* Set all name pointers to the argument name. */
269 for (category = 0; category < LC_ALL; ++category)
270 newnames[category] = (char *) locale;
271
272 if (strchr (locale, ';') != NULL)
273 {
274 /* This is a composite name. Make a copy and split it up. */
275 char *np = strdupa (locale);
276 char *cp;
277 int cnt;
278
279 while ((cp = strchr (np, '=')) != NULL)
280 {
281 for (cnt = 0; cnt < LC_ALL; ++cnt)
282 if ((size_t) (cp - np) == _nl_category_name_sizes[cnt]
283 && memcmp (np, _nl_category_names[cnt], cp - np) == 0)
284 break;
285
286 if (cnt == LC_ALL)
287 /* Bogus category name. */
288 ERROR_RETURN;
289
290 /* Found the category this clause sets. */
291 newnames[cnt] = ++cp;
292 cp = strchr (cp, ';');
293 if (cp != NULL)
294 {
295 /* Examine the next clause. */
296 *cp = '\0';
297 np = cp + 1;
298 }
299 else
300 /* This was the last clause. We are done. */
301 break;
302 }
303
304 for (cnt = 0; cnt < LC_ALL; ++cnt)
305 if (newnames[cnt] == locale)
306 /* The composite name did not specify all categories. */
307 ERROR_RETURN;
308 }
309
310 /* Load the new data for each category. */
311 while (category-- > 0)
312 /* Only actually load the data if anything will use it. */
313 if (_nl_current[category] != NULL)
314 {
315 newdata[category] = _nl_find_locale (locale_path, locale_path_len,
316 category,
317 &newnames[category]);
318
319 if (newdata[category] == NULL)
320 {
321 /* Loading this part of the locale failed. Abort the
322 composite load. */
323 int save_errno;
324 abort_composite:
325 save_errno = errno;
326
327 while (++category < LC_ALL)
328 if (_nl_current[category] != NULL)
329 _nl_free_locale (newdata[category]);
330 else
331 if (_nl_current[category] == NULL
332 && newnames[category] != _nl_C_name)
333 free (newnames[category]);
334
335 errno = save_errno;
336 return NULL;
337 }
338 }
339 else
340 {
341 /* The data is never used; just change the name. */
342 newnames[category] = clever_copy (newnames[category]);
343 if (newnames[category] == NULL)
344 goto abort_composite;
345 }
346
347 /* Create new composite name. */
348 composite = new_composite_name (LC_ALL, newnames);
349 if (composite == NULL)
350 {
351 category = -1;
352 goto abort_composite;
353 }
354
355 /* Now we have loaded all the new data. Put it in place. */
356 for (category = 0; category < LC_ALL; ++category)
357 {
358 setdata (category, newdata[category]);
359 setname (category, newnames[category]);
360 }
361 setname (LC_ALL, composite);
362
363 return composite;
364 }
365 else
366 {
367 const struct locale_data *newdata = NULL;
368 char *newname = NULL;
369
370 if (_nl_current[category] != NULL)
371 {
372 /* Only actually load the data if anything will use it. */
373 newname = (char *) locale;
374 newdata = _nl_find_locale (locale_path, locale_path_len, category,
375 (char **) &newname);
376 if (newdata == NULL)
377 return NULL;
378 }
379
380 /* Create new composite name. */
381 composite = new_composite_name (category, &newname);
382 if (composite == NULL)
383 {
384 /* If anything went wrong free what we managed to allocate
385 so far. */
386 int save_errno = errno;
387
388 if (_nl_current[category] != NULL)
389 _nl_free_locale (newdata);
390
391 errno = save_errno;
392 return NULL;
393 }
394
395 if (_nl_current[category] != NULL)
396 setdata (category, newdata);
397
398 setname (category, newname);
399 setname (LC_ALL, composite);
400
401 return newname;
402 }
403 }