use internal malloc for posix_spawn file actions objects
[musl] / src / locale / setlocale.c
index 32a8fca..360c443 100644 (file)
@@ -3,44 +3,17 @@
 #include <string.h>
 #include "locale_impl.h"
 #include "libc.h"
-#include "atomic.h"
+#include "lock.h"
 
-static char buf[2+4*(LOCALE_NAME_MAX+1)];
-
-static char *setlocale_one_unlocked(int cat, const char *name)
-{
-       struct __locale_map *lm;
-
-       if (name) __setlocalecat(&libc.global_locale, cat, name);
-
-       switch (cat) {
-       case LC_CTYPE:
-               return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C";
-       case LC_NUMERIC:
-               return "C";
-       case LC_MESSAGES:
-               return libc.global_locale.messages_name[0]
-                       ? libc.global_locale.messages_name : "C";
-       default:
-               lm = libc.global_locale.cat[cat-2];
-               return lm ? lm->name : "C";
-       }
-}
+static char buf[LC_ALL*(LOCALE_NAME_MAX+1)];
 
 char *setlocale(int cat, const char *name)
 {
-       static volatile int lock[2];
-       struct __locale_map *lm;
-       int i, j;
-
-       if (!libc.global_locale.messages_name) {
-               libc.global_locale.messages_name =
-                       buf + 2 + 3*(LOCALE_NAME_MAX+1);
-       }
+       const struct __locale_map *lm;
 
        if ((unsigned)cat > LC_ALL) return 0;
 
-       LOCK(lock);
+       LOCK(__locale_lock);
 
        /* For LC_ALL, setlocale is required to return a string which
         * encodes the current setting for all categories. The format of
@@ -48,41 +21,58 @@ char *setlocale(int cat, const char *name)
         * performs both the serialization and deserialization, depends
         * on the format, so it can easily be changed if needed. */
        if (cat == LC_ALL) {
+               int i;
                if (name) {
-                       char part[LOCALE_NAME_MAX+1];
-                       if (name[0] && name[1]==';'
-                           && strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) {
-                               part[0] = name[0];
-                               part[1] = 0;
-                               setlocale(LC_CTYPE, part);
-                               part[LOCALE_NAME_MAX] = 0;
-                               for (i=LC_TIME; i<LC_MESSAGES; i++) {
-                                       memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX);
-                                       for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--)
-                                               part[j] = 0;
-                                       setlocale_one_unlocked(i, part);
+                       struct __locale_struct tmp_locale;
+                       char part[LOCALE_NAME_MAX+1] = "C.UTF-8";
+                       const char *p = name;
+                       for (i=0; i<LC_ALL; i++) {
+                               const char *z = __strchrnul(p, ';');
+                               if (z-p <= LOCALE_NAME_MAX) {
+                                       memcpy(part, p, z-p);
+                                       part[z-p] = 0;
+                                       if (*z) p = z+1;
+                               }
+                               lm = __get_locale(i, part);
+                               if (lm == LOC_MAP_FAILED) {
+                                       UNLOCK(__locale_lock);
+                                       return 0;
                                }
-                               setlocale_one_unlocked(LC_MESSAGES, name
-                                       + 2 + 3*(LOCALE_NAME_MAX+1));
-                       } else {
-                               for (i=0; i<LC_ALL; i++)
-                                       setlocale_one_unlocked(i, name);
+                               tmp_locale.cat[i] = lm;
                        }
+                       libc.global_locale = tmp_locale;
                }
-               memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1));
-               buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C';
-               for (i=LC_TIME; i<LC_MESSAGES; i++) {
-                       lm = libc.global_locale.cat[i-2];
-                       if (lm) memcpy(buf + 2 + (i-2)*(LOCALE_NAME_MAX+1),
-                               lm->name, strlen(lm->name));
+               char *s = buf;
+               const char *part;
+               int same = 0;
+               for (i=0; i<LC_ALL; i++) {
+                       const struct __locale_map *lm =
+                               libc.global_locale.cat[i];
+                       if (lm == libc.global_locale.cat[0]) same++;
+                       part = lm ? lm->name : "C";
+                       size_t l = strlen(part);
+                       memcpy(s, part, l);
+                       s[l] = ';';
+                       s += l+1;
                }
-               UNLOCK(lock);
-               return buf;
+               *--s = 0;
+               UNLOCK(__locale_lock);
+               return same==LC_ALL ? (char *)part : buf;
        }
 
-       char *ret = setlocale_one_unlocked(cat, name);
+       if (name) {
+               lm = __get_locale(cat, name);
+               if (lm == LOC_MAP_FAILED) {
+                       UNLOCK(__locale_lock);
+                       return 0;
+               }
+               libc.global_locale.cat[cat] = lm;
+       } else {
+               lm = libc.global_locale.cat[cat];
+       }
+       char *ret = lm ? (char *)lm->name : "C";
 
-       UNLOCK(lock);
+       UNLOCK(__locale_lock);
 
        return ret;
 }