(options): short form os verbose is v.
[kopensolaris-gnu/glibc.git] / locale / newlocale.c
1 /* Return a reference to locale information record.
2    Copyright (C) 1996, 1997 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_ALL];
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_ALL) - 1;
55
56   /* Sanity check for CATEGORY argument.  */
57   if ((category_mask & ~(1 << LC_ALL) - 1) != 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_ALL; ++cnt)
76         result.__locales[cnt] = _nl_C[cnt];
77
78       /* If no category is to be set we return BASE if available or a
79          dataset using the C locale data.  */
80       if (category_mask == 0)
81         {
82           result_ptr = (__locale_t) malloc (sizeof (struct __locale_struct));
83           *result_ptr = result;
84
85           goto update;
86         }
87     }
88
89   /* We perhaps really have to load some data.  So we determine the
90      path in which to look for the data now.  The environment variable
91      `LOCPATH' must only be used when the binary has no SUID or SGID
92      bit set.  */
93   locale_path = NULL;
94   locale_path_len = 0;
95
96   locpath_var = __secure_getenv ("LOCPATH");
97   if (locpath_var != NULL && locpath_var[0] != '\0')
98     if (__argz_create_sep (locpath_var, ':',
99                            &locale_path, &locale_path_len) != 0)
100       return NULL;
101
102   if (__argz_append (&locale_path, &locale_path_len,
103                      LOCALE_PATH, sizeof (LOCALE_PATH)) != 0)
104     return NULL;
105
106   /* Get the names for the locales we are interested in.  We either
107      allow a composite name or a single name.  */
108   for (cnt = 0; cnt < LC_ALL; ++cnt)
109     newnames[cnt] = locale;
110   if (strchr (locale, ';') != NULL)
111     {
112       /* This is a composite name.  Make a copy and split it up.  */
113       char *np = strdupa (locale);
114       char *cp;
115
116       while ((cp = strchr (np, '=')) != NULL)
117         {
118           for (cnt = 0; cnt < LC_ALL; ++cnt)
119             if ((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_ALL)
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_ALL; ++cnt)
142         if ((category_mask & 1 << cnt) != 0 && newnames[cnt] == locale)
143           /* The composite name did not specify the category we need.  */
144           ERROR_RETURN;
145     }
146
147   /* Now process all categories we are interested in.  */
148   for (cnt = 0; cnt < LC_ALL; ++cnt)
149     if ((category_mask & 1 << cnt) != 0)
150       {
151         result.__locales[cnt] = _nl_find_locale (locale_path, locale_path_len,
152                                                  cnt, &newnames[cnt]);
153         if (result.__locales[cnt] == NULL)
154           return NULL;
155       }
156
157   /* We successfully loaded all required data.  */
158   if (base == NULL)
159     {
160       /* Allocate new structure.  */
161       result_ptr = (__locale_t) malloc (sizeof (struct __locale_struct));
162       if (result_ptr == NULL)
163         return NULL;
164
165       *result_ptr = result;
166     }
167   else
168     *(result_ptr = base) = result;
169
170   /* Update the special members.  */
171  update:
172   {
173     union locale_data_value *ctypes = result_ptr->__locales[LC_CTYPE]->values;
174   result_ptr->__ctype_b = (const unsigned short int *)
175     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_CLASS)] .string);
176 #if BYTE_ORDER == BIG_ENDIAN
177   result_ptr->__ctype_tolower = (const int *)
178     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOLOWER_EB)].string);
179   result_ptr->__ctype_toupper = (const int *)
180     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOUPPER_EB)].string);
181 #elif BYTE_ORDER == LITTLE_ENDIAN
182   result_ptr->__ctype_tolower = (const int *)
183     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOLOWER_EL)].string);
184   result_ptr->__ctype_toupper = (const int *)
185     (ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOUPPER_EL)].string);
186 #else
187 #error bizarre byte order
188 #endif
189   }
190
191   return result_ptr;
192 }