Sat May 4 05:44:25 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[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 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       if (new == NULL)
173         return NULL;
174
175       memcpy (new, newnames[0], last_len + 1);
176       return new;
177     }
178
179   new = malloc (cumlen);
180   if (new == NULL)
181     return NULL;
182   p = new;
183   for (i = 0; i < LC_ALL; ++i)
184     {
185       /* Add "CATEGORY=NAME;" to the string.  */
186       char *name = (category == LC_ALL ? newnames[i] :
187                     category == i ? newnames[0] :
188                     (char *) _nl_current_names[i]);
189       p = __stpcpy (p, _nl_category_names[i]);
190       *p++ = '=';
191       p = __stpcpy (p, name);
192       *p++ = ';';
193     }
194   p[-1] = '\0';         /* Clobber the last ';'.  */
195   return new;
196 }
197
198
199 /* Put NAME in _nl_current_names.  */
200 static inline void
201 setname (int category, const char *name)
202 {
203   if (_nl_current[category] == NULL
204       && _nl_current_names[category] != _nl_C_name)
205     free ((void *) _nl_current_names[category]);
206
207   _nl_current_names[category] = name;
208 }
209
210
211 /* Put DATA in *_nl_current[CATEGORY].  */
212 static inline void
213 setdata (int category, const struct locale_data *data)
214 {
215   if (_nl_current[category] != NULL)
216     {
217       *_nl_current[category] = data;
218       if (_nl_category_postload[category])
219         (*_nl_category_postload[category]) ();
220     }
221 }
222
223
224 char *
225 setlocale (int category, const char *locale)
226 {
227   char *locpath_var;
228   char *locale_path;
229   size_t locale_path_len;
230   char *composite;
231
232   /* Sanity check for CATEGORY argument.  */
233   if (category < 0 || category > LC_ALL)
234     ERROR_RETURN;
235
236   /* Does user want name of current locale?  */
237   if (locale == NULL)
238     return (char *) _nl_current_names[category];
239
240   if (strcmp (locale, _nl_current_names[category]) == 0)
241     /* Changing to the same thing.  */
242     return (char *) _nl_current_names[category];
243
244   /* We perhaps really have to load some data.  So we determine the
245      path in which to look for the data now.  But this environment
246      variable must only be used when the binary has no SUID or SGID
247      bit set.  */
248   locale_path = NULL;
249   locale_path_len = 0;
250
251   locpath_var = getenv ("LOCPATH");
252   if (locpath_var != NULL && locpath_var[0] != '\0'
253       && __getuid () == __geteuid () && __getgid () == __getegid ())
254     if (__argz_create_sep (locpath_var, ':',
255                            &locale_path, &locale_path_len) != 0)
256       return NULL;
257
258   if (__argz_append (&locale_path, &locale_path_len,
259                      LOCALE_PATH, sizeof (LOCALE_PATH)) != 0)
260     return NULL;
261
262   if (category == LC_ALL)
263     {
264       /* The user wants to set all categories.  The desired locales
265          for the individual categories can be selected by using a
266          composite locale name.  This is a semi-colon separated list
267          of entries of the form `CATEGORY=VALUE'.  */
268       char *newnames[LC_ALL];
269       const struct locale_data *newdata[LC_ALL];
270
271       /* Set all name pointers to the argument name.  */
272       for (category = 0; category < LC_ALL; ++category)
273         newnames[category] = (char *) locale;
274
275       if (strchr (locale, ';') != NULL)
276         {
277           /* This is a composite name.  Make a copy and split it up.  */
278           char *np = strdupa (locale);
279           char *cp;
280           int cnt;
281
282           while ((cp = strchr (np, '=')) != NULL)
283             {
284               for (cnt = 0; cnt < LC_ALL; ++cnt)
285                 if (cp - np == _nl_category_name_sizes[cnt]
286                     && memcmp (np, _nl_category_names[cnt], cp - np) == 0)
287                   break;
288
289               if (cnt == LC_ALL)
290                 /* Bogus category name.  */
291                 ERROR_RETURN;
292
293               /* Found the category this clause sets.  */
294               newnames[cnt] = ++cp;
295               cp = strchr (cp, ';');
296               if (cp != NULL)
297                 {
298                   /* Examine the next clause.  */
299                   *cp = '\0';
300                   np = cp + 1;
301                 }
302               else
303                 /* This was the last clause.  We are done.  */
304                 break;
305             }
306
307           for (cnt = 0; cnt < LC_ALL; ++cnt)
308             if (newnames[cnt] == locale)
309               /* The composite name did not specify all categories.  */
310               ERROR_RETURN;
311         }
312
313       /* Load the new data for each category.  */
314       while (category-- > 0)
315         /* Only actually load the data if anything will use it.  */
316         if (_nl_current[category] != NULL)
317           {
318             newdata[category] = _nl_find_locale (locale_path, locale_path_len,
319                                                  category,
320                                                  &newnames[category]);
321
322             if (newdata[category] == NULL)
323               {
324                 /* Loading this part of the locale failed.  Abort the
325                    composite load.  */
326                 int save_errno;
327               abort_composite:
328                 save_errno = errno;
329
330                 while (++category < LC_ALL)
331                   if (_nl_current[category] != NULL)
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;
371       char *newname;
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 }