1 /** @file
2   Worker functions for the setlocale function.
3 
4   Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials are licensed and made available under
6   the terms and conditions of the BSD License that accompanies this distribution.
7   The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13  * Copyright (c) 1991, 1993
14  *  The Regents of the University of California.  All rights reserved.
15  *
16  * This code is derived from software contributed to Berkeley by
17  * Paul Borman at Krystal Technologies.
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  * 1. Redistributions of source code must retain the above copyright
23  *    notice, this list of conditions and the following disclaimer.
24  * 2. Redistributions in binary form must reproduce the above copyright
25  *    notice, this list of conditions and the following disclaimer in the
26  *    documentation and/or other materials provided with the distribution.
27  * 3. Neither the name of the University nor the names of its contributors
28  *    may be used to endorse or promote products derived from this software
29  *    without specific prior written permission.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
32  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
35  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41  * SUCH DAMAGE.
42 
43     setlocale.c 8.1 (Berkeley) 7/4/93
44  *  NetBSD: setlocale.c,v 1.50 2006/02/16 19:19:49 tnozaki Exp
45 **/
46 #include  <LibConfig.h>
47 #include  <sys/EfiCdefs.h>
48 
49 #if defined(_MSC_VER)
50   // Disable warnings about assignment within conditional expressions.
51   #pragma warning ( disable : 4706 )
52 #endif
53 
54 #define _CTYPE_PRIVATE
55 
56 #include  "namespace.h"
57 #include  <sys/localedef.h>
58 #include  <sys/types.h>
59 #include  <sys/stat.h>
60 #include  <assert.h>
61 #include  <limits.h>
62 #include  <ctype.h>
63 #define __SETLOCALE_SOURCE__
64 #include  <locale.h>
65 #include  <paths.h>
66 #include  <stdio.h>
67 #include  <stdlib.h>
68 #include  <string.h>
69 #include  <unistd.h>
70 #include  "rune.h"
71 #ifdef WITH_RUNE
72   #include "rune_local.h"
73 #else
74   #include "ctypeio.h"
75 #endif
76 
77 #ifdef CITRUS
78   #include <citrus/citrus_namespace.h>
79   #include <citrus/citrus_region.h>
80   #include <citrus/citrus_lookup.h>
81   #include <citrus/citrus_bcs.h>
82 #else
83   #include "aliasname_local.h"
84   #define _lookup_alias(p, a, b, s, c)  __unaliasname((p), (a), (b), (s))
85   #define _bcs_strcasecmp(a, b)   strcasecmp((a), (b))
86 #endif
87 
88 #define _LOCALE_SYM_FORCE "/force"
89 
90 #ifndef WITH_RUNE
91   const char *_PathLocale = NULL;
92 #endif
93 
94 /** Category names for getenv(). **/
95 static const char *const categories[_LC_LAST] = {
96     "LC_ALL",
97     "LC_COLLATE",
98     "LC_CTYPE",
99     "LC_MONETARY",
100     "LC_NUMERIC",
101     "LC_TIME",
102     "LC_MESSAGES"
103 };
104 
105 /** Current locales for each category.  **/
106 static char current_categories[_LC_LAST][32] = {
107     "C",
108     "C",
109     "C",
110     "C",
111     "C",
112     "C",
113     "C"
114 };
115 
116 /*
117  * The locales we are going to try and load
118  */
119 static char new_categories[_LC_LAST][32];
120 
121 static char current_locale_string[_LC_LAST * 33];
122 
123 static char *currentlocale(void);
124 static void revert_to_default(int);
125 static int force_locale_enable(int);
126 static int load_locale_sub(int, const char *, int);
127 static char *loadlocale(int);
128 static const char *__get_locale_env(int);
129 
130 char *
__setlocale(int category,const char * locale)131 __setlocale(int category, const char *locale)
132 {
133   int i, loadlocale_success;
134   size_t len;
135   const char *env, *r;
136 
137   //if (issetugid() ||
138   //    (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE"))))
139   //  _PathLocale = _PATH_LOCALE;
140 
141   if (category < 0 || category >= _LC_LAST)
142     return (NULL);
143 
144   if (!locale)
145     return (category ?
146         current_categories[category] : currentlocale());
147 
148   /*
149    * Default to the current locale for everything.
150    */
151   for (i = 1; i < _LC_LAST; ++i)
152     (void)strncpyX(new_categories[i], current_categories[i],
153         sizeof(new_categories[i]));
154 
155   /*
156    * Now go fill up new_categories from the locale argument
157    */
158   if (!*locale) {
159     if (category == LC_ALL) {
160       for (i = 1; i < _LC_LAST; ++i) {
161         env = __get_locale_env(i);
162         (void)strncpyX(new_categories[i], env,
163             sizeof(new_categories[i]));
164       }
165     }
166     else {
167       env = __get_locale_env(category);
168       (void)strncpyX(new_categories[category], env,
169         sizeof(new_categories[category]));
170     }
171   } else if (category) {
172     (void)strncpyX(new_categories[category], locale,
173         sizeof(new_categories[category]));
174   } else {
175     if ((r = strchr(locale, '/')) == 0) {
176       for (i = 1; i < _LC_LAST; ++i) {
177         (void)strncpyX(new_categories[i], locale,
178             sizeof(new_categories[i]));
179       }
180     } else {
181       for (i = 1;;) {
182         _DIAGASSERT(*r == '/' || *r == 0);
183         _DIAGASSERT(*locale != 0);
184         if (*locale == '/')
185           return (NULL);  /* invalid format. */
186         len = r - locale;
187         if (len + 1 > sizeof(new_categories[i]))
188           return (NULL);  /* too long */
189         (void)memcpy(new_categories[i], locale, len);
190         new_categories[i][len] = '\0';
191         if (*r == 0)
192           break;
193         _DIAGASSERT(*r == '/');
194         if (*(locale = ++r) == 0)
195           /* slash followed by NUL */
196           return (NULL);
197         /* skip until NUL or '/' */
198         while (*r && *r != '/')
199           r++;
200         if (++i == _LC_LAST)
201           return (NULL);  /* too many slashes. */
202       }
203       if (i + 1 != _LC_LAST)
204         return (NULL);  /* too few slashes. */
205     }
206   }
207 
208   if (category)
209     return (loadlocale(category));
210 
211   loadlocale_success = 0;
212   for (i = 1; i < _LC_LAST; ++i) {
213     if (loadlocale(i) != NULL)
214       loadlocale_success = 1;
215   }
216 
217   /*
218    * If all categories failed, return NULL; we don't need to back
219    * changes off, since none happened.
220    */
221   if (!loadlocale_success)
222     return NULL;
223 
224   return (currentlocale());
225 }
226 
227 static char *
currentlocale()228 currentlocale()
229 {
230   int i;
231 
232   (void)strncpyX(current_locale_string, current_categories[1],
233       sizeof(current_locale_string));
234 
235   for (i = 2; i < _LC_LAST; ++i)
236     if (strcmp(current_categories[1], current_categories[i])) {
237       (void)snprintf(current_locale_string,
238           sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s",
239           current_categories[1], current_categories[2],
240           current_categories[3], current_categories[4],
241           current_categories[5], current_categories[6]);
242       break;
243     }
244   return (current_locale_string);
245 }
246 
247 static void
revert_to_default(int category)248 revert_to_default(int category)
249 {
250   switch (category) {
251   case LC_CTYPE:
252 #ifdef WITH_RUNE
253     (void)_xpg4_setrunelocale("C");
254     (void)__runetable_to_netbsd_ctype("C");
255 #else
256     if (_cClass != _C_CharClassTable) {
257       /* LINTED const castaway */
258       free((void *)_cClass);
259       _cClass = _C_CharClassTable;
260     }
261     if (_uConvT != _C_ToUpperTable) {
262       /* LINTED const castaway */
263       free((void *)_uConvT);
264       _uConvT = _C_ToUpperTable;
265     }
266     if (_lConvT != _C_ToLowerTable) {
267       /* LINTED const castaway */
268       free((void *)_lConvT);
269       _lConvT = _C_ToLowerTable;
270     }
271 #endif
272     break;
273   case LC_MESSAGES:
274   case LC_COLLATE:
275   case LC_MONETARY:
276   case LC_NUMERIC:
277   case LC_TIME:
278     break;
279   }
280 }
281 
282 static int
force_locale_enable(int category)283 force_locale_enable(int category)
284 {
285   revert_to_default(category);
286 
287   return 0;
288 }
289 
290 static int
load_locale_sub(int category,const char * locname,int isspecial)291 load_locale_sub(
292   int category,
293   const char *locname,
294   int isspecial
295   )
296 {
297   char name[PATH_MAX];
298 
299   /* check for the default locales */
300   if (!strcmp(new_categories[category], "C") ||
301       !strcmp(new_categories[category], "POSIX")) {
302     revert_to_default(category);
303     return 0;
304   }
305 
306   /* check whether special symbol */
307   if (isspecial && _bcs_strcasecmp(locname, _LOCALE_SYM_FORCE) == 0)
308     return force_locale_enable(category);
309 
310   /* sanity check */
311   if (strchr(locname, '/') != NULL)
312     return -1;
313 
314   (void)snprintf(name, sizeof(name), "%s/%s/%s",
315            _PathLocale, locname, categories[category]);
316 
317   switch (category) {
318   case LC_CTYPE:
319 #ifdef WITH_RUNE
320     if (_xpg4_setrunelocale(__UNCONST(locname)))
321       return -1;
322     if (__runetable_to_netbsd_ctype(locname)) {
323       /* very unfortunate, but need to go to "C" locale */
324       revert_to_default(category);
325       return -1;
326     }
327 #else
328     if (!__loadctype(name))
329       return -1;
330 #endif
331     break;
332 
333   case LC_MESSAGES:
334     /*
335      * XXX we don't have LC_MESSAGES support yet,
336      * but catopen may use the value of LC_MESSAGES category.
337      * so return successfully if locale directory is present.
338      */
339     (void)snprintf(name, sizeof(name), "%s/%s",
340       _PathLocale, locname);
341     /* local */
342     {
343       struct stat st;
344       if (stat(name, &st) < 0)
345         return -1;
346       if (!S_ISDIR(st.st_mode))
347         return -1;
348     }
349     break;
350 
351   case LC_COLLATE:
352   case LC_MONETARY:
353   case LC_NUMERIC:
354   case LC_TIME:
355     return -1;
356   }
357 
358   return 0;
359 }
360 
361 static char *
loadlocale(int category)362 loadlocale(int category)
363 {
364   //char aliaspath[PATH_MAX], loccat[PATH_MAX], buf[PATH_MAX];
365   //const char *alias;
366 
367   _DIAGASSERT(0 < category && category < _LC_LAST);
368 
369   if (strcmp(new_categories[category], current_categories[category]) == 0)
370     return (current_categories[category]);
371 
372   /* (1) non-aliased file */
373   if (!load_locale_sub(category, new_categories[category], 0))
374     goto success;
375 
376   ///* (2) lookup locname/catname type alias */
377   //(void)snprintf(aliaspath, sizeof(aliaspath),
378   //         "%s/" _LOCALE_ALIAS_NAME, _PathLocale);
379   //(void)snprintf(loccat, sizeof(loccat), "%s/%s",
380   //         new_categories[category], categories[category]);
381   //alias = _lookup_alias(aliaspath, loccat, buf, sizeof(buf),
382   //          _LOOKUP_CASE_SENSITIVE);
383   //if (!load_locale_sub(category, alias, 1))
384   //  goto success;
385 
386   ///* (3) lookup locname type alias */
387   //alias = _lookup_alias(aliaspath, new_categories[category],
388   //          buf, sizeof(buf), _LOOKUP_CASE_SENSITIVE);
389   //if (!load_locale_sub(category, alias, 1))
390   //  goto success;
391 
392   return NULL;
393 
394 success:
395   (void)strncpyX(current_categories[category],
396     new_categories[category],
397     sizeof(current_categories[category]));
398   return current_categories[category];
399 }
400 
401 static const char *
__get_locale_env(int category)402 __get_locale_env(int category)
403 {
404   const char *env;
405 
406   //_DIAGASSERT(category != LC_ALL);
407 
408   ///* 1. check LC_ALL. */
409   //env = getenv(categories[0]);
410 
411   ///* 2. check LC_* */
412   //if (!env || !*env)
413   //  env = getenv(categories[category]);
414 
415   ///* 3. check LANG */
416   //if (!env || !*env)
417   //  env = getenv("LANG");
418 
419   ///* 4. if none is set, fall to "C" */
420   //if (!env || !*env || strchr(env, '/'))
421     env = "C";
422 
423   return env;
424 }
425