]>
Commit | Line | Data |
---|---|---|
19bc17a9 | 1 | /* Copyright (C) 1991, 1992, 1995, 1996 Free Software Foundation, Inc. |
28f540f4 RM |
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., 675 Mass Ave, | |
17 | Cambridge, 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) \ | |
34 | extern const struct locale_data *_nl_current_##category; \ | |
35 | extern 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. */ | |
43 | const 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. */ | |
53 | const 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. */ | |
64 | const 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. */ | |
72 | const 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) \ | |
85 | extern 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. */ | |
92 | void (*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 | ||
101 | const 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. */ | |
105 | const 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. */ | |
115 | char *_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 | 124 | char * |
933e73fa | 125 | setlocale (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 | } |