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