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