Update for locale rewrite.
[kopensolaris-gnu/glibc.git] / locale / newlocale.c
1 /* Return a reference to locale information record.
2    Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <argz.h>
22 #include <errno.h>
23 #include <locale.h>
24 #include <stdlib.h>
25
26 #include "localeinfo.h"
27
28
29 /* Constant data defined in setlocale.c.  */
30 extern struct locale_data *const _nl_C[];
31
32 /* Use this when we come along an error.  */
33 #define ERROR_RETURN                                                          \
34   do {                                                                        \
35     __set_errno (EINVAL);                                                     \
36     return NULL;                                                              \
37   } while (0)
38
39
40 __locale_t
41 __newlocale (int category_mask, const char *locale, __locale_t base)
42 {
43   /* Intermediate memory for result.  */
44   const char *newnames[__LC_LAST];
45   struct __locale_struct result;
46   __locale_t result_ptr;
47   char *locale_path;
48   size_t locale_path_len;
49   const char *locpath_var;
50   int cnt;
51
52   /* We treat LC_ALL in the same way as if all bits were set.  */
53   if (category_mask == LC_ALL)
54     category_mask = (1 << __LC_LAST) - 1 - (1 << LC_ALL);
55
56   /* Sanity check for CATEGORY argument.  */
57   if ((category_mask & ~((1 << LC_ALL) - 1 - (1 << LC_ALL))) != 0)
58     ERROR_RETURN;
59
60   /* `newlocale' does not support asking for the locale name. */
61   if (locale == NULL)
62     ERROR_RETURN;
63
64   /* Allocate memory for the result.  */
65   if (base != NULL)
66     {
67       if (base != NULL)
68         return base;
69
70       result = *base;
71     }
72   else
73     {
74       /* Fill with pointers to C locale data to .  */
75       for (cnt = 0; cnt < __LC_LAST; ++cnt)
76         if (cnt != LC_ALL)
77           result.__locales[cnt] = _nl_C[cnt];
78
79       /* If no category is to be set we return BASE if available or a
80          dataset using the C locale data.  */
81       if (category_mask == 0)
82         {
83           result_ptr = (__locale_t) malloc (sizeof (struct __locale_struct));
84           *result_ptr = result;
85
86           goto update;
87         }
88     }
89
90   /* We perhaps really have to load some data.  So we determine the
91      path in which to look for the data now.  The environment variable
92      `LOCPATH' must only be used when the binary has no SUID or SGID
93      bit set.  */
94   locale_path = NULL;
95   locale_path_len = 0;
96
97   locpath_var = __secure_getenv ("LOCPATH");
98   if (locpath_var != NULL && locpath_var[0] != '\0')
99     if (__argz_create_sep (locpath_var, ':',
100                            &locale_path, &locale_path_len) != 0)
101       return NULL;
102
103   if (__argz_append (&locale_path, &locale_path_len,
104                      LOCALE_PATH, sizeof (LOCALE_PATH)) != 0)
105     return NULL;
106
107   /* Get the names for the locales we are interested in.  We either
108      allow a composite name or a single name.  */
109   for (cnt = 0; cnt < __LC_LAST; ++cnt)
110     if (cnt != LC_ALL)
111       newnames[cnt] = locale;
112   if (strchr (locale, ';') != NULL)
113     {
114       /* This is a composite name.  Make a copy and split it up.  */
115       char *np = strdupa (locale);
116       char *cp;
117
118       while ((cp = strchr (np, '=')) != NULL)
119         {
120           for (cnt = 0; cnt < __LC_LAST; ++cnt)
121             if (cnt != LC_ALL
122                 && (size_t) (cp - np) == _nl_category_name_sizes[cnt]
123                 && memcmp (np, _nl_category_names[cnt], cp - np) == 0)
124               break;
125
126           if (cnt == __LC_LAST)
127             /* Bogus category name.  */
128             ERROR_RETURN;
129
130           /* Found the category this clause sets.  */
131           newnames[cnt] = ++cp;
132           cp = strchr (cp, ';');
133           if (cp != NULL)
134             {
135               /* Examine the next clause.  */
136               *cp = '\0';
137               np = cp + 1;
138             }
139           else
140             /* This was the last clause.  We are done.  */
141             break;
142         }
143
144       for (cnt = 0; cnt < __LC_LAST; ++cnt)
145         if (cnt != LC_ALL
146             && (category_mask & 1 << cnt) != 0 && newnames[cnt] == locale)
147           /* The composite name did not specify the category we need.  */
148           ERROR_RETURN;
149     }
150
151   /* Now process all categories we are interested in.  */
152   for (cnt = 0; cnt < __LC_LAST; ++cnt)
153     if (cnt != LC_ALL && (category_mask & 1 << cnt) != 0)
154       {
155         result.__locales[cnt] = _nl_find_locale (locale_path, locale_path_len,
156                                                  cnt, &newnames[cnt]);
157         if (result.__locales[cnt] == NULL)
158           return NULL;
159       }
160
161   /* We successfully loaded all required data.  */
162   if (base == NULL)
163     {
164       /* Allocate new structure.  */
165       result_ptr = (__locale_t) malloc (sizeof (struct __locale_struct));
166       if (result_ptr == NULL)
167         return NULL;
168
169       *result_ptr = result;
170     }
171   else
172     *(result_ptr = base) = result;
173
174   /* Update the special members.  */
175  update:
176   {
177     union locale_data_value *ctypes = result_ptr->__locales[LC_CTYPE]->values;
178   result_ptr->__ctype_b = (const unsigned short int *)
179     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_CLASS)] .string);
180 #if BYTE_ORDER == BIG_ENDIAN
181   result_ptr->__ctype_tolower = (const int *)
182     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOLOWER_EB)].string);
183   result_ptr->__ctype_toupper = (const int *)
184     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOUPPER_EB)].string);
185 #elif BYTE_ORDER == LITTLE_ENDIAN
186   result_ptr->__ctype_tolower = (const int *)
187     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOLOWER_EL)].string);
188   result_ptr->__ctype_toupper = (const int *)
189     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOUPPER_EL)].string);
190 #else
191 #error bizarre byte order
192 #endif
193   }
194
195   return result_ptr;
196 }