summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2014-07-24 03:23:11 -0400
committerRich Felker <dalias@aerifal.cx>2014-07-24 03:23:11 -0400
commit6cb4f91db7a5dc3bca63037ddc5f998a08dc3fb0 (patch)
tree2a347ad0a3c5240c77d61ad8d078ed9a77ff22bb
parent674e28af2deaa3ef2b71af18f7a18af22467d5ef (diff)
downloadmusl-6cb4f91db7a5dc3bca63037ddc5f998a08dc3fb0.tar.gz
implement locale file loading and state for remaining locale categories
there is still no code which actually uses the loaded locale files, so the main observable effect of this commit is that calls to setlocale store and give back the names of the selected locales for the remaining categories (LC_TIME, LC_COLLATE, LC_MONETARY) if a locale file by the requested name could be loaded.
-rw-r--r--src/internal/libc.h3
-rw-r--r--src/internal/locale_impl.h7
-rw-r--r--src/locale/__setlocalecat.c58
-rw-r--r--src/locale/setlocale.c14
4 files changed, 80 insertions, 2 deletions
diff --git a/src/internal/libc.h b/src/internal/libc.h
index 037d16b6..2eef98e4 100644
--- a/src/internal/libc.h
+++ b/src/internal/libc.h
@@ -5,9 +5,12 @@
#include <stdio.h>
#include <limits.h>
+struct __locale_map;
+
struct __locale_struct {
int ctype_utf8;
char *messages_name;
+ struct __locale_map *cat[4];
};
struct __libc {
diff --git a/src/internal/locale_impl.h b/src/internal/locale_impl.h
index 2747b85a..0ee72d3e 100644
--- a/src/internal/locale_impl.h
+++ b/src/internal/locale_impl.h
@@ -5,6 +5,13 @@
#define LOCALE_NAME_MAX 15
+struct __locale_map {
+ const void *map;
+ size_t map_size;
+ char name[LOCALE_NAME_MAX+1];
+ struct __locale_map *next;
+};
+
int __setlocalecat(locale_t, int, const char *);
#define CURRENT_LOCALE \
diff --git a/src/locale/__setlocalecat.c b/src/locale/__setlocalecat.c
index a947dbff..bbecde41 100644
--- a/src/locale/__setlocalecat.c
+++ b/src/locale/__setlocalecat.c
@@ -4,6 +4,58 @@
#include "libc.h"
#include "atomic.h"
+const unsigned char *__map_file(const char *, size_t *);
+int __munmap(void *, size_t);
+char *__strchrnul(const char *, int);
+
+static struct __locale_map *findlocale(const char *name, size_t n)
+{
+ static void *loc_head;
+ struct __locale_map *p, *new, *old_head;
+ const char *path = 0, *z;
+ char buf[256];
+ size_t l;
+ const void *map;
+ size_t map_size;
+
+ for (p=loc_head; p; p=p->next)
+ if (!strcmp(name, p->name)) return p;
+
+ if (strchr(name, '/')) return 0;
+
+ if (!libc.secure) path = getenv("MUSL_LOCPATH");
+ /* FIXME: add a default path? */
+ if (!path) return 0;
+
+ for (; *path; path=z+!!*z) {
+ z = __strchrnul(path, ':');
+ l = z - path - !!*z;
+ if (l >= sizeof buf - n - 2) continue;
+ memcpy(buf, path, l);
+ buf[l] = '/';
+ memcpy(buf+l+1, name, n);
+ buf[l+1+n] = 0;
+ map = __map_file(buf, &map_size);
+ if (map) {
+ new = malloc(sizeof *new);
+ if (!new) {
+ __munmap((void *)map, map_size);
+ return 0;
+ }
+ new->map = map;
+ new->map_size = map_size;
+ memcpy(new->name, name, n);
+ new->name[n] = 0;
+ do {
+ old_head = loc_head;
+ new->next = old_head;
+ } while (a_cas_p(&loc_head, old_head, new) != old_head);
+ return new;
+ }
+ }
+ return 0;
+}
+
static const char envvars[][12] = {
"LC_CTYPE",
"LC_NUMERIC",
@@ -26,6 +78,7 @@ int __setlocalecat(locale_t loc, int cat, const char *val)
int builtin = (val[0]=='C' && !val[1])
|| !strcmp(val, "C.UTF-8")
|| !strcmp(val, "POSIX");
+ struct __locale_map *data, *old;
switch (cat) {
case LC_CTYPE:
@@ -40,6 +93,11 @@ int __setlocalecat(locale_t loc, int cat, const char *val)
}
/* fall through */
default:
+ data = builtin ? 0 : findlocale(val, n);
+ if (data == loc->cat[cat-2]) break;
+ do old = loc->cat[cat-2];
+ while (a_cas_p(&loc->cat[cat-2], old, data) != old);
+ case LC_NUMERIC:
break;
}
return 0;
diff --git a/src/locale/setlocale.c b/src/locale/setlocale.c
index cbc0b551..8ea389a3 100644
--- a/src/locale/setlocale.c
+++ b/src/locale/setlocale.c
@@ -9,6 +9,9 @@ static char buf[2+4*(LOCALE_NAME_MAX+1)];
char *setlocale(int cat, const char *name)
{
+ 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);
@@ -24,7 +27,6 @@ char *setlocale(int cat, const char *name)
if (cat == LC_ALL) {
if (name) {
char part[LOCALE_NAME_MAX+1];
- int i, j;
if (name[0] && name[1]==';'
&& strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) {
part[0] = name[0];
@@ -45,6 +47,11 @@ char *setlocale(int cat, const char *name)
}
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));
+ }
return buf;
}
@@ -58,10 +65,13 @@ char *setlocale(int cat, const char *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:
- return "C";
+ lm = libc.global_locale.cat[cat-2];
+ return lm ? lm->name : "C";
}
}