]> git.saurik.com Git - apple/libc.git/blobdiff - locale/FreeBSD/setlocale.c
Libc-1082.20.4.tar.gz
[apple/libc.git] / locale / FreeBSD / setlocale.c
index 3f59bea898a6ce5726c5dab27bc7b8bfc21af19a..c65d189188a08702b9836742446b93eb81fd1e18 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
 static char sccsid[] = "@(#)setlocale.c        8.1 (Berkeley) 7/4/93";
 #endif /* LIBC_SCCS and not lint */
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libc/locale/setlocale.c,v 1.41 2002/08/08 05:51:54 ache Exp $");
+__FBSDID("$FreeBSD: src/lib/libc/locale/setlocale.c,v 1.51 2007/01/09 00:28:00 imp Exp $");
+
+#include "xlocale_private.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <limits.h>
 #include <locale.h>
-#include <rune.h>
+#include <paths.h>     /* for _PATH_LOCALE */
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -56,12 +54,12 @@ __FBSDID("$FreeBSD: src/lib/libc/locale/setlocale.c,v 1.41 2002/08/08 05:51:54 a
 #include "lmessages.h" /* for __messages_load_locale() */
 #include "setlocale.h"
 #include "ldpart.h"
-#include "../stdtime/timelocal.h" /* for __time_load_locale() */
+#include "timelocal.h" /* for __time_load_locale() */
 
 /*
  * Category names for getenv()
  */
-static char *categories[_LC_LAST] = {
+static const char * const categories[_LC_LAST] = {
     "LC_ALL",
     "LC_COLLATE",
     "LC_CTYPE",
@@ -84,25 +82,31 @@ static char current_categories[_LC_LAST][ENCODING_LEN + 1] = {
     "C",
 };
 
+/*
+ * Path to locale storage directory
+ */
+char   *_PathLocale;
+
 /*
  * The locales we are going to try and load
  */
 static char new_categories[_LC_LAST][ENCODING_LEN + 1];
 static char saved_categories[_LC_LAST][ENCODING_LEN + 1];
 
-static char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)];
-
 static char    *currentlocale(void);
-static int     wrap_setrunelocale(const char *);
 static char    *loadlocale(int);
+__private_extern__ const char *__get_locale_env(int);
+
+#define        UNLOCK_AND_RETURN(x)    {XL_UNLOCK(&__global_locale); return (x);}
 
 char *
 setlocale(category, locale)
        int category;
        const char *locale;
 {
-       int i, j, len, saverr;
-       char *env, *r;
+       int i, j, len, saverr, save__numeric_fp_cvt;
+        const char *env, *r;
+       locale_t save__lc_numeric_loc;
 
        if (category < LC_ALL || category >= _LC_LAST) {
                errno = EINVAL;
@@ -113,6 +117,7 @@ setlocale(category, locale)
                return (category != LC_ALL ?
                    current_categories[category] : currentlocale());
 
+       XL_LOCK(&__global_locale);
        /*
         * Default to the current locale for everything.
         */
@@ -123,46 +128,34 @@ setlocale(category, locale)
         * Now go fill up new_categories from the locale argument
         */
        if (!*locale) {
-               env = getenv("LC_ALL");
-
-               if (category != LC_ALL && (env == NULL || !*env))
-                       env = getenv(categories[category]);
-
-               if (env == NULL || !*env)
-                       env = getenv("LANG");
-
-               if (env == NULL || !*env)
-                       env = "C";
-
-               if (strlen(env) > ENCODING_LEN) {
-                       errno = EINVAL;
-                       return (NULL);
-               }
-               (void)strcpy(new_categories[category], env);
-
                if (category == LC_ALL) {
                        for (i = 1; i < _LC_LAST; ++i) {
-                               if ((env = getenv(categories[i])) == NULL ||
-                                   !*env)
-                                       env = new_categories[LC_ALL];
-                               else if (strlen(env) > ENCODING_LEN) {
+                               env = __get_locale_env(i);
+                               if (strlen(env) > ENCODING_LEN) {
                                        errno = EINVAL;
-                                       return (NULL);
+                                       UNLOCK_AND_RETURN (NULL);
                                }
                                (void)strcpy(new_categories[i], env);
                        }
+               } else {
+                       env = __get_locale_env(category);
+                       if (strlen(env) > ENCODING_LEN) {
+                               errno = EINVAL;
+                               UNLOCK_AND_RETURN (NULL);
+                       }
+                       (void)strcpy(new_categories[category], env);
                }
        } else if (category != LC_ALL) {
                if (strlen(locale) > ENCODING_LEN) {
                        errno = EINVAL;
-                       return (NULL);
+                       UNLOCK_AND_RETURN (NULL);
                }
                (void)strcpy(new_categories[category], locale);
        } else {
                if ((r = strchr(locale, '/')) == NULL) {
                        if (strlen(locale) > ENCODING_LEN) {
                                errno = EINVAL;
-                               return (NULL);
+                               UNLOCK_AND_RETURN (NULL);
                        }
                        for (i = 1; i < _LC_LAST; ++i)
                                (void)strcpy(new_categories[i], locale);
@@ -171,23 +164,23 @@ setlocale(category, locale)
                                ;
                        if (!r[1]) {
                                errno = EINVAL;
-                               return (NULL);  /* Hmm, just slashes... */
+                               UNLOCK_AND_RETURN (NULL);       /* Hmm, just slashes... */
                        }
                        do {
                                if (i == _LC_LAST)
                                        break;  /* Too many slashes... */
                                if ((len = r - locale) > ENCODING_LEN) {
                                        errno = EINVAL;
-                                       return (NULL);
+                                       UNLOCK_AND_RETURN (NULL);
                                }
                                (void)strlcpy(new_categories[i], locale,
                                              len + 1);
                                i++;
+                               while (*r == '/')
+                                       r++;
                                locale = r;
-                               while (*locale == '/')
-                                       ++locale;
-                               while (*++r && *r != '/')
-                                       ;
+                               while (*r && *r != '/')
+                                       r++;
                        } while (*locale);
                        while (i < _LC_LAST) {
                                (void)strcpy(new_categories[i],
@@ -198,8 +191,11 @@ setlocale(category, locale)
        }
 
        if (category != LC_ALL)
-               return (loadlocale(category));
+               UNLOCK_AND_RETURN (loadlocale(category));
 
+       save__numeric_fp_cvt = __global_locale.__numeric_fp_cvt;
+       save__lc_numeric_loc = __global_locale.__lc_numeric_loc;
+       XL_RETAIN(save__lc_numeric_loc);
        for (i = 1; i < _LC_LAST; ++i) {
                (void)strcpy(saved_categories[i], current_categories[i]);
                if (loadlocale(i) == NULL) {
@@ -212,11 +208,15 @@ setlocale(category, locale)
                                        (void)loadlocale(j);
                                }
                        }
+                       __global_locale.__numeric_fp_cvt = save__numeric_fp_cvt;
+                       __global_locale.__lc_numeric_loc = save__lc_numeric_loc;
+                       XL_RELEASE(save__lc_numeric_loc);
                        errno = saverr;
-                       return (NULL);
+                       UNLOCK_AND_RETURN (NULL);
                }
        }
-       return (currentlocale());
+       XL_RELEASE(save__lc_numeric_loc);
+       UNLOCK_AND_RETURN (currentlocale());
 }
 
 static char *
@@ -224,7 +224,17 @@ currentlocale()
 {
        int i;
 
-       (void)strcpy(current_locale_string, current_categories[1]);
+       size_t bufsiz = _LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1);
+       static char *current_locale_string = NULL;
+       
+       if (current_locale_string == NULL) {
+               current_locale_string = malloc(bufsiz);
+               if (current_locale_string == NULL) {
+                       return NULL;
+               }
+       }
+       
+       (void)strlcpy(current_locale_string, current_categories[1], bufsiz);
 
        for (i = 2; i < _LC_LAST; ++i)
                if (strcmp(current_categories[1], current_categories[i])) {
@@ -238,25 +248,14 @@ currentlocale()
        return (current_locale_string);
 }
 
-static int
-wrap_setrunelocale(const char *locale)
-{
-       int ret = setrunelocale((char *)locale);
-
-       if (ret != 0) {
-               errno = ret;
-               return (_LDP_ERROR);
-       }
-       return (_LDP_LOADED);
-}
-
 static char *
 loadlocale(category)
        int category;
 {
        char *new = new_categories[category];
        char *old = current_categories[category];
-       int (*func)(const char *);
+       int (*func)(const char *, locale_t);
+       int saved_errno;
 
        if ((new[0] == '.' &&
             (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) ||
@@ -265,31 +264,15 @@ loadlocale(category)
                return (NULL);
        }
 
-       if (_PathLocale == NULL) {
-               char *p = getenv("PATH_LOCALE");
-
-               if (p != NULL
-#ifndef __NETBSD_SYSCALLS
-                       && !issetugid()
-#endif
-                       ) {
-                       if (strlen(p) + 1/*"/"*/ + ENCODING_LEN +
-                           1/*"/"*/ + CATEGORY_LEN >= PATH_MAX) {
-                               errno = ENAMETOOLONG;
-                               return (NULL);
-                       }
-                       _PathLocale = strdup(p);
-                       if (_PathLocale == NULL) {
-                               errno = ENOMEM;
-                               return (NULL);
-                       }
-               } else
-                       _PathLocale = _PATH_LOCALE;
-       }
+       saved_errno = errno;
+       errno = __detect_path_locale();
+       if (errno != 0)
+               return (NULL);
+       errno = saved_errno;
 
        switch (category) {
        case LC_CTYPE:
-               func = wrap_setrunelocale;
+               func = __wrap_setrunelocale;
                break;
        case LC_COLLATE:
                func = __collate_load_tables;
@@ -314,11 +297,68 @@ loadlocale(category)
        if (strcmp(new, old) == 0)
                return (old);
 
-       if (func(new) != _LDP_ERROR) {
+       if (func(new, &__global_locale) != _LDP_ERROR) {
                (void)strcpy(old, new);
+               switch (category) {
+               case LC_CTYPE:
+                       if (__global_locale.__numeric_fp_cvt == LC_NUMERIC_FP_SAME_LOCALE)
+                               __global_locale.__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED;
+                       break;
+               case LC_NUMERIC:
+                       __global_locale.__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED;
+                       XL_RELEASE(__global_locale.__lc_numeric_loc);
+                       __global_locale.__lc_numeric_loc = NULL;
+                       break;
+               }
                return (old);
        }
 
        return (NULL);
 }
 
+__private_extern__ const char *
+__get_locale_env(category)
+        int category;
+{
+        const char *env;
+
+        /* 1. check LC_ALL. */
+        env = getenv(categories[0]);
+
+        /* 2. check LC_* */
+       if (env == NULL || !*env)
+                env = getenv(categories[category]);
+
+        /* 3. check LANG */
+       if (env == NULL || !*env)
+                env = getenv("LANG");
+
+        /* 4. if none is set, fall to "C" */
+       if (env == NULL || !*env)
+                env = "C";
+
+       return (env);
+}
+
+/*
+ * Detect locale storage location and store its value to _PathLocale variable
+ */
+__private_extern__ int
+__detect_path_locale(void)
+{
+       if (_PathLocale == NULL) {
+               char *p = getenv("PATH_LOCALE");
+
+               if (p != NULL && !issetugid()) {
+                       if (strlen(p) + 1/*"/"*/ + ENCODING_LEN +
+                           1/*"/"*/ + CATEGORY_LEN >= PATH_MAX)
+                               return (ENAMETOOLONG);
+                       _PathLocale = strdup(p);
+                       if (_PathLocale == NULL)
+                               return (errno == 0 ? ENOMEM : errno);
+               } else
+                       _PathLocale = _PATH_LOCALE;
+       }
+       return (0);
+}
+