8bf58da608964d123f1024505f05c27c42ccd7f7
[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_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 *locale_path;
225   size_t locale_path_len;
226   char *composite;
227
228   /* Sanity check for CATEGORY argument.  */
229   if (category < 0 || category > LC_ALL)
230     ERROR_RETURN;
231
232   /* Does user want name of current locale?  */
233   if (locale == NULL)
234     return (char *) _nl_current_names[category];
235
236   if (strcmp (locale, _nl_current_names[category]) == 0)
237     /* Changing to the same thing.  */
238     return (char *) _nl_current_names[category];
239
240   /* We perhaps really have to load some data.  So we determine the
241      path in which to look for the data now.  But this environment
242      variable must only be used when the binary has no SUID or SGID
243      bit set.  */
244   locale_path = NULL;
245   locale_path_len = 0;
246
247   if (!__libc_enable_secure)
248     {
249       char *locpath_var = getenv ("LOCPATH");
250
251       if (locpath_var != NULL && locpath_var[0] != '\0')
252         if (__argz_create_sep (locpath_var, ':',
253                                &locale_path, &locale_path_len) != 0)
254           return NULL;
255     }
256
257   if (__argz_append (&locale_path, &locale_path_len,
258                      LOCALE_PATH, sizeof (LOCALE_PATH)) != 0)
259     return NULL;
260
261   if (category == LC_ALL)
262     {
263       /* The user wants to set all categories.  The desired locales
264          for the individual categories can be selected by using a
265          composite locale name.  This is a semi-colon separated list
266          of entries of the form `CATEGORY=VALUE'.  */
267       char *newnames[LC_ALL];
268       const struct locale_data *newdata[LC_ALL];
269
270       /* Set all name pointers to the argument name.  */
271       for (category = 0; category < LC_ALL; ++category)
272         newnames[category] = (char *) locale;
273
274       if (strchr (locale, ';') != NULL)
275         {
276           /* This is a composite name.  Make a copy and split it up.  */
277           char *np = strdupa (locale);
278           char *cp;
279           int cnt;
280
281           while ((cp = strchr (np, '=')) != NULL)
282             {
283               for (cnt = 0; cnt < LC_ALL; ++cnt)
284                 if ((size_t) (cp - np) == _nl_category_name_sizes[cnt]
285                     && memcmp (np, _nl_category_names[cnt], cp - np) == 0)
286                   break;
287
288               if (cnt == LC_ALL)
289                 /* Bogus category name.  */
290                 ERROR_RETURN;
291
292               /* Found the category this clause sets.  */
293               newnames[cnt] = ++cp;
294               cp = strchr (cp, ';');
295               if (cp != NULL)
296                 {
297                   /* Examine the next clause.  */
298                   *cp = '\0';
299                   np = cp + 1;
300                 }
301               else
302                 /* This was the last clause.  We are done.  */
303                 break;
304             }
305
306           for (cnt = 0; cnt < LC_ALL; ++cnt)
307             if (newnames[cnt] == locale)
308               /* The composite name did not specify all categories.  */
309               ERROR_RETURN;
310         }
311
312       /* Load the new data for each category.  */
313       while (category-- > 0)
314         /* Only actually load the data if anything will use it.  */
315         if (_nl_current[category] != NULL)
316           {
317             newdata[category] = _nl_find_locale (locale_path, locale_path_len,
318                                                  category,
319                                                  &newnames[category]);
320
321             if (newdata[category] == NULL)
322               {
323                 /* Loading this part of the locale failed.  Abort the
324                    composite load.  */
325                 int save_errno;
326               abort_composite:
327                 save_errno = errno;
328
329                 while (++category < LC_ALL)
330                   if (_nl_current[category] != NULL
331                       && newdata[category] != _nl_C[category])
332                     _nl_free_locale (newdata[category]);
333                   else
334                     if (_nl_current[category] == NULL
335                         && newnames[category] != _nl_C_name)
336                       free (newnames[category]);
337
338                 errno = save_errno;
339                 return NULL;
340               }
341           }
342         else
343           {
344             /* The data is never used; just change the name.  */
345             newnames[category] = clever_copy (newnames[category]);
346             if (newnames[category] == NULL)
347               goto abort_composite;
348           }
349
350       /* Create new composite name.  */
351       composite = new_composite_name (LC_ALL, newnames);
352       if (composite == NULL)
353         {
354           category = -1;
355           goto abort_composite;
356         }
357
358       /* Now we have loaded all the new data.  Put it in place.  */
359       for (category = 0; category < LC_ALL; ++category)
360         {
361           setdata (category, newdata[category]);
362           setname (category, newnames[category]);
363         }
364       setname (LC_ALL, composite);
365
366       return composite;
367     }
368   else
369     {
370       const struct locale_data *newdata = NULL;
371       char *newname = NULL;
372
373       if (_nl_current[category] != NULL)
374         {
375           /* Only actually load the data if anything will use it.  */
376           newname = (char *) locale;
377           newdata = _nl_find_locale (locale_path, locale_path_len, category,
378                                      (char **) &newname);
379           if (newdata == NULL)
380             return NULL;
381         }
382
383       /* Create new composite name.  */
384       composite = new_composite_name (category, &newname);
385       if (composite == NULL)
386         {
387           /* If anything went wrong free what we managed to allocate
388              so far.  */
389           int save_errno = errno;
390
391           if (_nl_current[category] != NULL)
392             _nl_free_locale (newdata);
393
394           errno = save_errno;
395           return NULL;
396         }
397
398       if (_nl_current[category] != NULL)
399         setdata (category, newdata);
400
401       setname (category, newname);
402       setname (LC_ALL, composite);
403
404       return newname;
405     }
406 }