(repertoiremap_hash): Don't specify array size.
[kopensolaris-gnu/glibc.git] / locale / newlocale.c
1 /* Return a reference to locale information record.
2    Copyright (C) 1996, 1997, 1999, 2000 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_LAST) - 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     result = *base;
67   else
68     {
69       /* Fill with pointers to C locale data.  */
70       for (cnt = 0; cnt < __LC_LAST; ++cnt)
71         if (cnt != LC_ALL)
72           result.__locales[cnt] = _nl_C[cnt];
73     }
74
75   /* If no category is to be set we return BASE if available or a
76      dataset using the C locale data.  */
77   if (category_mask == 0)
78     {
79       result_ptr = (__locale_t) malloc (sizeof (struct __locale_struct));
80       if (result_ptr == NULL)
81         return NULL;
82       *result_ptr = result;
83
84       goto update;
85     }
86
87   /* We perhaps really have to load some data.  So we determine the
88      path in which to look for the data now.  The environment variable
89      `LOCPATH' must only be used when the binary has no SUID or SGID
90      bit set.  */
91   locale_path = NULL;
92   locale_path_len = 0;
93
94   locpath_var = __secure_getenv ("LOCPATH");
95   if (locpath_var != NULL && locpath_var[0] != '\0')
96     if (__argz_create_sep (locpath_var, ':',
97                            &locale_path, &locale_path_len) != 0)
98       return NULL;
99
100   if (__argz_append (&locale_path, &locale_path_len,
101                      LOCALE_PATH, sizeof (LOCALE_PATH)) != 0)
102     return NULL;
103
104   /* Get the names for the locales we are interested in.  We either
105      allow a composite name or a single name.  */
106   for (cnt = 0; cnt < __LC_LAST; ++cnt)
107     if (cnt != LC_ALL)
108       newnames[cnt] = locale;
109   if (strchr (locale, ';') != NULL)
110     {
111       /* This is a composite name.  Make a copy and split it up.  */
112       char *np = strdupa (locale);
113       char *cp;
114
115       while ((cp = strchr (np, '=')) != NULL)
116         {
117           for (cnt = 0; cnt < __LC_LAST; ++cnt)
118             if (cnt != LC_ALL
119                 && (size_t) (cp - np) == _nl_category_name_sizes[cnt]
120                 && memcmp (np, _nl_category_names[cnt], cp - np) == 0)
121               break;
122
123           if (cnt == __LC_LAST)
124             /* Bogus category name.  */
125             ERROR_RETURN;
126
127           /* Found the category this clause sets.  */
128           newnames[cnt] = ++cp;
129           cp = strchr (cp, ';');
130           if (cp != NULL)
131             {
132               /* Examine the next clause.  */
133               *cp = '\0';
134               np = cp + 1;
135             }
136           else
137             /* This was the last clause.  We are done.  */
138             break;
139         }
140
141       for (cnt = 0; cnt < __LC_LAST; ++cnt)
142         if (cnt != LC_ALL
143             && (category_mask & 1 << cnt) != 0 && newnames[cnt] == locale)
144           /* The composite name did not specify the category we need.  */
145           ERROR_RETURN;
146     }
147
148   /* Now process all categories we are interested in.  */
149   for (cnt = 0; cnt < __LC_LAST; ++cnt)
150     if (cnt != LC_ALL && (category_mask & 1 << cnt) != 0)
151       {
152         result.__locales[cnt] = _nl_find_locale (locale_path, locale_path_len,
153                                                  cnt, &newnames[cnt]);
154         if (result.__locales[cnt] == NULL)
155           return NULL;
156       }
157
158   /* We successfully loaded all required data.  */
159   if (base == NULL)
160     {
161       /* Allocate new structure.  */
162       result_ptr = (__locale_t) malloc (sizeof (struct __locale_struct));
163       if (result_ptr == NULL)
164         return NULL;
165
166       *result_ptr = result;
167     }
168   else
169     *(result_ptr = base) = result;
170
171   /* Update the special members.  */
172  update:
173   {
174     union locale_data_value *ctypes = result_ptr->__locales[LC_CTYPE]->values;
175     result_ptr->__ctype_b = (const unsigned short int *)
176       (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_CLASS)].string);
177     result_ptr->__ctype_tolower = (const int *)
178       (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOLOWER)].string);
179     result_ptr->__ctype_toupper = (const int *)
180       (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOUPPER)].string);
181   }
182
183   return result_ptr;
184 }