c0d6fcdc4e50811155009320aedde92ee3ca50c8
[kopensolaris-gnu/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_symbol (_nl_current_##category) weak_symbol (_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 our standard locale.  */
106 const char _nl_C_name[] = "C";
107
108 /* Name of current locale for each individual category.
109    Each is malloc'd unless it is nl_C_name.  */
110 static const char *_nl_current_names[] =
111   {
112 #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \
113     [category] = _nl_C_name,
114 #include "categories.def"
115 #undef  DEFINE_CATEGORY
116     [LC_ALL] = _nl_C_name               /* For LC_ALL.  */
117   };
118
119
120
121 /* Use this when we come along an error.  */
122 #define ERROR_RETURN                                                          \
123   do {                                                                        \
124     errno = EINVAL;                                                           \
125     return NULL;                                                              \
126   } while (0)
127
128
129 static inline char *
130 clever_copy (const char *string)
131 {
132   size_t len;
133   char *new;
134
135   if (strcmp (string, "C") == 0 || strcmp (string, "POSIX") == 0)
136     /* This return is dangerous because the returned string might be
137        placed in read-only memory.  But everything should be set up to
138        handle this case.  */
139     return (char *) _nl_C_name;
140
141   len = strlen (string) + 1;
142   new = (char *) malloc (len);
143   return new != NULL ? memcpy (new, string, len) : NULL;
144 }
145
146
147 /* Construct a new composite name.  */
148 static inline char *
149 new_composite_name (int category, char *newnames[LC_ALL])
150 {
151   size_t last_len;
152   size_t cumlen = 0;
153   int i;
154   char *new, *p;
155   int same = 1;
156
157   for (i = 0; i < LC_ALL; ++i)
158     {
159       char *name = (category == LC_ALL ? newnames[i] :
160                     category == i ? newnames[0] :
161                     (char *) _nl_current_names[i]);
162       last_len = strlen (name);
163       cumlen += _nl_category_name_sizes[i] + 1 + last_len + 1;
164       if (i > 0 && same && strcmp (name, newnames[0]) != 0)
165         same = 0;
166     }
167
168   if (same)
169     {
170       /* All the categories use the same name.  */
171       if (strcmp (newnames[0], "C") == 0 || strcmp (newnames[0], "POSIX") == 0)
172         return (char *) _nl_C_name;
173
174       new = malloc (last_len + 1);
175       if (new == NULL)
176         return NULL;
177
178       memcpy (new, newnames[0], last_len + 1);
179       return new;
180     }
181
182   new = malloc (cumlen);
183   if (new == NULL)
184     return NULL;
185   p = new;
186   for (i = 0; i < LC_ALL; ++i)
187     {
188       /* Add "CATEGORY=NAME;" to the string.  */
189       char *name = (category == LC_ALL ? newnames[i] :
190                     category == i ? newnames[0] :
191                     (char *) _nl_current_names[i]);
192       p = __stpcpy (p, _nl_category_names[i]);
193       *p++ = '=';
194       p = __stpcpy (p, name);
195       *p++ = ';';
196     }
197   p[-1] = '\0';         /* Clobber the last ';'.  */
198   return new;
199 }
200
201
202 /* Put NAME in _nl_current_names.  */
203 static inline void
204 setname (int category, const char *name)
205 {
206   if (_nl_current[category] == NULL
207       && _nl_current_names[category] != _nl_C_name)
208     free ((void *) _nl_current_names[category]);
209
210   _nl_current_names[category] = name;
211 }
212
213
214 /* Put DATA in *_nl_current[CATEGORY].  */
215 static inline void
216 setdata (int category, const struct locale_data *data)
217 {
218   if (_nl_current[category] != NULL)
219     {
220       *_nl_current[category] = data;
221       if (_nl_category_postload[category])
222         (*_nl_category_postload[category]) ();
223     }
224 }
225
226
227 char *
228 setlocale (int category, const char *locale)
229 {
230   char *locpath_var;
231   char *locale_path;
232   size_t locale_path_len;
233   char *composite;
234
235   /* Sanity check for CATEGORY argument.  */
236   if (category < 0 || category > LC_ALL)
237     ERROR_RETURN;
238
239   /* Does user want name of current locale?  */
240   if (locale == NULL)
241     return (char *) _nl_current_names[category];
242
243   if (strcmp (locale, _nl_current_names[category]) == 0)
244     /* Changing to the same thing.  */
245     return (char *) _nl_current_names[category];
246
247   /* We perhaps really have to load some data.  So we determine the
248      path in which to look for the data now.  But this environment
249      variable must only be used when the binary has no SUID or SGID
250      bit set.  */
251   locale_path = NULL;
252   locale_path_len = 0;
253
254   locpath_var = getenv ("LOCPATH");
255   if (locpath_var != NULL && locpath_var[0] != '\0'
256       && __getuid () == __geteuid () && __getgid () == __getegid ())
257     if (__argz_create_sep (locpath_var, ':',
258                            &locale_path, &locale_path_len) != 0)
259       return NULL;
260
261   if (__argz_append (&locale_path, &locale_path_len,
262                      LOCALE_PATH, sizeof (LOCALE_PATH)) != 0)
263     return NULL;
264   
265   if (category == LC_ALL)
266     {
267       /* The user wants to set all categories.  The desired locales
268          for the individual categories can be selected by using a
269          composite locale name.  This is a semi-colon separated list
270          of entries of the form `CATEGORY=VALUE'.  */
271       char *newnames[LC_ALL];
272       const struct locale_data *newdata[LC_ALL];
273
274       /* Set all name pointers to the argument name.  */
275       for (category = 0; category < LC_ALL; ++category)
276         newnames[category] = (char *) locale;
277       
278       if (strchr (locale, ';') != NULL)
279         {
280           /* This is a composite name.  Make a copy and split it up.  */
281           char *np = strdupa (locale);
282           char *cp;
283           int cnt;
284
285           while ((cp = strchr (np, '=')) != NULL)
286             {
287               for (cnt = 0; cnt < LC_ALL; ++cnt)
288                 if (cp - np == _nl_category_name_sizes[cnt]
289                     && memcmp (np, _nl_category_names[cnt], cp - np) == 0)
290                   break;
291
292               if (cnt == LC_ALL)
293                 /* Bogus category name.  */
294                 ERROR_RETURN;
295
296               /* Found the category this clause sets.  */
297               newnames[cnt] = ++cp;
298               cp = strchr (cp, ';');
299               if (cp != NULL)
300                 {
301                   /* Examine the next clause.  */
302                   *cp = '\0';
303                   np = cp + 1;
304                 }
305               else
306                 /* This was the last clause.  We are done.  */
307                 break;
308             }
309
310           for (cnt = 0; cnt < LC_ALL; ++cnt)
311             if (newnames[cnt] == locale)
312               /* The composite name did not specify all categories.  */
313               ERROR_RETURN;
314         }
315
316       /* Load the new data for each category.  */
317       while (category-- > 0)
318         /* Only actually load the data if anything will use it.  */
319         if (_nl_current[category] != NULL)
320           {
321             newdata[category] = _nl_find_locale (locale_path, locale_path_len,
322                                                  category,
323                                                  &newnames[category]);
324
325             if (newdata[category] == NULL)
326               {
327                 /* Loading this part of the locale failed.  Abort the
328                    composite load.  */
329                 int save_errno;
330               abort_composite:
331                 save_errno = errno;
332                 
333                 while (++category < LC_ALL)
334                   if (_nl_current[category] != NULL)
335                     _nl_free_locale (newdata[category]);
336                   else
337                     if (_nl_current[category] == NULL
338                         && newnames[category] != _nl_C_name)
339                       free (newnames[category]);
340
341                 errno = save_errno;
342                 return NULL;
343               }
344           }
345         else
346           {
347             /* The data is never used; just change the name.  */
348             newnames[category] = clever_copy (newnames[category]);
349             if (newnames[category] == NULL)
350               goto abort_composite;
351           }
352
353       /* Create new composite name.  */
354       composite = new_composite_name (LC_ALL, newnames);
355       if (composite == NULL)
356         {
357           category = -1;
358           goto abort_composite;
359         }
360
361       /* Now we have loaded all the new data.  Put it in place.  */
362       for (category = 0; category < LC_ALL; ++category)
363         {
364           setdata (category, newdata[category]);
365           setname (category, newnames[category]);
366         }
367       setname (LC_ALL, composite);
368
369       return composite;
370     }
371   else
372     {
373       const struct locale_data *newdata;
374       char *newname;
375
376       if (_nl_current[category] != NULL)
377         {
378           /* Only actually load the data if anything will use it.  */
379           newname = (char *) locale;
380           newdata = _nl_find_locale (locale_path, locale_path_len, category,
381                                      (char **) &newname);
382           if (newdata == NULL)
383             return NULL;
384         }
385
386       /* Create new composite name.  */
387       composite = new_composite_name (category, &newname);
388       if (composite == NULL)
389         {
390           /* If anything went wrong free what we managed to allocate
391              so far.  */
392           int save_errno = errno;
393
394           if (_nl_current[category] != NULL)
395             _nl_free_locale (newdata);
396
397           errno = save_errno;
398           return NULL;
399         }
400
401       if (_nl_current[category] != NULL)
402         setdata (category, newdata);
403
404       setname (category, newname);
405       setname (LC_ALL, composite);
406
407       return newname;
408     }
409 }